ID: Q197393
The information in this article applies to:
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.
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.
Microsoft has confirmed this to be a bug in the Microsoft products listed at the beginning of this article.
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.
1. Obtain an initialized DEVMODE structure for the default printer.
2. Change the dmCopies member of the DEVMODE structure to the desired
number of copies.
3. Initialize the hDevMode member of the PRINTDLG structure with a handle
to global memory pointing to a buffer containing the modified DEVMODE.
4. 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.
/*
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(LPDEVMODE pDevMode, int nCopies)
{
LPSTR pDevice;
HANDLE hPrinter;
DWORD dwRet;
// Get the name of the printer from the DEVMODE.
pDevice = (LPSTR)pDevMode->dmDeviceName;
// 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;
HDC hDC = NULL;
// 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 );
if (!ModifyDevMode( 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 );
// 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.
/*
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 */
Additional query words:
Keywords : kbCmnDlgPrint kbGDI kbPrinting kbSDKPlatform kbSDKWin32
Version : WINDOWS:95
Platform : WINDOWS
Issue type : kbbug
Solution Type : kbfix
Last Reviewed: December 15, 1998