Solved

Set Printer Page Size API

Posted on 2007-03-28
12
2,322 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
Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

 
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

6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

Join & Write a Comment

Here is a helpful source code for C++ Builder programmers that allows you to manage and manipulate HTML content from C++ code, while also handling HTML events like onclick, onmouseover, ... Some objects defined and used in this source include: …
Container Orchestration platforms empower organizations to scale their apps at an exceptional rate. This is the reason numerous innovation-driven companies are moving apps to an appropriated datacenter wide platform that empowers them to scale at a …
THe viewer will learn how to use NetBeans IDE 8.0 for Windows to perform CRUD operations on a MySql database.
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.

747 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

12 Experts available now in Live!

Get 1:1 Help Now