Technical Note 02: Correctly using DEVMODE structure to set printer parameters and options DEVMODE is a Windows structure that holds all the printer parameters. It is made up of two parts: public and private. The public part contains data that is common to all printers. The private part contains data that is specific to a particular printer. The private part can be of variable length and can contain anything the printer driver designer decides to include in it. One of trickier aspects of working with the DEVMODE structure is that it depends on the operating system. Its length and content cannot be known beforehand. No assumptions can or should be made as to the size of this structure in either its private or public part. To get the size of the DEVMODE structure specific to the printer, we should use the DocumentProperties API call. This method is defined in WINSPOOL.H as: LONG WINAPI DocumentProperties( HWND hWnd, // handle of the parent window HANDLE hPrinter, // handle of the specific printer LPTSTR pDeviceName, // printer name PDEVMODE pDevModeOutput, // output DEVMODE structure PDEVMODE pDevModeInput, // input DEVMODE structure DWORD fMode // requested operation ); To obtain the size of the DEVMODE structure, we can use the following instructions: LONG lSize = 0 ; LPDEVMODE lpDevMode = NULL ; lSize = DocumentProperties( NULL, NULL, szPrinterName, NULL, NULL, 0 ) ; if ( 0 == lSize ) { // error , printer is unknown return ; } An alternative way would be to use the OpenPrinter API call, and use the handle returned by this function to get the DEVMODE structure: HANDLE hPrinter; if ( !OpenPrinter( lpDeviceName, &hPrinter, NULL ) ) return FALSE; // get real size of DEVMODE int nSize = DocumentProperties( NULL, hPrinter, NULL, NULL, NULL, 0 ); Once we have the real size of the structure, we can allocate memory for it and get the printer default values for all parameters: lpDevMode = (LPDEVMODE)GlobalAllocPtr( lSize ) ; DocumentProperties( NULL, NULL, szPrinterName, lpDevMode, NULL, DM_OUT_BUFFER ) ; This will also give us the size of the public and private members of the DEVMODE structure: lpDevMode->dmSize is the size of the public structure (common to all printers) lpDevMode->dmDriverExtra is the size of the private structure (specific to each printer) To modify one of the public printer parameters, for example printer resolution and paper orinetation, we can use the following instructions: lpDevMode->dmPrintQuality = 600; // set the resolution to 600 DPI lpDevMode->dmOrientation = DMORIENT_PORTRAIT; // set orientation to landscape lpDevMode->dmFields = DM_ORIENTATION | DM_PRINTQUALITY; // indicate which fields are modified Two fields located at the start of the private data can be of interest to the programmer: dwOptions and dwMargins. dwOptions contains compression and font embedding options as follows: Bit 0 : flate compression of page content Bit 1 : font embedding option Bit 2 : JPEG compression Bit 3 : 256 color compression Example, to read or set the JPEG compression option, we would use the following instructions: LPDWORD lpdwFlags = (LPDWORD)((LPBYTE)lpDevMode + lpDevMode->dmSize ); BOOL bJpegCompression = (BOOL)(*lpdwFlags & 4); // this returns JPEG compression flag // now invert the JPEG compression option if ( bJpegCompression ) { *lpdwFlags = *lpdwFlags & ~4; // clear the JPEG compression flag } else { *lpdwFlags = *lpdwFlags | 4; // set the JPEG compression flag } dwMargins contains the minimum horizontal margin in the low part, and minimum vertical margin in the high part. These are defined in 0.1 mm units. To modify the minimum margins, we can use the following instructions: lpMargins = (LPDWORD)( (LPBYTE)lpDevMode + lpDevMode->dmSize + sizeof( DWORD ) ); *lpMargins = MAKELONG( 30, 30 ); // this will set the margins to 3 mm Once the DEVMODE structure has been modified to suit our configuration, we can either create a printer device context using the CreateDC API call: HDC hDC = CreateDC( _T( "winspool" ), szPrinter, NULL, lpDevMode ); Or make this configuration as the default for this printer and for all applications using this printer. This procedure is different for Windows NT/2000 or Windows 95/98: if ( bNT ) { DWORD dw; PRINTER_INFO_2 *pi2; // get default printer info structure which contains the DEVMODE GetPrinter( m_hPrinter, 2, NULL, 0, &dw ); pi2 = (PRINTER_INFO_2 *)GlobalAllocPtr( GHND, dw ); GetPrinter( m_hPrinter, 2, (LPBYTE)pi2, dw, &dw ); // set the new printer info structure pi2->pDevMode = lpDevMode; SetPrinter( m_hPrinter, 2, (LPBYTE)pi2, 0 ); GlobalFreePtr( pi2 ); } else { // under Windows 95/98, use DocumentProperties to set the default DEVMODE DocumentProperties( NULL, m_hPrinter, (LPTSTR)(LPCTSTR)m_szPrinter, NULL, m_lpDevMode, DM_IN_BUFFER | DM_UPDATE ); } // notify applications that the default DEVMODE has changed SendMessageTimeout( HWND_BROADCAST, WM_DEVMODECHANGE, 0, (LPARAM)szPrinter, SMTO_NORMAL, 1000, NULL ); * This document has been converted to HTML using the Amyuni DHTML Converter