Click here to Skip to main content
15,878,748 members
Articles / Programming Languages / C++

Configuring Printer Settings Programmatically

,
Rate me:
Please Sign up or sign in to vote.
4.83/5 (20 votes)
30 Nov 2010CPOL3 min read 83.5K   3.8K   44   5
In this article, I will describe the method of setting the printer settings and the problems I faced on different OS versions.

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:
    C++
    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:
    C++
    LPDEVMODE  pDevMode = (LPDEVMODE)::malloc(dwNeeded);
  • Get the current printer settings with the DM_OUT_BUFFER parameter:
    C++
    ::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:
    C++
    // check that the driver supports changes 
    if(pDevMode ->dmFields & DM_COPIES )
        {
    	      pDevMode ->dmCopies = 3;   // define the number of copies as 3
            pDevMode ->dmFields |= DM_COPIES;  // define, which field was changed
        }
  • 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:
    C++
    ::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:
    C++
    ::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:
    C++
    PRINTER_INFO_2 *pi2 = (PRINTER_INFO_2  *)::GlobalAlloc(GPTR,dwNeeded);
  • Get the PRINTER_INFO_2 structure:
    C++
    GetPrinter(hPrinter, 2, (LPBYTE)pi2, dwNeeded, &dwNeeded);
  • Change the required parameters:
    C++
        // check that the driver supports the changes 
        if(pi2->pDevMode->dmFields & DM_PAPERSIZE )
        {
    // define the page size as A3
             pi2->pDevMode->dmPaperSize = DMPAPER_A3;
    // define, which field was changed
             pi2->pDevMode->dmFields |= DM_PAPERSIZE;      
     }
  • Update data:
    C++
    ::DocumentProperties  (NULL, hPrinter, PrinterName,
            		pi2->pDevMode, pi2->pDevMode,
            		DM_IN_BUFFER | DM_OUT_BUFFER);
  • Update the printer information by calling the SetPrinter function:
    C++
    ::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:

    C++
    PDEVMODE pDevmodeSupport = NULL;
    
    if( WinVerUtils::IsWin2k() )
    {
     // the DevmodeCopy function copies the fields from one 
     //structure to another byte by byte
     DevmodeCopy(srcDevmode, *pi2.pDevMode);
    
     // the ChangePrinterProperties function changes the printer parameters 
     // and returns the changed structure
     pDevmodeSupport = ChangePrinterProperties(szPrinterName, &srcDevmode, Printer);
    }
    else if( WinVerUtils::IsWin2k3OrLower() )
    {
     DevmodeCopy(srcDevmode, *pi2.pDevMode);
     // an attempt to change the printer parameters
          pDevmodeSupport = ChangePrinterProperties(szPrinterName, &srcDevmode, NULL);
    
    if ( pDevmodeSupport )
    {
    	// do not try to install the security descriptor
    pi2.pSecurityDescriptor = NULL; 
    // restore the bit mask of the supported data
           		pi2.pDevMode->dmFields = pDevmodeSupport->dmFields;
    // dmDriverExtra contains an amount of private data 
    // that follows the structure immediately 
           		pi2.pDevMode->dmDriverExtra = 0; 
    }
    }
    else if( WinVerUtils::IsWin2008R2() )
    {
    	// save the bit mask of the supported data
    pDevmodeSupport = ChangePrinterProperties(szPrinterName, &srcDevmode, NULL);
           if ( pDevmodeSupport )
            {
                DevmodeCopy(srcDevmode, *pi2.pDevMode);
    	     // restore the bit mask
                pi2.pDevMode->dmFields = pDevmodeSupport->dmFields;
            }
    }
    else
    {
     DevmodeCopy(srcDevmode, *pi2.pDevMode);
    }
    
    if ( pDevmodeSupport )
    {
    ::free(pDevmodeSupport);
    }
  • Let’s consider the function:
    C++
    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.

    C++
    {
           …
    	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) )
        	{
    //The function fails. To get extended error information, call GetLastError.
        	}
    	… 
    }

    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:

C++
PDEVMODEW ChangePrinterProperties( IN CString& szPrinterName, 
    IN PDEVMODE  pSrcDevMode, 
    IN HANDLE    hPrinter )

The function that defines the DEVMODE printer structure for the current OS:

C++
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:

C++
VOID DevmodeCopy(  IN const DEVMODE& fromDevMode,  OUT DEVMODE&  toDevMode )

The function that sets the settings with devMode to the printer with the szPrinterName name:

C++
BOOL SetPrinterSetting(  IN CString&  szPrinterName, IN  DEVMODE& devMode  )

WinVerUtils.h, WinVerUtils.cpp contain the functions for checking of the current OS version. For example:

C++
BOOL WinVerUtils::IsWin2k(); 

WinErrorUtils.h contains the function of obtaining the error code description:

C++
CString  winErrorUtils::GetErrorDescription( DWORD dwErrorCode  )  

traceUtils.h contains the macros for displaying of the text and error description in the console.

C++
#define PRINT(message) \
            std::wcout<<message<<"\n";

#define PRINT_ERROR(error) \
            PRINT(winErrorUtils::GetErrorDescription(error).GetBuffer()); 

History

  • 30th November, 2010: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Chief Technology Officer Apriorit Inc.
United States United States
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 is a Organisation

33 members

Written By
Ukraine Ukraine
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 3 Pin
buyong19-Oct-11 19:31
buyong19-Oct-11 19:31 
QuestionHow to setting the margin [modified] Pin
Max++5-Feb-11 14:44
Max++5-Feb-11 14:44 
Thanks for your nice article.

How can I change margin setting of printing?

modified on Saturday, February 5, 2011 9:02 PM

AnswerRe: How to setting the margin Pin
Alexandr Sirenko7-Feb-11 4:14
Alexandr Sirenko7-Feb-11 4:14 
GeneralDescribe OS specific problems encountered Pin
Damir Valiulin10-Dec-10 19:47
Damir Valiulin10-Dec-10 19:47 
AnswerRe: Describe OS specific problems encountered Pin
Alexandr Sirenko14-Dec-10 23:15
Alexandr Sirenko14-Dec-10 23:15 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.