Home > Back-end >  Why might DeviceCapabilities() return 4294967295 for DC_BINS?
Why might DeviceCapabilities() return 4294967295 for DC_BINS?

Time:11-18

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

CodePudding user response:

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 ...;
  • Related