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