Solved

Set Printer Page Size API

Posted on 2007-03-28
12
2,324 Views
Last Modified: 2013-11-23
I have written an application in Borland C++ Builder 5 to print address information on custom sized envelopes.  Everything worked fine until I got a new printer (HP Photosmart 8450).  This printer is smarter than my old one and when I try and print an envelope, it takes it in and then refuses to print saying that the size of the paper is too small.  Apparently it can detect that the envelope, inserted sideways, is only 4 inches wide.

I came up with a piece of code to set the printer page size so that the 4 inches (or whatever the size of the envelope) will be the expected paper width.  Unfortunately, when I run that piece of code in my program it seems to corrupt the driver or something, because not only does my program no longer print, but no other program can print either.  I get a message in my program that says: "Printer Selected is not valid."  Restarting the computer and/or printer has no effect.  The only way I can print again is if I reinstall the driver.

Here is my code:

    HANDLE printerHandle = INVALID_HANDLE_VALUE;
    AnsiString printerDriver = AnsiString( "" );
    AnsiString printerPort = AnsiString( "" );
    PRINTER_DEFAULTS printerDefaults;
    ZeroMemory( ( void * ) &printerDefaults, sizeof( PRINTER_DEFAULTS ) );
    printerDefaults.DesiredAccess = PRINTER_ALL_ACCESS;
    PRINTER_INFO_2 *printerInfo2 = NULL;
    if( OpenPrinter( printerName.c_str( ), &printerHandle, &printerDefaults ) != 0 )
    {
        DWORD printerInfoSize = 0;
        GetPrinter( printerHandle, 2, 0, 0, &printerInfoSize );
        if( printerInfoSize == 0 )
            ShowMessage( getErrorMessageWinAPI( GetLastError( ) ) );
        else
        {
            printerInfo2 = ( PRINTER_INFO_2 * ) new BYTE[ printerInfoSize ];
            if( GetPrinter( printerHandle, 2, ( BYTE * ) printerInfo2, printerInfoSize, &printerInfoSize ) != 0 )
            {
                printerInfo2->pDevMode->dmFields = DM_PAPERSIZE | DM_PAPERLENGTH | DM_PAPERWIDTH;
                printerInfo2->pDevMode->dmPaperSize = DMPAPER_USER;
                printerInfo2->pDevMode->dmPaperLength = envelopeLength;
                printerInfo2->pDevMode->dmPaperWidth = envelopeWidth;
                if( SetPrinter( printerHandle, 2, ( BYTE * ) printerInfo2, 0 ) == 0 )
                    ShowMessage( getErrorMessageWinAPI( GetLastError( ) ) );
                SendMessageTimeout( HWND_BROADCAST, WM_DEVMODECHANGE, 0, ( LPARAM ) PrinterComboBox->Text.c_str( ), SMTO_NORMAL, 5000, NULL );
            }
            delete [ ] ( BYTE * ) printerInfo2;
        }
    }
    ClosePrinter( printerHandle );

Please help me find a way to set the printer page size.  Thank you in advance for your responses.


--Adrian
0
Comment
Question by:AdrianSRU
  • 5
  • 5
  • 2
12 Comments
 
LVL 16

Expert Comment

by:George Tokas
ID: 18814226
Hello Adrian...
What about using TPrinter and the Canvas property?

George Tokas.
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 18814427
George is right. You first should try to set the printer options via TPrinter rather than manipulating the driver directly. The printing size normally was determined by choosing the appropriate printer size format like letter, A5 rather than pass the height in millimeter, inches  or pixels...

>>>> ZeroMemory( ( void * ) &printerDefaults, sizeof( PRINTER_DEFAULTS ) );
With that you make the member printerDefaults.pDevMode a NULL pointer what should be a valid pointer to a DEVMODE structure. Also the member printerDefaults.pDatatype most likely should not be NULL but should point to a char string that is the default data type for the printer.

If you really need to go that way you should retrieve the current (valid) defaults, e. g via GetPrinterDeviceDefaults, and copy the datta rather than creating new ones and make all zero.

Regards, Alex
0
 
LVL 12

Author Comment

by:AdrianSRU
ID: 18815127
Thank you both very much for your responses.

George, I would love to be able to do this with TPrinter instead of messing around in the API but I haven't been able to find out how to do it.  The PageHeight and PageWidth properties of TPrinter are read-only and I haven't been able to find any way to change the size of the Canvas.

Alex, I don't think that PRINTER_DEFAULTS is my problem.  This link is to a page on microsoft support and it uses the ZeroMemory function on the PRINTER_DEFAULTS object the same way that I have it in my code: http://support.microsoft.com/kb/140285


--Adrian
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 18815328
>>>> I don't think that PRINTER_DEFAULTS is my problem

I checked the docs for structure PRINTER_DEFAULTS and there is no hint that the pointers can be NULL. Moreover, I already used that struct in a project last year and I know for sure that I had to get reasonable defaults.

