Solved

Set Printer Page Size API

Posted on 2007-03-28
12
2,351 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
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
 
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

Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

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

Suggested Solutions

Title # Comments Views Activity
Copying WordPress Pages 5 93
C++ question 3 74
Online file editor, manager 6 101
How can I build my own IDE using ASP.NET MVC? 2 65
Introduction This article is a continuation of the C/C++ Visual Studio Express debugger series. Part 1 provided a quick start guide in using the debugger. Part 2 focused on additional topics in breakpoints. As your assignments become a little more …
Many modern programming languages support the concept of a property -- a class member that combines characteristics of both a data member and a method.  These are sometimes called "smart fields" because you can add logic that is applied automaticall…
The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

733 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