PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0

Sunday, September 18, 2022

[FIXED] Why might DeviceCapabilities() return 4294967295 for DC_BINS?

 September 18, 2022     printing, winapi     No comments   

Issue

I'm fetching the selected printer tray from a WIN32 call to PrintDlgEx(). This seems to work successfully most of the time, but recently I added a new printer to my machine (a DYMO LabelWriter 450) and it caused my simple software to fail.

Upon investigation, the call to DeviceCapabilities() for DC_BINS is returning 4294967295, while all of the other printers I've tested so far return single digit bin counts.

My first inclination is to omit the bin name when the bin count is greater than a given threshold (say... 20?), but I don't love this solution.

Is there a known reason that a printer would return the max UNSIGNED INT value for this? Is it just poorly written drivers, or is there an alternate meaning? Or perhaps I totally misunderstand the intended value.

If I have to write an arbitrary cap I will, but I'd like to better understand why this situation exists. Clearly, this printer doesn't have billions of different printer trays.

Here's an MRE:

    HINSTANCE hinst = GetModuleHandle(NULL);
    HRESULT hResult;
    PRINTDLGEX pdx = {0};
    LPPRINTPAGERANGE pPageRanges = NULL;
    HWND hWndOwner = GetForegroundWindow();

    if(!hWndOwner){
        hWndOwner = GetDesktopWindow();
    }

    // Allocate an array of PRINTPAGERANGE structures.
    pPageRanges = (LPPRINTPAGERANGE) GlobalAlloc(GPTR, 10 * sizeof(PRINTPAGERANGE));
    if(!pPageRanges){
        return wprintf(L"{\"error\": \"%s\"}", GetLastError()); // "Your computer does not have enough memory to complete this operation:"
    }

    //  Initialize the PRINTDLGEX structure.
    pdx.lStructSize = sizeof(PRINTDLGEX);
    pdx.hwndOwner = hWndOwner;
    pdx.hDevMode = NULL;
    pdx.hDevNames = NULL;
    pdx.hDC = NULL;
    pdx.Flags = PD_RETURNDC | PD_COLLATE;
    pdx.Flags2 = 0;
    pdx.ExclusionFlags = 0;
    pdx.nPageRanges = 0;
    pdx.nMaxPageRanges = 10;
    pdx.lpPageRanges = pPageRanges;
    pdx.nMinPage = 1;
    pdx.nMaxPage = 1000;
    pdx.nCopies = 1;
    pdx.hInstance = 0;
    pdx.lpPrintTemplateName = NULL;
    pdx.lpCallback = NULL;
    pdx.nPropertyPages = 0;
    pdx.lphPropertyPages = NULL;
    pdx.nStartPage = START_PAGE_GENERAL;
    pdx.dwResultAction = 0;

    //  Invoke the Print property sheet.
    hResult = PrintDlgEx(&pdx);

    DEVMODE * myDevMode     = (DEVMODE *)GlobalLock(pdx.hDevMode);
    DWORD binCount = DeviceCapabilities((CHAR*)myDevMode->dmDeviceName, nullptr, DC_BINS, nullptr, nullptr);
    DWORD binNameCount = DeviceCapabilities((CHAR*)myDevMode->dmDeviceName, nullptr, DC_BINNAMES, nullptr, nullptr);
    wprintf(L"\"binCount\":\"%lu\",", binCount);
    wprintf(L"\"binNameCount\":\"%lu\",", binNameCount);

Solution

DeviceCapabilities() returns a signed int, not an unsigned DWORD.

The unsigned value 4294967295 is hex 0xFFFFFFFF, which is the same numeric value as a signed -1.

Per the DeviceCapabilities() documentation:

Return value

If the function succeeds, the return value depends on the setting of the fwCapability parameter. A return value of zero generally indicates that, while the function completed successfully, there was some type of failure, such as a capability that is not supported. For more details, see the descriptions for the fwCapability values.

If the function returns -1, this may mean either that the capability is not supported or there was a general function failure.

You are not accounting for the possibility of DeviceCapabilities() failing (or PrintDlgEx(), either).

Try this:

HWND hWndOwner = GetForegroundWindow();
if (!hWndOwner){
    hWndOwner = GetDesktopWindow();
}

// Allocate an array of PRINTPAGERANGE structures.
LPPRINTPAGERANGE pPageRanges = (LPPRINTPAGERANGE) GlobalAlloc(GPTR, 10 * sizeof(PRINTPAGERANGE));
if (!pPageRanges){
    // NOTE: GetLastError() returns DWORD, not TCHAR*! So, if you
    // want to translate the error code in a human-readable string,
    // use FormatMessage() instead...
    return wprintf(L"{\"error\": %lu}", GetLastError());
}

//  Initialize the PRINTDLGEX structure.
PRINTDLGEX pdx = {0};
pdx.lStructSize = sizeof(PRINTDLGEX);
pdx.hwndOwner = hWndOwner;
pdx.Flags = PD_RETURNDC | PD_COLLATE;
pdx.nMaxPageRanges = 10;
pdx.lpPageRanges = pPageRanges;
pdx.nMinPage = 1;
pdx.nMaxPage = 1000;
pdx.nCopies = 1;
pdx.nStartPage = START_PAGE_GENERAL;

HRESULT hResult = PrintDlgEx(&pdx);
if (hResult != S_OK)
{
    GlobalFree(reinterpret_cast<HGLOBAL>(pPageRanges));
    return wprintf(L"{\"error\": %d}", hResult);
}

if (pdx.dwResultAction == PD_RESULT_CANCEL)
{
    GlobalFree(reinterpret_cast<HGLOBAL>(pPageRanges));
    return wprintf(L"{\"error\": \"cancelled\"}");
}

DEVMODE *myDevMode = (DEVMODE*) GlobalLock(pdx.hDevMode);

int binCount = DeviceCapabilities(reinterpret_cast<TCHAR*>(myDevMode->dmDeviceName), nullptr, DC_BINS, nullptr, nullptr);
wprintf(L"\"binCount\":%d,", binCount);

int binNameCount = DeviceCapabilities(reinterpret_cast<TCHAR*>(myDevMode->dmDeviceName), 
nullptr, DC_BINNAMES, nullptr, nullptr);
wprintf(L"\"binNameCount\":%d,", binNameCount);

if (binCount == -1)
{
    ...
}

if (binNameCount == -1)
{
    ...
}

...

GlobalUnlock(pdx.hDevMode);
GlobalFree(reinterpret_cast<HGLOBAL>(pPageRanges));

return ...;


Answered By - Remy Lebeau
Answer Checked By - Clifford M. (PHPFixing Volunteer)
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Newer Post Older Post Home

0 Comments:

Post a Comment

Note: Only a member of this blog may post a comment.

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
Comments
Atom
Comments

Copyright © PHPFixing