Click here to Skip to main content
Click here to Skip to main content

Tagged as

Port Monitor: How to Receive the Number of Document Copies During the Printing

, , 18 May 2010 CPOL
Rate this:
Please Sign up or sign in to vote.
In this article, we will examine a problem of receiving the correct value of the dmCopies variable in the DEVMODE structure while printing from Microsoft Word 2003.

Table of Contents

  1. Introduction
  2. Problem Review
  3. DEVMODE and Spool File
  4. Getting Copies Count
  5. Demo Project Class Architecture

1. Introduction

In this article, we will examine a problem of receiving the correct value of the dmCopies variable in the DEVMODE structure while printing from Microsoft Word 2003. This can be useful for the case of writing a program that controls printing (to printers, as well as to files).

2. Problem Review

During the printing, you can define the number of copies to print. You can receive the defined number of copies with the help of the GetJob function in the program (for more information, see this link). But the number of these copies will be invalid while printing from Microsoft Word 2003 and will always be equal to 1.

We will receive the JOB_INFO_2 structure (or the JOB_INFO_1 structure depending on the type, which was defined in the Level field). This structure, in its turn, contains the DEVMODE structure. The last one contains the important one for us - dmCopies value.

3. DEVMODE and Spool File

So, what is the process of receiving the DEVMODE.dmCopies value? We can receive the DEVMODE structure by intercepting the StartDocPort function. As it was mentioned before, we get the invalid number of copies while printing from MSWord. In this case, the correct dmCopies value is stored in the Spool File, which, in its turn, is created during the printing. This file contains the copy of the DEVMODE structure. We can get the last one in a different way (with the help of the GetJob function), but with the correct value of the dmCopies field. You should take into account that this copy of the structure is not created in the Spool File during printing from the majority of programs, and also this file has another structure. To get the full description of the Spool File structure, see this link.

The common universal algorithm of receiving the correct value of the number of copies includes the following steps:

  1. Receive the pointer to the Spool File.
  2. Check if the Spool File has the necessary format (it must have the EMFSPOOL format). Read the file header where the version (EMFSPOOL_VERSION) is stored. If the format of the header or the version do not correspond – move to the step 5, otherwise to step 3.
  3. Spool File consists of separate records with the indication of size and type of the record. All records go one by one serially. By reading each record by turn, we find the record of the DEVMODE structure. If the structure is not found, move to step 5, otherwise to step 4.
  4. Read the DEVMODE structure. You receive the correct value of the number of copies;
  5. Receive the number of copies with the help of the GetJob function (see the reference above).

Now let’s examine it in detail with the code examples.

Step 1. Receive the pointer to the SpoolFile with the help of the OpenPrinter function:

  HANDLE hSpoolFile = INVALID_HANDLE_VALUE;
  std::wstringstream sPrinterName;
  sPrinterName << pPrinterName;
  sPrinterName << _T(",Job ");
  sPrinterName << JobId;
  OpenPrinter((LPWSTR)sPrinterName.str().c_str(), &hSpoolFile, NULL);

Step 2. Check the Spool File for data correctness.

The following example shows how to perform data reading from the Spool File:

[Code from .\Printer\EmfSpoolReader.cpp]

BOOL ReadSpoolFile( HANDLE hSpoolFile, LPVOID pBuf, DWORD cbBuf )
{
    DWORD dwRead = 0;
    LPBYTE pTempBuf = (LPBYTE)pBuf;

    while ( ::ReadPrinter(hSpoolFile, pTempBuf, cbBuf, &dwRead) && (dwRead > 0) )
    {
        pTempBuf += dwRead;
        cbBuf -= dwRead;

        if (cbBuf == 0)
        {
            return TRUE;
        }
    }
    return FALSE;
}

So, we read the header of the Spool File and compare the version:

[Code from .\Printer\EmfSpoolReader.cpp]

#define EMFSPOOL_VERSION    0x00010000

//more information about EMRI_HEADER see in MS-EMFSPOOL specification
typedef struct tagEMRIHEADER { 
    DWORD dwVersion;
    DWORD cjSize;
    DWORD dpszDocName;
    DWORD dpszOutput;
} EMRI_HEADER, *PEMRI_HEADER;

BOOL OpenSpoolFile( HANDLE hSpoolFile )
{
    EMRI_HEADER splHeader = {0};

    BOOL bIsEmfData = ReadSpoolFile( hSpoolFile, &splHeader, sizeof(splHeader) ) 
				&& ( EMFSPOOL_VERSION == splHeader.dwVersion );
    if ( !bIsEmfData )
    {
        return FALSE;
    }

    DWORD nSize = splHeader.cjSize - sizeof(splHeader);
    if ( 0 != nSize )
    {
        std::vector<BYTE> buffer(nSize);
        ReadSpoolFile(hSpoolFile, &buffer.at(0), nSize);
    }

    return TRUE;
}

Steps 3 and 4. Look through the records of the Spool File, find the DEVMODE record, and read it:

[Code from .\Printer\EmfSpoolReader.cpp]

#define EMRI_DEVMODE        0x00000003