>>>> but I haven't been able to find out how to do it.
Did you try to set the paper size via printer setup?  If it works that way you may debug the printer options once with the wrong settings and once with the correct settings. After that you should know what option has changed and look for a TPrinter member function where you can pass that attribute (or google for it). A readonly attribute mostly means that you need to set it somewhere else, e. g. setting the attributes of the printer setup rather than the attributes of the printer. Check the docu whether there is a class for the printer setup dialog. If so, you may be able to call it in a hidden mode - without showing the dialog - but can set the defaults programmatically.

Regards, Alex
0
 
LVL 16

Expert Comment

by:George Tokas
ID: 18815635
Check this out Adrian:
http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_10244734.html
Delphi but useful...

George Tokas.
0
 
LVL 12

Author Comment

by:AdrianSRU
ID: 18816215
George, thanks for the link.  It is partially working now.  I can change the paper size if I specify one of the pre-set paper sizes in the dmPaperSize property, but it doesn't work when I set a custom page size with dmPaperLength and dmPaperWidth.

    char printerDevice[ 256 ] = "", printerDriver[ 256 ] = "", printerPort[ 256 ] = "";
    THandle printerDeviceModeHandle = 0;
    Printer( )->GetPrinter( printerDevice, printerDriver, printerPort, printerDeviceModeHandle );
    if( printerDeviceModeHandle != 0 )
    {
        DEVMODE *printerDeviceMode = ( DEVMODE * ) GlobalLock( ( void * ) printerDeviceModeHandle );
        if( printerDeviceMode != NULL )
        {
            printerDeviceMode->dmFields = DM_PAPERSIZE | DM_PAPERLENGTH | DM_PAPERWIDTH;
            printerDeviceMode->dmPaperSize = DMPAPER_USER;
            printerDeviceMode->dmPaperLength = envelopeLength;
            printerDeviceMode->dmPaperWidth = envelopeWidth;
            GlobalUnlock( printerDeviceMode );
            Printer( )->PrinterIndex = Printer( )->PrinterIndex;
        }
    }

If I set dmPaperSize=DMPAPER_ENV_10 (standard envelope size), the PageHeight and PageWidth properties of TPrinter get set to the proper size.  However, with the above code where I set a custom size, the PageHeight and PageWidth properties are unaffected.  Am I doing something wrong here?


--Adrian
0
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

 
LVL 16

Expert Comment

by:George Tokas
ID: 18816672
Maybe you should change:
printerDeviceMode->dmPaperSize = 0;
Check out:
http://www.experts-exchange.com/Programming/System/Windows__Programming/MFC/Q_21654539.html

George.
0
 
LVL 12

Author Comment

by:AdrianSRU
ID: 18817075
It still doesn't work with dmPaperSize=0.  Here is my code with some additional numbers that may help:

This is with using the standard envelope size:

ShowMessage( Printer( )->PageHeight );    // prints 6487
ShowMessage( Printer( )->PageWidth );    // prints 4950

    char printerDevice[ 256 ] = "", printerDriver[ 256 ] = "", printerPort[ 256 ] = "";
    THandle printerDeviceModeHandle = 0;
    Printer( )->GetPrinter( printerDevice, printerDriver, printerPort, printerDeviceModeHandle );
    if( printerDeviceModeHandle != 0 )
    {
        DEVMODE *printerDeviceMode = ( DEVMODE * ) GlobalLock( ( void * ) printerDeviceModeHandle );
        if( printerDeviceMode != NULL )
        {
            printerDeviceMode->dmFields = DM_PAPERSIZE;
            printerDeviceMode->dmPaperSize = DMPAPER_ENV_10;
            GlobalUnlock( printerDeviceMode );
            Printer( )->PrinterIndex = Printer( )->PrinterIndex;
        }
    }

ShowMessage( Printer( )->PageHeight );    // prints 5320
ShowMessage( Printer( )->PageWidth );    // prints 2325

The standard size changed it properly.  Height: 6487->5320.  Width: 4950->2325

Now this is with the custom size:

ShowMessage( Printer( )->PageHeight );    // prints 6487
ShowMessage( Printer( )->PageWidth );    // prints 4950

    char printerDevice[ 256 ] = "", printerDriver[ 256 ] = "", printerPort[ 256 ] = "";
    THandle printerDeviceModeHandle = 0;
    Printer( )->GetPrinter( printerDevice, printerDriver, printerPort, printerDeviceModeHandle );
    if( printerDeviceModeHandle != 0 )
    {
        DEVMODE *printerDeviceMode = ( DEVMODE * ) GlobalLock( ( void * ) printerDeviceModeHandle );
        if( printerDeviceMode != NULL )
        {
            printerDeviceMode->dmFields = DM_PAPERSIZE | DM_PAPERLENGTH | DM_PAPERWIDTH;
            printerDeviceMode->dmPaperSize = 0;
            printerDeviceMode->dmPaperLength = 2394;
            printerDeviceMode->dmPaperWidth = 1016;
            GlobalUnlock( printerDeviceMode );
            Printer( )->PrinterIndex = Printer( )->PrinterIndex;
        }
    }

