Link to home
Start Free TrialLog in
Avatar of AdrianSRU
AdrianSRU

asked on

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
Avatar of George Tokas
George Tokas
Flag of Greece image

Hello Adrian...
What about using TPrinter and the Canvas property?

George Tokas.
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
Avatar of AdrianSRU
AdrianSRU

ASKER

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
>>>> 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
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
Maybe you should change:
printerDeviceMode->dmPaperSize = 0;
Check out:
https://www.experts-exchange.com/questions/21654539/Custom-paper-settings-problem-in-MFC.html

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

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


--Adrian
ASKER CERTIFIED SOLUTION
Avatar of George Tokas
George Tokas
Flag of Greece image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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