• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 2408
  • Last Modified:

Set Printer Page Size API

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
AdrianSRU
Asked:
AdrianSRU
  • 5
  • 5
  • 2
1 Solution
 
George TokasCommented:
Hello Adrian...
What about using TPrinter and the Canvas property?

George Tokas.
0
 
itsmeandnobodyelseCommented:
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
 
AdrianSRUAuthor Commented:
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
Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

 
itsmeandnobodyelseCommented:
>>>> 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
 
George TokasCommented:
Check this out Adrian:
http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_10244734.html
Delphi but useful...

George Tokas.
0
 
AdrianSRUAuthor Commented:
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
 
George TokasCommented:
Maybe you should change:
printerDeviceMode->dmPaperSize = 0;
Check out:
http://www.experts-exchange.com/Programming/System/Windows__Programming/MFC/Q_21654539.html

George.
0
 
AdrianSRUAuthor Commented:
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
 
George TokasCommented:
Maybe:
printerDeviceMode->dmFields = DM_PAPERSIZE ;
Did you try it that way??
Because of the zero...

George.
0
 
AdrianSRUAuthor Commented:
George, no change.  Is it possible that my printer just doesn't support custom page sizes?


--Adrian
0
 
George TokasCommented:
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
 
AdrianSRUAuthor Commented:
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

Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

  • 5
  • 5
  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now