ShowMessage( Printer( )->PageHeight );    // prints 6487
ShowMessage( Printer( )->PageWidth );    // prints 4950

The custom size didn't change PageHeight and PageWidth at all.  I don't get it.


--Adrian
0
 
LVL 16

Expert Comment

by:George Tokas
ID: 18817743
Maybe:
printerDeviceMode->dmFields = DM_PAPERSIZE ;
Did you try it that way??
Because of the zero...

George.
0
 
LVL 12

Author Comment

by:AdrianSRU
ID: 18822602
George, no change.  Is it possible that my printer just doesn't support custom page sizes?


--Adrian
0
 
LVL 16

Accepted Solution

by:
George Tokas earned 500 total points
ID: 18822715
I think that GetCaps can determine if it supports...
The fact is that I never used TPrinter with custom page before and also there were very few times I used a printer in my projects....
Those were mostly from bibliography (kind of say) than experience...

George.
0
 
LVL 12

Author Comment

by:AdrianSRU
ID: 18982274
Well, I haven't found a way to get my printer to accept a custom page size but I have found a workable solution.  I figured out how to use DeviceCapabilities to get a list of the paper sizes that the printer supports as well as the corresponding number to assign to dmPaperSize.  Then I just loop through all of the sizes and find one that is as close as possible to the custom size that I need.  The code that I used is:

    char printerDevice[ 256 ] = "", printerDriver[ 256 ] = "", printerPort[ 256 ] = "";
    THandle printerDeviceModeHandle = 0;
    Printer( )->GetPrinter( printerDevice, printerDriver, printerPort, printerDeviceModeHandle );
    if( printerDeviceModeHandle != 0 )
    {
        DEVMODE *printerDeviceMode = ( DEVMODE * ) GlobalLock( ( void * ) printerDeviceModeHandle );
        if( printerDeviceMode != NULL )
        {    
            bool minSizeFound = false;
            WORD minWidthDifferenceSize = 0;
            try
            {
                DWORD resultPaperSize = 0, resultPapers = 0;
                resultPaperSize = DeviceCapabilities( PrinterComboBox->Text.c_str( ), printerPort, DC_PAPERSIZE, NULL, NULL );
                resultPapers = DeviceCapabilities( PrinterComboBox->Text.c_str( ), printerPort, DC_PAPERS, NULL, NULL );
                if( resultPaperSize > 0 && resultPaperSize == resultPapers )
                {
                    int bufferSize = resultPaperSize * sizeof( POINT );
                    BYTE *buffer = new BYTE[ bufferSize ];
                    resultPaperSize = DeviceCapabilities( PrinterComboBox->Text.c_str( ), printerPort, DC_PAPERSIZE, buffer, NULL );
                    WORD papers[ 9999 ];
                    resultPapers = DeviceCapabilities( PrinterComboBox->Text.c_str( ), printerPort, DC_PAPERS, ( char * ) &papers, NULL );
                    if( resultPaperSize > 0 && resultPaperSize == resultPapers )
                    {
                        double minWidthDifference = 999999;
                        for( DWORD i = 0; i < resultPaperSize; i++ )
                        {
                            POINT p;
                            Move( buffer + i * sizeof( POINT ), ( void * ) &p, sizeof( POINT ) );
                            double paperSizeWidth = p.x / 254.0508001016 * PixelsPerInch - printerOffsetX;
                            double paperSizeHeight = p.y / 254.0508001016 * PixelsPerInch - printerOffsetY;

                            double widthDifference = paperSizeWidth - bitmap->Width;
                            if( widthDifference < 0 )
                                widthDifference *= -1;
                            if( widthDifference < minWidthDifference && paperSizeHeight > bitmap->Width * 0.9 )
                            {
                                minSizeFound = true;
                                minWidthDifferenceSize = papers[ i ];
                                minWidthDifference = widthDifference;
                            }
                        }
                    }
                    delete [ ] buffer;
                }
            } catch( ... ) { }

            if( minSizeFound )
            {
                printerDeviceMode->dmFields = DM_PAPERSIZE;
                printerDeviceMode->dmPaperSize = minWidthDifferenceSize;
            }
            GlobalUnlock( ( void * ) printerDeviceModeHandle );
            Printer( )->PrinterIndex = Printer( )->PrinterIndex;
        }
    }

Thanks for your help George!


--Adrian
0

Featured Post

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Introduction This article is the first in a series of articles about the C/C++ Visual Studio Express debugger.  It provides a quick start guide in using the debugger. Part 2 focuses on additional topics in breakpoints.  Lastly, Part 3 focuses on th…
How to install Selenium IDE and loops for quick automated testing. Get Selenium IDE from http://seleniumhq.org Go to that link and select download selenium in the right hand columnThat will then direct you to their download page.From that page s…
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.

920 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now