How to modify the "default" printer for your Microsoft Foundation Classes (MFC) application to be other than the actual system-defined default printer (193103)
The information in this article applies to:
- The Microsoft Foundation Classes (MFC), when used with:
- Microsoft Visual C++, 32-bit Editions 5.0
- Microsoft Visual C++, 32-bit Editions 6.0
- Microsoft Visual C++ .NET (2002)
- Microsoft Visual C++ .NET (2003)
This article was previously published under Q193103 NOTE: Microsoft Visual C++ .NET (2002) and Microsoft Visual C++ .NET
(2003) support both the managed code model that is provided by the .NET
Framework and the unmanaged native Windows code model. The information in this
article applies to unmanaged Visual C++ code only. SUMMARY You may sometimes want the "default" printer for your MFC
application to be other than the actual system-defined default printer. This
article shows how you can:
- Retrieve the system default printer.
- Define your own application default printer based on the
current view.
- Modify the settings for this default printer.
- Properly use the MFC CPrintDialog class to allow the user
to override your application's default printer settings.
MORE INFORMATION The sample code provided in the section illustrates How To
- Create and install your own DEVMODE and DEVNAMES for your
printer (or obtain a copy of the DEVMODE and DEVNAMES from the system default
printer in case your chosen default printer is unavailable).
- Modify the default settings for your default printer (such
as orientation and resolution).
- Have CPrintDialog use your default printer and settings
based on the current view when presenting the user with the Windows standard
Print Common dialog box.
- Have MFC's print logic use your default printer based on
the current view when printing your report (by temporarily changing and then
resetting MFC's default printer).
The following code shows how to implement these setting in your
CView-derived class to allow each of your views to have its own default
printer. If you instead want to have the same default printer for all views
(that is, for your entire application and not just for each of your views), you
can implement the following code in the appropriate places of your
CWinApp-derived class. Sample Code for Visual C++ .NET// 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;
}
Sample Code for Visual C++ 6.0// 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;
}
REFERENCES
For more information, click the following article numbers to view the articles in the Microsoft Knowledge Base:
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
Modification Type: | Major | Last Reviewed: | 6/2/2005 |
---|
Keywords: | kbGDI kbhowto kbprint KB193103 kbAudDeveloper |
---|
|