Browse DevX
Sign up for e-mail newsletters from DevX


Empower Your Printing with Custom Print Processors : Page 3

Using Custom Print Processors is one of the many ways to take control of the printing process. Find out how to use this user-mode DLL and C/C++ to enforce security policies, add missing printing functionality, or alter print jobs.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

How to Implement Your Own Print Processor
Using the Microsoft DDK sample for your Print Processor makes it really easy to develop your own. The code in this article is based on the genprint DDK and the sample, written in C, was converted to C++ to benefit from object-oriented model strengths.

The sample Print Processor which comes with this article contains an additional separator page for each print job with customizable content: a 1000x1000 bitmap image located at C:\SeparatorPP\Separator.bmp. You may replace it with a picture of your choice and the Print Processor will use it.

Take some time to go over the source files and understand their roles. Table 2 lists these files with their appropriate descriptions and types:

  • DDK: This means the file was taken from the Microsoft DDK sample.
  • DDK Modified: This means the file was taken from the Microsoft DDK sample and modified.
  • New: This means the file is an entirely new creation.

File Name




Exports definition files

DDK (renamed from winprint.def)


This file implements functions that should be exported by every Print Processor

DDK (renamed from genprint.c)

local.h; local.cpp

Debugging output functions

DDK (local.cpp is renamed from local.c)


Contains support routines for the Print Processor

DDK (renamed from support.c)


Spool memory management functions

DDK (renamed from util.c)


Handles the RAW data type printing

DDK (renamed from raw.c)


Handles the TEXT data type printing

DDK (renamed from text.c)


Handles sending formfeed to a printer

DDK (renamed from parsparm.c)


Handles the NT EMF data type printing; this is where the sample code involves addding a separator page

DDK Modified (renamed from emf.c)


Defines the abstract base class (CEMFJobModifier) for EMF job modifications


JobSeparator.h; JobSeparator.cpp

Defines and implements the job modifier class, which adds the separator page; derives from the CEMFJobModifier class


Table 2. The table lists the sample source files and provides a short description and the origin of each file.

Because the DDK sample was originally written in pure C and converted to C++, all the files have the CPP extensions instead of the original C extensions.

Run the new code during the execution of the PrintDocumentOnPrintProcessor exported function. Listing 1 shows the standard implementation of the function (taken from SeparatorPP.cpp). As you can see, the function routes the execution to the appropriate handler, depending on the print job data type. For the sake of simplicity, this sample adds a separator only to NT EMF print jobs. This means you'll be concentrating on the PrintEMFJob function located in the emf.cpp source file. The function accepts two parameters:

  • PPRINTPROCESSORDATA: The pointer to the job data.
  • LPWSTR: The document name.
Listing 2 shows the code for the PrintEMFJob function. Only the relevant code appears (for the full code, refer to the source code). The bold lines of code show the modifications that were made to support the separator page functionality. One of the first stages in printing the NT EMF job is to obtain the device context for the printer device (hPrinterDC):

hSpoolHandle = GdiGetSpoolFileHandle(pData->pPrinterName, pDevmode, pDocumentName); if (hSpoolHandle) { hPrinterDC = GdiGetDC(hSpoolHandle); }

The code then uses this handle to add the separator page. Right after the PrintEMFJob function starts the document printing, create an instance of the CJobSeparator class and modify the print job:

DocInfo.cbSize = sizeof(DOCINFOW); DocInfo.lpszDocName = pData->pDocument; DocInfo.lpszOutput = pData->pOutputFile; DocInfo.lpszDatatype = NULL; if (!GdiStartDocEMF(hSpoolHandle, &DocInfo)) goto CleanUp; bStartDoc = TRUE; //////////////////////////////////////////////////////////// // Adding Separator Page CJobSeparator JobSeparator(hPrinterDC); JobSeparator.ModifyPrintout(); // End of Adding Separator Page ////////////////////////////////////////////////////////////

The actual job modification happens in the ModifyPrintout function, which is implemented by the CJobSeparator class (see Listing 3).

The StartPage API starts a new page, where you draw the bitmap loaded from the C:\SeparatorPP\Separator.bmp file. ModifyPrintout uses the helper method RetrieveBitmapInfo to build the BITMAPINFO structure prior to stretching the bitmap to the printer device context. Finally, EndPage finishes printing the page.

After adding the separator page, PrintEMFJob completes the print job by playing EMF records—with the help of the GDI API—and translating them into the specific printer device language.

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date