Article ID: 193103
Article Last Modified on 11/21/2006
// MyPrintView.h : Interface of the CMyPrintView class.
class CMyPrintView : public CView
{
...
public:
afx_msg void OnFilePrintSetup();
// Generated message map functions.
protected:
DECLARE_MESSAGE_MAP()
protected:
BOOL GetPrinterDevice(LPTSTR pszPrinterName,
HGLOBAL* phDevNames, HGLOBAL* phDevMode);
HANDLE CopyHandle(HANDLE h);
HANDLE m_hDevMode;
HANDLE m_hDevNames;
};
// MyPrintView.cpp : Implementation of the CMyPrintView class.
#include <winspool.h>
...
BEGIN_MESSAGE_MAP(CMyPrintView, CView)
...
ON_COMMAND(ID_FILE_PRINT_SETUP, OnFilePrintSetup)
...
END_MESSAGE_MAP()
CMyPrintView::CMyPrintView()
{
// TODO: Add construction code here.
...
m_hDevMode = m_hDevNames = NULL;
}
CMyPrintView::~CMyPrintView()
{
...
if (m_hDevMode) GlobalFree(m_hDevMode);
if (m_hDevNames) GlobalFree(m_hDevNames);
}
// returns a DEVMODE and DEVNAMES for the printer name specified
BOOL CMyPrintView::GetPrinterDevice(LPTSTR pszPrinterName, HGLOBAL* phDevNames, HGLOBAL* phDevMode)
{
...
// please copy this from Q166129 - see link at bottom of article
// note: if you're specifying a remote printer, this function will
// only work on Windows NT
}
void CMyPrintView::OnInitialUpdate()
{
CView::OnInitialUpdate(); // Let MFC do its thing first...
// Try to use a specific printer.
// If printer is not available, use the default printer.
if (!GetPrinterDevice(_T("HP LaserJet III"), &m_hDevNames, &m_hDevMode)) // See KB article Q166129
{
// Could not install our desired default printer.
// Instead, "clone" the currently defined default printer
// and make changes to it instead.
PRINTDLG pd;
// Retrieve MFC's default printer.
AfxGetApp()->GetPrinterDeviceDefaults(&pd);
// Make our own private copy of MFC's DEVMODE and DEVNAMES data.
// Note: we should NOT GlobalFree the handles returned
// to us in the PRINTDLG structure, because those are the
// actual handles that MFC uses!
m_hDevMode = CopyHandle(pd.hDevMode);
m_hDevNames = CopyHandle(pd.hDevNames);
}
// Set our desired printer defaults.
LPDEVMODE lpDevMode = (LPDEVMODE) GlobalLock(m_hDevMode);
// For each option, only change it if the printer supports it.
if (lpDevMode->dmFields & DM_ORIENTATION)
lpDevMode->dmOrientation = DMORIENT_LANDSCAPE;
if (lpDevMode->dmFields & DM_DUPLEX)
lpDevMode->dmDuplex = DMDUP_HORIZONTAL;
if (lpDevMode->dmFields & DM_PAPERSIZE)
lpDevMode->dmPaperSize = DMPAPER_A4SMALL;
if (lpDevMode->dmFields & DM_YRESOLUTION)
// NOTE: must change BOTH dmYResolution AND dmPrintQuality.
lpDevMode->dmYResolution = lpDevMode->dmPrintQuality = 75;
GlobalUnlock(m_hDevMode);
}
void CMyPrintView::OnFilePrintSetup()
{
// Ask the user which printer they want to use.
CPrintDialog pd(
FALSE,
PD_ALLPAGES | PD_USEDEVMODECOPIES |
PD_NOPAGENUMS | PD_HIDEPRINTTOFILE | PD_NOSELECTION,
this);
// Make sure we don't accidentally create a device context
pd.m_pd.Flags &= ~PD_RETURNDC; // Reset flag set by constructor.
// Force the CPrintDialog to use our device mode & name.
pd.m_pd.hDevMode = CopyHandle(m_hDevMode);
pd.m_pd.hDevNames = CopyHandle(m_hDevNames);
// Display the dialog box and let the user make their selection.
if (pd.DoModal() == IDOK)
{
// The user clicked OK
// (and POSSIBLY changed printers).
// In any case, the CPrintDialog logic made a copy of the original
// DEVMODE/DEVNAMES that we passed it and applied the user's
// changes to that copy and discarded the original copy we passed
// it. (NOTE: If the user had clicked CANCEL instead, the original
// values we passed would have been returned unchanged).
GlobalFree(m_hDevMode); // Free old copies.
GlobalFree(m_hDevNames); // Free old copies.
m_hDevMode = CopyHandle(pd.m_pd.hDevMode); // Save new copies.
m_hDevNames = CopyHandle(pd.m_pd.hDevNames); // Save new copies.
}
// Regardless of whether the user clicked OK or CANCEL,
// we need to ALWAYS do a GlobalFree of CPrintDialog's
// m_pd.hDevMode and m_pd.hDevNames upon return from
// DoModal in order to prevent a resource leak.
GlobalFree(pd.m_pd.hDevMode); // Because DoModal was called,
GlobalFree(pd.m_pd.hDevNames); // we need to free these.
}
BOOL CMyPrintView::OnPreparePrinting(CPrintInfo* pInfo)
{
PRINTDLG pd;
// Save MFC's printer and install ours instead.
AfxGetApp()->GetPrinterDeviceDefaults(&pd);
AfxGetApp()->SelectPrinter(m_hDevNames,m_hDevMode,FALSE);
// Perform default MFC handling.
BOOL b = DoPreparePrinting(pInfo);
// Reinstall MFC's printer.
AfxGetApp()->SelectPrinter(pd.hDevNames,pd.hDevMode,FALSE);
return b;
}
HANDLE CMyPrintView::CopyHandle(HANDLE h)
{
// Return a handle to a copy of the data
// that the passed handle was for.
if (!h) return NULL;
BYTE* lpCopy;
BYTE* lp;
HANDLE hCopy;
DWORD dwLen = GlobalSize(h);
if (hCopy = GlobalAlloc(GHND, dwLen))
{
lpCopy = (BYTE*)GlobalLock(hCopy);
lp = (BYTE*)GlobalLock(h);
CopyMemory(lpCopy,lp,dwLen);
GlobalUnlock(hCopy);
GlobalUnlock(h);
}
return hCopy;
}
// MyPrintView.h : Interface of the CMyPrintView class.
class CMyPrintView : public CView
{
...
// Generated message map functions.
protected:
//{{AFX_MSG(CMyPrintView)
...
afx_msg void OnFilePrintSetup();
...
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
BOOL GetPrinterDevice(LPTSTR pszPrinterName,
HGLOBAL* phDevNames, HGLOBAL* phDevMode);
HANDLE CMyPrintView::CopyHandle(HANDLE h);
HANDLE m_hDevMode;
HANDLE m_hDevNames;
};
// MyPrintView.cpp : Implementation of the CMyPrintView class.
#include <winspool.h>
...
BEGIN_MESSAGE_MAP(CMyPrintView, CView)
...
ON_COMMAND(ID_FILE_PRINT_SETUP, OnFilePrintSetup)
...
END_MESSAGE_MAP()
CMyPrintView::CMyPrintView()
{
// TODO: Add construction code here.
...
m_hDevMode = m_hDevNames = NULL;
}
CMyPrintView::~CMyPrintView()
{
...
if (m_hDevMode) GlobalFree(m_hDevMode);
if (m_hDevNames) GlobalFree(m_hDevNames);
}
// returns a DEVMODE and DEVNAMES for the printer name specified
BOOL CMyPrintView::GetPrinterDevice(LPTSTR pszPrinterName, HGLOBAL* phDevNames, HGLOBAL* phDevMode)
{
...
// please copy this from Q166129 - see link at bottom of article
// note: if you're specifying a remote printer, this function will
// only work on Windows NT
}
void CMyPrintView::OnInitialUpdate()
{
CView::OnInitialUpdate(); // Let MFC do its thing first...
// Try to use a specific printer.
// If printer is not available, use the default printer.
if (!GetPrinterDevice(_T("HP LaserJet III"), &m_hDevNames, &m_hDevMode)) // See KB article Q166129
{
// Could not install our desired default printer.
// Instead, "clone" the currently defined default printer
// and make changes to it instead.
PRINTDLG pd;
// Retrieve MFC's default printer.
AfxGetApp()->GetPrinterDeviceDefaults(&pd);
// Make our own private copy of MFC's DEVMODE and DEVNAMES data.
// Note: we should NOT GlobalFree the handles returned
// to us in the PRINTDLG structure, because those are the
// actual handles that MFC uses!
m_hDevMode = CopyHandle(pd.hDevMode);
m_hDevNames = CopyHandle(pd.hDevNames);
}
// Set our desired printer defaults.
LPDEVMODE lpDevMode = (LPDEVMODE) GlobalLock(m_hDevMode);
// For each option, only change it if the printer supports it.
if (lpDevMode->dmFields & DM_ORIENTATION)
lpDevMode->dmOrientation = DMORIENT_LANDSCAPE;
if (lpDevMode->dmFields & DM_DUPLEX)
lpDevMode->dmDuplex = DMDUP_HORIZONTAL;
if (lpDevMode->dmFields & DM_PAPERSIZE)
lpDevMode->dmPaperSize = DMPAPER_A4SMALL;
if (lpDevMode->dmFields & DM_YRESOLUTION)
// NOTE: must change BOTH dmYResolution AND dmPrintQuality.
lpDevMode->dmYResolution = lpDevMode->dmPrintQuality = 75;
GlobalUnlock(m_hDevMode);
}
void CMyPrintView::OnFilePrintSetup()
{
// Ask the user which printer they want to use.
CPrintDialog pd(
FALSE,
PD_ALLPAGES | PD_USEDEVMODECOPIES |
PD_NOPAGENUMS | PD_HIDEPRINTTOFILE | PD_NOSELECTION,
this);
// Make sure we don't accidentally create a device context
pd.m_pd.Flags &= ~PD_RETURNDC; // Reset flag set by constructor.
// Force the CPrintDialog to use our device mode & name.
pd.m_pd.hDevMode = CopyHandle(m_hDevMode);
pd.m_pd.hDevNames = CopyHandle(m_hDevNames);
// Display the dialog box and let the user make their selection.
if (pd.DoModal() == IDOK)
{
// The user clicked OK
// (and POSSIBLY changed printers).
// In any case, the CPrintDialog logic made a copy of the original
// DEVMODE/DEVNAMES that we passed it and applied the user's
// changes to that copy and discarded the original copy we passed
// it. (NOTE: If the user had clicked CANCEL instead, the original
// values we passed would have been returned unchanged).
GlobalFree(m_hDevMode); // Free old copies.
GlobalFree(m_hDevNames); // Free old copies.
m_hDevMode = CopyHandle(pd.m_pd.hDevMode); // Save new copies.
m_hDevNames = CopyHandle(pd.m_pd.hDevNames); // Save new copies.
}
// Regardless of whether the user clicked OK or CANCEL,
// we need to ALWAYS do a GlobalFree of CPrintDialog's
// m_pd.hDevMode and m_pd.hDevNames upon return from
// DoModal in order to prevent a resource leak.
GlobalFree(pd.m_pd.hDevMode); // Because DoModal was called,
GlobalFree(pd.m_pd.hDevNames); // we need to free these.
}
BOOL CMyPrintView::OnPreparePrinting(CPrintInfo* pInfo)
{
PRINTDLG pd;
// Save MFC's printer and install ours instead.
AfxGetApp()->GetPrinterDeviceDefaults(&pd);
AfxGetApp()->SelectPrinter(m_hDevNames,m_hDevMode,FALSE);
// Perform default MFC handling.
BOOL b = DoPreparePrinting(pInfo);
// Reinstall MFC's printer.
AfxGetApp()->SelectPrinter(pd.hDevNames,pd.hDevMode,FALSE);
return b;
}
HANDLE CMyPrintView::CopyHandle(HANDLE h)
{
// Return a handle to a copy of the data
// that the passed handle was for.
if (!h) return NULL;
BYTE* lpCopy;
BYTE* lp;
HANDLE hCopy;
DWORD dwLen = GlobalSize(h);
if (hCopy = GlobalAlloc(GHND, dwLen))
{
lpCopy = (BYTE*)GlobalLock(hCopy);
lp = (BYTE*)GlobalLock(h);
CopyMemory(lpCopy,lp,dwLen);
GlobalUnlock(hCopy);
GlobalUnlock(h);
}
return hCopy;
}
166129 How to print directly to a non-default printer in MFC
147202 How to add a network printer connection
140560 How to set the default printer programmatically in Windows 95, Windows 98, or Windows Me
167345 How to modify printer settings with the DocumentProperties() function
126897 How to change default printer settings in an MFC application
162609 How to create a printer device context (DC) in MFC
Keywords: kbgdi kbhowto kbprint KB193103