BUG: nCopies Does Not Initialize the Print Common Dialog Box (197393)
The information in this article applies to:
This article was previously published under Q197393 SYMPTOMS
On Windows 95, an attempt by an application to initialize the Copies edit
control in the Print Common Dialog box fails. However, the user of the
application can still edit the number of copies in the dialog box that are
then returned to the application when the dialog box is dismissed.
RESOLUTION
To work around the problem, an application can use the dmCopies member of
the printer's DEVMODE structure. The application should properly initialize
the structure and then set the dmCopies member of the DEVMODE structure to
the desired number of copies. When the application raises the Print Common
Dialog box with the PrintDlg() function, it should pass the DEVMODE using
the hDevMode member of the PRINTDLG structure.
Alternatively, an application can use a Print Hook function to override the
initialization of the Print common dialog box to set the Copies edit
control to the desired value.
For sample source code demonstrating these techniques, see the MORE
INFORMATION section of this article.
STATUS
Microsoft has confirmed this to be a bug in the Microsoft products listed
at the beginning of this article.
MORE INFORMATION
As documented in the Platform SDK (formerly the Win32 SDK), you can use the
nCopies member of the PRINTDLG structure to pre-specify for the Print
Dialog how many copies to print. The user specifies and changes the number
of copies to print in the Copies area of the Print common dialog box. When
the dialog box is dismissed, the printer device handles the number of
copies if it supports copies using the DEVMODE structure, or it returns the
number of copies to the application using the nCopies member.
However, on Windows 95, any value specified in the nCopies member of the
PRINTDLG structure is ignored. The Windows 95 Print Common Dialog contains
a bug that always initializes this value in the dialog box to "1" or to the
value specified in the dmCopies member of the DEVMODE structure.
Applications typically do not encounter this problem as they are written to
expect the default value of "1" for the number of copies. Applications that
automatically print multiple copies, as is the case for forms in triplicate
and the like, encounter this problem.
To work around this bug you can initialize the number of copies to print
using the dmCopies member of the printer device DEVMODE structure.
Steps to Implement dmCopies Workaround- Obtain an initialized DEVMODE structure for the default printer.
- Change the dmCopies member of the DEVMODE structure to the desired
number of copies.
- Initialize the hDevMode member of the PRINTDLG structure with a handle
to global memory pointing to a buffer containing the modified DEVMODE.
- Call the PrintDlg() function to raise the Print Common Dialog box with
the initialized structures.
This workaround may fail to work if the target printer (the default
printer) does not support device copies using the DEVMODE structure, as is
the case with most dot matrix printers.
Sample Code for dmCopies Workaround
/*
FUNCTION ModifyDevMode
PURPOSE: Encapsuulate the method of making changes
to a Printer DEVMODE.
ASSUMPTIONS: pDevMode must be a properly initialized
DEVMODE from either PrintDlg(),
GetPrinter(), or DocumentProperties().
*/
BOOL ModifyDevMode(LPTSTR lpszPrinterName, LPDEVMODE pDevMode, int nCopies)
{
LPTSTR pDevice = lpszPrinterName;
HANDLE hPrinter;
DWORD dwRet;
// Open the printer so you can call DocumentProperties.
if (!OpenPrinter( pDevice, &hPrinter, NULL ))
return FALSE; // Must be a bad printer name
/*
* Make changes to the DevMode that are supported.
*/
if (pDevMode->dmFields & DM_COPIES)
{
// Supported, so make the change
pDevMode->dmCopies = nCopies;
}
else
{
// Not supported so exit.
ClosePrinter( hPrinter );
return FALSE;
}
/*
* Merge the new settings with the old.
* This gives the driver a chance to update any private
* portions of the DevMode structure.
*/
dwRet = DocumentProperties( NULL,
hPrinter,
pDevice,
pDevMode, /* Reuse your buffer for output. */
pDevMode, /* Pass your changes to the driver. */
DM_IN_BUFFER | /* Commands to merge your changes */
DM_OUT_BUFFER ); /* and write the result. */
ClosePrinter( hPrinter );
return TRUE;
} /* End of function ModifyDevMode. */
/*
FUNCTION GetUserPrinterDC
PURPOSE: Launches the Printer Common Dialog and initializes the
Copies edit control for that dialog. It does this using the
dmCopies member of the DEVMODE structure to avoid a bug in
Windows 95 that causes the Copies edit control to not be
initialized from the PRINTDLG structure.
*/
HDC GetUserPrinterDC(HWND hWnd, int nCopies)
{
PRINTDLG pd;
LPDEVMODE pDevMode;
LPDEVNAMES pDevNames;
HDC hDC = NULL;
LPTSTR pDevice;
// Initialize the DEVMODE to get the default printer.
ZeroMemory( &pd, sizeof(pd) );
pd.lStructSize = sizeof(pd);
pd.hwndOwner = hWnd;
pd.Flags = PD_RETURNDEFAULT;
// Get a DEVMODE for the default printer.
if (!PrintDlg( &pd ))
return NULL;
//Your number of copies goes into DEVMODE.dmCopies.
pDevMode = (LPDEVMODE)GlobalLock( pd.hDevMode );
pDevNames = (LPDEVNAMES)GlobalLock( pd.hDevNames );
pDevice = (LPTSTR)pDevNames + pDevNames->wDeviceOffset;
if (!ModifyDevMode( pDevice, pDevMode, nCopies ))
{
// Can't initialize dmCopies, so use PRINTDLG, so the code at
// least works on other versions of Windows (OSR 2, 98, NT).
pd.nCopies = nCopies;
}
GlobalUnlock( pd.hDevMode );
GlobalUnlock( pd.hDevNames );
// Setup and get the user's Device Context.
pd.Flags = PD_RETURNDC;
if (PrintDlg( &pd ))
hDC = pd.hDC;
// Cleanup properly.
if (pd.hDevMode)
GlobalFree( pd.hDevMode );
if (pd.hDevNames)
GlobalFree( pd.hDevNames );
// Send it back to the caller.
return hDC;
} /* End of function GetUserPrinterDC. */
An alternative workaround is to use the Print Common Dialog lpfnPrintHook
member to set the Copies edit control to the desired value when the dialog
is initialized. An application can install a dialog hook procedure using
the lpfnPrintHook member of the PRINTDLG structure. Then, when the hook
procedure processes the WM_INITDIALOG message for the Print common dialog
box, it can set the number of copies in the Copies edit control.
In many ways this is a better workaround than using the DEVMODE's dmCopies
member because it works with all printers. However, the application must
pass its own version of the number of copies, and the technique relies upon
edit control IDs taken from Windows dialog template files and header files.
The application must pass the number of copies separately because the Print
Common Dialog provides a version of the PRINTDLG structure to the
lpfnPrintHook function which has been initialized to be consistent with the
dialog's controls. As a result, the nCopies member of the PRINTDLG
structure will have been overwritten when the lpfnPrintHook function is
called.
Sample Code for Print Hook Function Workaround
/*
FUNCTION PrintHookProc
PURPOSE: Hook Procedure for the PrintDlg Dialog.
Used to override the Dialog's initialization processing
of the Number of Copies edit control.
*/
UINT CALLBACK PrintHookProc(
HWND hdlg,
UINT uiMsg,
WPARAM wParam,
LPARAM lParam )
{
static PRINTDLG *pPD = NULL;
int CopiesEditCtlId = edt3;
// Note: You get the previous edit control ID from the
// Prnsetup.dlg dialog template. edt3 is defined in
// Dlgs.h.
switch ( uiMsg )
{
case WM_INITDIALOG:
{
// In this message you get a pointer to the
// PRINTDLG structure.
pPD = (PRINTDLG *)lParam;
// Extract the desired value for nCopoies.
pPD->nCopies = (WORD)pPD->lCustData;
// Effect it in the edit control.
SetDlgItemInt( hdlg, CopiesEditCtlId, pPD->nCopies, FALSE );
return 1;
}
}
// Otherwise, you didn't handle the message.
return 0;
} /* End of function PrintHookProc. */
/*
FUNCTION GetUserPrinterDC
PURPOSE: Launches the Printer Common Dialog box and
initializes the Copies edit control for that dialog box.
Uses a PrintHookProc to avoid a bug in Windows 95 that
causes the Copies edit control to not be initialized from
the PRINTDLG structure.
*/
HDC GetUserPrinterDC(HWND hWnd, int nCopies)
{
PRINTDLG pd;
HDC hDC = NULL;
// Set up and get the users Device Context.
ZeroMemory( &pd, sizeof(pd) );
pd.lStructSize = sizeof(pd);
pd.hwndOwner = hWnd;
pd.nCopies = nCopies;
pd.Flags = PD_RETURNDC | PD_ENABLEPRINTHOOK;
// Pass the nCopies using custom data so you have it in the
// PrintHookProc after the Dialog has setup its own
// copy of this PRINTDLG structure on WM_INITDIALOG.
pd.lCustData = nCopies;
pd.lpfnPrintHook = PrintHookProc;
if (PrintDlg( &pd ))
hDC = pd.hDC;
// Cleanup properly.
if (pd.hDevMode)
GlobalFree( pd.hDevMode );
if (pd.hDevNames)
GlobalFree( pd.hDevNames );
// Send it back to the caller.
return hDC;
} /* End of function GetUserPrinterDC */
Modification Type: | Major | Last Reviewed: | 9/11/2001 |
---|
Keywords: | kbbug kbCmnDlgPrint kbGDI kbprint KB197393 |
---|
|