Introduction
The usage of DocumentProperties
and SetPrinter
API functions gives an opportunity to change the printer settings. But, when using them, I could not have a stable method of changing settings on all Windows OS versions. Below, all possible methods of changing printer parameters will be described and also the solution of how to obtain the stabler behavior for different Windows OS versions.
Changing Printer Settings
You can change the printer settings with the help of API:
DocumentProperties
- Retrieves and changes the printer parameters SetPrinter
- Determines data for printer, changes the state, and also can manage the printing and tasks
Changing Settings Using the DocumentProperties Function
To use the DocumentProperties
function to change the printer settings, you should do as follows:
- Get the number of bytes required for the
DEVMODE
structure:
DWORD dwNeeded = ::DocumentProperties(NULL, hPrinter, szPrinterName, NULL, NULL, 0)
where:
hPrinter
– A HANDLE to your local printer that can be obtained by calling OpenPrinter
or AddPrinter
szPrinterName
- name of the local printer.
- Allocate memory for the
DEVMODE
structure:
LPDEVMODE pDevMode = (LPDEVMODE)::malloc(dwNeeded);
- Get the current printer settings with the
DM_OUT_BUFFER
parameter:
::DocumentProperties(NULL, hPrinter, szPrinterName,
pDevMode, NULL, DM_OUT_BUFFER);
DM_OUT_BUFFER
- points that the function will write the current settings to the pDevMode
structure. - Change the required parameters:
if(pDevMode ->dmFields & DM_COPIES )
{
pDevMode ->dmCopies = 3; pDevMode ->dmFields |= DM_COPIES; }
- The next call of
DocumentProperties
with DM_IN_BUFFER
and DM_OUT_BUFFER
parameters configures settings for the printer. The returned structure can be used as an argument in the call of the CreateDC
function:
::DocumentProperties(NULL, hPrinter, szPrinterName,
pDevMode, pDevMode, DM_IN_BUFFER | DM_OUT_BUFFER);
The utilizing of the DocumentProperties
function doesn’t always make it possible to change the printer settings on different OS versions. Let’s take a look at the solution of this problem below.
Changing Settings Using the SetPrinter Function
To use the SetPrinter
function to change the printer settings, do as follows:
- Call the
GetPrinter
function to get the size of the PRINTER_INFO_2
structure:
::GetPrinter(hPrinter, 2, 0, 0, &dwNeeded);
where:
hPrinter
- A HANDLE to your local printer that can be obtained by calling OpenPrinter or AddPrinter; - 2 - A level, which indicates that the size of the
PRINTER_INFO_2
structure is inquired and that it contains the detailed information about the printer (for more detailed information about different structure types, see MSDN); dwNeeded
- Size of the PRINTER_INFO_2
structure in bytes.
- Allocate memory for the
PRINTER_INFO_2
structure:
PRINTER_INFO_2 *pi2 = (PRINTER_INFO_2 *)::GlobalAlloc(GPTR,dwNeeded);
- Get the
PRINTER_INFO_2
structure:
GetPrinter(hPrinter, 2, (LPBYTE)pi2, dwNeeded, &dwNeeded);
- Change the required parameters:
if(pi2->pDevMode->dmFields & DM_PAPERSIZE )
{
pi2->pDevMode->dmPaperSize = DMPAPER_A3;
pi2->pDevMode->dmFields |= DM_PAPERSIZE;
}
- Update data:
::DocumentProperties (NULL, hPrinter, PrinterName,
pi2->pDevMode, pi2->pDevMode,
DM_IN_BUFFER | DM_OUT_BUFFER);
- Update the printer information by calling the
SetPrinter
function:
::SetPrinter(hPrinter, 2, (LPBYTE)pi2, 0);
On some operating systems, the call of the SetPrinter
function causes the program crash or this function does not work. The research of this problem brought me to the following results.
Before calling the SetPrinter
function, call the DocumentProperties
function with some additional actions to update the DEVMODE
structure. The following piece of code demonstrates the methods of settings updating for different operating systems:
PDEVMODE pDevmodeSupport = NULL;
if( WinVerUtils::IsWin2k() )
{
DevmodeCopy(srcDevmode, *pi2.pDevMode);
pDevmodeSupport = ChangePrinterProperties(szPrinterName, &srcDevmode, Printer);
}
else if( WinVerUtils::IsWin2k3OrLower() )
{
DevmodeCopy(srcDevmode, *pi2.pDevMode);
pDevmodeSupport = ChangePrinterProperties(szPrinterName, &srcDevmode, NULL);
if ( pDevmodeSupport )
{
pi2.pSecurityDescriptor = NULL;
pi2.pDevMode->dmFields = pDevmodeSupport->dmFields;
pi2.pDevMode->dmDriverExtra = 0;
}
}
else if( WinVerUtils::IsWin2008R2() )
{
pDevmodeSupport = ChangePrinterProperties(szPrinterName, &srcDevmode, NULL);
if ( pDevmodeSupport )
{
DevmodeCopy(srcDevmode, *pi2.pDevMode);
pi2.pDevMode->dmFields = pDevmodeSupport->dmFields;
}
}
else
{
DevmodeCopy(srcDevmode, *pi2.pDevMode);
}
if ( pDevmodeSupport )
{
::free(pDevmodeSupport);
}
- Let’s consider the function:
ChangePrinterProperties( IN CString& szPrinterName,
OUT PRINTER_INFO_2& pi2,
IN DEVMODE& srcDevmode,
IN HANDLE hPrinter )
This function returns the modified DEVMODE
structure after the pSrcDevMode
structure was used with the help of the DocumentProperties
API function.
{
…
pDevMode->dmFields |= pSrcDevMode->dmFields & DM_YRESOLUTION;
pDevMode->dmYResolution = pSrcDevMode->dmYResolution;
if ( IDOK != ::DocumentProperties(NULL, hPrinter,
szPrinterName.GetBuffer(), pDevMode, pDevMode,
DM_IN_BUFFER | DM_OUT_BUFFER) )
{
}
…
}
Using the method described above, I managed to obtain the stabler method to apply the settings to local printers on different Windows OS versions.
Description of the Project Modules
PrinterUtils.h, PrinterUtils.cpp
The function that returns the DEVMODE
structure after applying the pSrcDevMode
structure:
PDEVMODEW ChangePrinterProperties( IN CString& szPrinterName,
IN PDEVMODE pSrcDevMode,
IN HANDLE hPrinter )
The function that defines the DEVMODE
printer structure for the current OS:
VOID ChangePrinterSettingsForCurrentOS( IN CString& szPrinterName,
OUT PRINTER_INFO_2& pi2,
IN DEVMODE& srcDevMode,
IN HANDLE hPrinter )
The function that performs copying from fromDevMode
to toDevMode
by fields:
VOID DevmodeCopy( IN const DEVMODE& fromDevMode, OUT DEVMODE& toDevMode )
The function that sets the settings with devMode
to the printer with the szPrinterName
name:
BOOL SetPrinterSetting( IN CString& szPrinterName, IN DEVMODE& devMode )
WinVerUtils.h, WinVerUtils.cpp contain the functions for checking of the current OS version. For example:
BOOL WinVerUtils::IsWin2k();
WinErrorUtils.h contains the function of obtaining the error code description:
CString winErrorUtils::GetErrorDescription( DWORD dwErrorCode )
traceUtils.h contains the macros for displaying of the text and error description in the console.
#define PRINT(message) \
std::wcout<<message<<"\n";
#define PRINT_ERROR(error) \
PRINT(winErrorUtils::GetErrorDescription(error).GetBuffer());
History
- 30th November, 2010: Initial post
ApriorIT is a software research and development company specializing in cybersecurity and data management technology engineering. We work for a broad range of clients from Fortune 500 technology leaders to small innovative startups building unique solutions.
As Apriorit offers integrated research&development services for the software projects in such areas as endpoint security, network security, data security, embedded Systems, and virtualization, we have strong kernel and driver development skills, huge system programming expertise, and are reals fans of research projects.
Our specialty is reverse engineering, we apply it for security testing and security-related projects.
A separate department of Apriorit works on large-scale business SaaS solutions, handling tasks from business analysis, data architecture design, and web development to performance optimization and DevOps.
Official site: https://www.apriorit.com
Clutch profile: https://clutch.co/profile/apriorit
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.