BOOL GetDevModeStruct( std::vector<BYTE> &buffer )
{
	if ( hSpoolFile == INVALID_HANDLE_VALUE )
	{
		return FALSE;
	}
	if ( NULL == hSpoolFile )
	{
		return FALSE;
	}

	for (;;)
	{
		EMRI_RECORD_HEADER emriRecordHeader = {0};

		if ( !ReadSpoolFile(&emriRecordHeader, sizeof(emriRecordHeader)) )
		{
			break;
		}
		
		if ( IsCorrectEmriHeader(emriRecordHeader) )
		{
			buffer.resize(emriRecordHeader.cjSize);

			if ( !ReadSpoolFile(&buffer.at(0), emriRecordHeader.cjSize) )
			{
				break;
			}

			if ( EMRI_DEVMODE == emriRecordHeader.ulID )
			{
				return TRUE;
			}
		}
		else
		{
			break;
		}
	}

	buffer.clear();
	return FALSE;
}

Step 5. If you did not receive the DEVMODE structure or the Spool File had another format during the previous steps, you can receive the structure with the help of the GetJob function (see the reference above):

[Code from .\Printer\Printer.cpp]

HANDLE hPrinter = 0;
HANDLE hSpoolFile = 0;

std::vector<BYTE> buffer;
PDEVMODE pDevModeFromSpool = NULL;
DEVMODE devMode = {0};
DWORD error = 0;

EmfSpoolReader reader(pPrinterName, JobId);
if ( reader.GetDevModeStruct(buffer) )
{
    pDevModeFromSpool = reinterpret_cast<PDEVMODE>(&buffer.at(0));
}
else if ( ::OpenPrinter(pPrinterName, &hPrinter, NULL) )
{
    GetDevMode(hPrinter, JobId, &devMode);
		::ClosePrinter(hPrinter);
}

Finally, if the pDevModeFromSpool pointer is equal to 0, the correct value of the number of copies is stored in the devMode, otherwise in the pDevModeFromSpool.

5. Demo Project

The demo project is performed in the form of the Port Monitor implementation and is the superstructure of the Local Port system monitor.

Demo project includes 2 projects:

  1. Port Monitor Installer, executable:
    • .\InstallPortMon\
    • InstallPortMon.cpp
    • Stdafx.cpp
    • Stdafx.h
  2. Port Monitor, dynamic library:
    • .\Printer\
    • emfspool.h
    • EmfSpoolReader.cpp
    • EmfSpoolReader.h
    • Printer.cpp
    • Printer.h
    • stdafx.cpp
    • stdafx.h
    • winsplp.h

All functions are called from the Local Port. An example of getting the number of copies is implemented in the StartDocPort function.

To view the result, do the following:

  1. Install the Port Monitor
  2. Add any printer to the Sample Port Monitor
  3. Send any document to the printer
  4. The number of copies will be displayed in the MessageBox

History

  • 18th May, 2010: Initial post

License

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

Share

About the Authors

Denys Podymskyy
Software Developer Apriorit Inc.
Ukraine Ukraine
No Biography provided
Group type: Organisation (No members)



Apriorit Inc
Apriorit Inc.
Ukraine Ukraine
ApriorIT is a Software Research and Development company that works in advanced knowledge-intensive scopes.
 
Company offers integrated research&development services for the software projects in such directions as Corporate Security, Remote Control, Mobile Development, Embedded Systems, Virtualization, Drivers and others.
 
Official site http://www.apriorit.com
Group type: Organisation

31 members

Follow on   LinkedIn

Comments and Discussions

 
QuestionWhether this application work with network printer with url like https://printer.mydomain.com/my_printer Pinmemberdileep Perumbavoor22-Jul-13 23:27 
Questionemf structure for spool file is different depending on number of copies PinmemberTrickyDicky211027-Oct-11 3:02 
I have used part of this code for my application (thanks for sharing!), but now I encountered something strange.
 
If I print a Word document and set the number of copies to 1, the spool file does not have a an emf record with DEVMODE. If I print the same document and set the number of copies to 2, the spool file DOES have a record with DEVMODE. In other words, depending of the number of copies, the emf spool file does or does not contain a DEVMODE record in the emf record list.
 
I am working on Windows 7
 
Does anybody know why and how to force the system so that the emf spool file ALWAYS contains a DEVMODE record?
 
Thanks in advance.
AnswerRe: emf structure for spool file is different depending on number of copies PinmemberTrickyDicky211031-Oct-11 0:40 
GeneralRe: emf structure for spool file is different depending on number of copies PinmemberTrickyDicky21102-Nov-11 0:54 
GeneralRe: emf structure for spool file is different depending on number of copies PinmemberDuncan Edwards Jones22-Jan-12 11:47 
QuestionHow is the running? PinmemberMember 329297413-Aug-11 12:39 
QuestionIs it really a hook? PinmemberKoep14-Dec-10 3:34 
Questionhow to set paper source ? [modified] Pinmemberriccardo6825-May-10 23:36 
AnswerRe: how to set paper source ? PingroupDenys Podymskyy26-May-10 0:08 
GeneralRe: how to set paper source ? Pinmemberriccardo6826-May-10 0:36 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141223.1 | Last Updated 18 May 2010
Article Copyright 2010 by Denys Podymskyy, Apriorit Inc
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid