My StartDoc() fails in Windows 7, runs well in Win

2019-08-07 06:45发布

My code is in 32 bit Visual C++ 2010. It is the usual print-using-default-printer one, which goes like this (this is the exact code minus error handlers):

// Get the length of the printer name.
GetDefaultPrinter(NULL, &size);
lpcPrinterName = new char[size];

// Get the printer name.
if(!GetDefaultPrinter(lpcPrinterName, &size)) {
    // handle error
    return false;
}

// Get a device context for the printer.
hdcPrint = CreateDC(NULL, lpcPrinterName, NULL, NULL);

// get printer parameters
iPrinterDPIX = GetDeviceCaps(hdcPrint, LOGPIXELSX);      // x dpi
iPrinterDPIY = GetDeviceCaps(hdcPrint, LOGPIXELSY);      // y dpi
iPrinterBPP  = GetDeviceCaps(hdcPrint, BITSPIXEL);       // bit per pixel
iPrinterHRes = GetDeviceCaps(hdcPrint, HORZRES);         // x printable area in pixels. 0 maps to 0 here
iPrinterVRes = GetDeviceCaps(hdcPrint, VERTRES);         // y printable area in pixels. 0 maps to 0 here

if (!OpenPrinter(lpcPrinterName, &printerHandle, NULL)) {
    // handle error
    return false;
}

// initialize docInfo
ZeroMemory(&docInfo, sizeof(docInfo));
docInfo.cbSize = sizeof(docInfo);
docInfo.lpszDocName = lpcstrDocName;

// ---> this is where it fails when run standalone on Windows 7, return value == -1
iPrintJobID = StartDoc(hdcPrint, &docInfo); // this starts a print job

if (iPrintJobID <= 0) {
    // handle error
    return false;
}

if (StartPage(hdcPrint) <= 0) { // this starts a new page
    // handle error
    return false;
}

{ // enclose in an inner scope to get graphics destroyed before deleting dc
    Gdiplus::Graphics graphics(hdcPrint, printerHandle);
    if (graphics.DrawImage(&bmp,x,y) != Ok)
        // handle error
        return false;
    }
}

if (EndPage(hdcPrint) <= 0) { // ends the page (eject paper)
    // handle error
    return false;
}

if (EndDoc(hdcPrint) <= 0) {  // end the print job (actually starts printing)
    // handle error
    return false;
}

ClosePrinter(printerHandle);
DeleteDC(hdcPrint);
delete[] lpcPrinterName;

I have done some trial and errors with these results:

  • The code runs well in Windows XP (administrator and normal user)

  • When run under Windows 7, StartDoc fails (returns -1) with GetLastError() returning 'Access is denied' error, under any user (Administrator, normal user, elevated user using run-as administrator, all fails)

  • The code runs well under Windows 7 only when:

    • run from Visual C++, either started with debugging or without debugging
    • and only when Visual C++ is run elevated (run-as administrator) or logged in as Administrator

In order to see the difference between those conditions seen from the programs's security context, I took a peek into both user and group SID's, compared them and found out that although they're run under the same user and group SID, the StartDoc still failed.

I've used Canon iP3680 in practice and bullZip PDF printer for easier code debugging which react exactly the same.

Can anyone help me find a way to get StartDoc to succeed under Windows 7? Does anyone ever experience the same or related problems?

Added:

I've also tried taking ownership of Spool/PRINTERS folder with no luck. It seems like the problem revolves around Windows 7's security. Some other printing cases I've found here manage to solve that way that's why I tried that

I forgot to add that the problem's gone when I set the printer to 'Print directly to the printer' instead of using printer spool. This hints that the problem is with the spooler.

Solved:

I was using a method of preventing my application from being killed by implementing an empty DACL, something like this:

dwErr = SetSecurityInfo(hProcess, SE_KERNEL_OBJECT, 
        DACL_SECURITY_INFORMATION, NULL/*ownerpsid*/, NULL, pEmptyDacl, NULL);

Removing that snippet clears all the problems.

1条回答
成全新的幸福
2楼-- · 2019-08-07 07:23

We had the same problem a few months ago in only one of our customers (over more than 15.000) using Windows 10.

After a long investigation, we discover the problem was the printer HDC obtained previously using general parameters. It worked well for all operations until we need to call StartDoc().

And the solution was to do the current StartDoc() call using the HDC we already had and, in case of error, create a new printer DC specifying the concrete pDeviceMode parameter:

newPrinterDC = CreateDC(NULL, PrinterName, NULL, pDeviceMode); 

After that, we can do the StartDoc() call again using the newPrinterDC.

This solved the problem and ran correctly in the 100% of the cases.

I hope it help you.

查看更多
登录 后发表回答