MORE INFORMATION
During a normal print operation in an MFC application, the
print dialog box is displayed which allows you to select the printer you want
to print to. The default printer displayed in the print dialog box is the
default printer specified in the operating system. MFC stores the default
printer of the application in the CWinApp::m_hDevMode and CWinApp::m_hDevNames
protected data members. Because MFC initializes these variables to NULL, the
MFC print architecture defaults to the operating system's default printer the
first time a print operation is performed. The operating system default
printer's DEVMODE and DEVNAMES are then copied to the MFC application's
m_hDevMode and m_hDevNames data members.
Occasionally, there may be a
situation where you need to print to a printer other than the default printer
without having the user specify it through the print dialog box. The
non-default printer can be a secondary printer or a network printer (assuming
your operating system has the drivers already installed).
Whether you
need to use the non-default printer on a permanent basis or for only one print
job, you need the DEVMODE and DEVNAMES structure to create the printer DC. The
PRINTER_INFO_2 structure from GetPrinter() contains all the information needed
to fill the DEVMODE and DEVNAMES structures.
Creating a DEVMODE and DEVNAMES Structure
The sample code below shows how to use GetPrinter() to create a
DEVMODE and DEVNAMES structure based on a printer name. The code is a global
function which returns the data structures through its parameters.
#include <winspool.h>
// returns a DEVMODE and DEVNAMES for the printer name specified
BOOL GetPrinterDevice(LPTSTR pszPrinterName, HGLOBAL* phDevNames, HGLOBAL* phDevMode)
{
// if NULL is passed, then assume we are setting app object's
// devmode and devnames
if (phDevMode == NULL || phDevNames == NULL)
return FALSE;
// Open printer
HANDLE hPrinter;
if (OpenPrinter(pszPrinterName, &hPrinter, NULL) == FALSE)
return FALSE;
// obtain PRINTER_INFO_2 structure and close printer
DWORD dwBytesReturned, dwBytesNeeded;
GetPrinter(hPrinter, 2, NULL, 0, &dwBytesNeeded);
PRINTER_INFO_2* p2 = (PRINTER_INFO_2*)GlobalAlloc(GPTR,
dwBytesNeeded);
if (GetPrinter(hPrinter, 2, (LPBYTE)p2, dwBytesNeeded,
&dwBytesReturned) == 0) {
GlobalFree(p2);
ClosePrinter(hPrinter);
return FALSE;
}
ClosePrinter(hPrinter);
// Allocate a global handle for DEVMODE
HGLOBAL hDevMode = GlobalAlloc(GHND, sizeof(*p2->pDevMode) +
p2->pDevMode->dmDriverExtra);
ASSERT(hDevMode);
DEVMODE* pDevMode = (DEVMODE*)GlobalLock(hDevMode);
ASSERT(pDevMode);
// copy DEVMODE data from PRINTER_INFO_2::pDevMode
memcpy(pDevMode, p2->pDevMode, sizeof(*p2->pDevMode) +
p2->pDevMode->dmDriverExtra);
GlobalUnlock(hDevMode);
// Compute size of DEVNAMES structure from PRINTER_INFO_2's data
DWORD drvNameLen = lstrlen(p2->pDriverName)+1; // driver name
DWORD ptrNameLen = lstrlen(p2->pPrinterName)+1; // printer name
DWORD porNameLen = lstrlen(p2->pPortName)+1; // port name
// Allocate a global handle big enough to hold DEVNAMES.
HGLOBAL hDevNames = GlobalAlloc(GHND,
sizeof(DEVNAMES) +
(drvNameLen + ptrNameLen + porNameLen)*sizeof(TCHAR));
ASSERT(hDevNames);
DEVNAMES* pDevNames = (DEVNAMES*)GlobalLock(hDevNames);
ASSERT(pDevNames);
// Copy the DEVNAMES information from PRINTER_INFO_2
// tcOffset = TCHAR Offset into structure
int tcOffset = sizeof(DEVNAMES)/sizeof(TCHAR);
ASSERT(sizeof(DEVNAMES) == tcOffset*sizeof(TCHAR));
pDevNames->wDriverOffset = tcOffset;
memcpy((LPTSTR)pDevNames + tcOffset, p2->pDriverName,
drvNameLen*sizeof(TCHAR));
tcOffset += drvNameLen;
pDevNames->wDeviceOffset = tcOffset;
memcpy((LPTSTR)pDevNames + tcOffset, p2->pPrinterName,
ptrNameLen*sizeof(TCHAR));
tcOffset += ptrNameLen;
pDevNames->wOutputOffset = tcOffset;
memcpy((LPTSTR)pDevNames + tcOffset, p2->pPortName,
porNameLen*sizeof(TCHAR));
pDevNames->wDefault = 0;
GlobalUnlock(hDevNames);
GlobalFree(p2); // free PRINTER_INFO_2
// set the new hDevMode and hDevNames
*phDevMode = hDevMode;
*phDevNames = hDevNames;
return TRUE;
}
As a quick reference, following is the declaration of
DEVNAMES with a short description of each field:
// commdlg.h
// the first three members are character offsets from the beginning of the // structure pointing to the specific string
typedef struct tagDEVNAMES {
WORD wDriverOffset; // file name of driver (without extension)
WORD wDeviceOffset; // device name
WORD wOutputOffset; // device name of physical output medium
WORD wDefault; // DN_DEFAULTPRN if default printer chosen
} DEVNAMES;
Setting the Application's Default Printer
To set the default printer of the application you need to set the
m_hDevNames and m_hDevMode data members (which are protected) through a member
function of the CWinApp derived object, GetPrinterDevice(). You can implement
the function below and call it whenever you need to change the default printer.
void CMainFrame::OnActionSetnondefaultprinter()
{
HGLOBAL hDevMode = NULL;
HGLOBAL hDevNames = NULL;
if (GetPrinterDevice(_T("\\\\RED-PRN-25\\PRIV0006"), &hDevNames, &hDevMode))
AfxGetApp()->SelectPrinter(hDevNames, hDevMode);
else
AfxMessageBox(_T("Failed to select custom printer"));
}
Bypassing the Print Dialog when Using the Application's Default Printer
To bypass the print dialog, OnPreparePrinting needs to be
overridden so that the m_bDirect flag can be set to TRUE. DoPreparePrinting is
then called to set the values of pInfo->m_pPD->m_pd.hDevMode and
pInfo->m_pPD->m_pd.hDevNames to the corresponding application object's
data member and to create the printer DC.
BOOL CNonDefPrinterView::OnPreparePrinting(CPrintInfo* pInfo)
{
pInfo->m_bDirect = TRUE; // TRUE if bypassing Print Dialog
return DoPreparePrinting(pInfo);
}
Bypassing the Print Dialog when Using a Non-Default Printer
To bypass the print dialog and use a non-default printer, you
need to set the pInfo->m_pPD->m_pd data members yourself and create the
printer DC. Following is the code that will accomplish this:
BOOL CNonDefPrinterView::OnPreparePrinting(CPrintInfo* pInfo)
{
// set to non-default printer without changing default app printer
HGLOBAL hDevMode;
HGLOBAL hDevNames;
if (!GetPrinterDevice(_T("\\\\RED-PRN-25\\PRIV0006"), &hDevNames, &hDevMode))
AfxMessageBox(_T("GetPrinterDevice called failed\n"));
pInfo->m_pPD->m_pd.hDevMode = hDevMode;
pInfo->m_pPD->m_pd.hDevNames = hDevNames;
pInfo->m_pPD->CreatePrinterDC();
return TRUE;
}
This example is shown using the GetPrinterDevice function. See
above for more detail.
REFERENCES
For more information, click the following article numbers to view the articles in the Microsoft Knowledge Base:
193103
How to modify the "default" printer for your Microsoft Foundation Classes (MFC) application to be other than the actual system-defined default printer
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