I am using C++ Builder XE7 VCL.
Around August 11 2016 2:00pm UTC, I started receiving multiple complaints from my user base about printing problems. Most of these printing modules have proven stable for many years, and there were no updates to my project within the past 24 hours. I was able to reproduce similar problems on my development/test environment.
Without going into many details of my project, let me present a very simple printing program that is failing:
void __fastcall TForm1::PrintButtonClick(TObject *Sender)
{
// Test Print:
TPrinter *Prntr = Printer();
Prntr->Title = "Test_";
Prntr->BeginDoc();
Prntr->Canvas->Font->Size = 10;
Prntr->Canvas->TextOut(300,1050,"* * * Printing Test * * *");
if (Prntr->Printing) {
Prntr->EndDoc();
}
}
On the first attempt to print, everything works perfectly as expected. If I click the button a second time, TPrinter
produces a small PDF, but the PDF file is actually corrupted and appears to have a file handle stuck to it.
If I click the button a third time, I get no printing and the following error message appears:
Printer is not currently printing.
My own test was done using a PDF printer driver, but the complaints I am receiving from users include a variety of local printers, network printers, PDF printers, etc.
In my actual project, I have try/catch
exception handling, so the actual results are slightly different, but substantially similar to this result. The results show the hallmarks of instability and/or memory leaks without much in terms of error messages.
I suspect there may have been some Microsoft Windows updates that are tangling with Embarcadero DLLs, but I have not been able to verify this so far.
Is anyone else having similar problems?
The reason using a TPrintDialog
or TPrinterSetupDialog
"works" to fix the error is because they force the singleton TPrinter
object (returned by the Vcl.Printers.Printer()
function) to release its current handle to a printer if it has one, thus causing TPrinter.BeginDoc()
to create a new handle. TPrinter
releases its printer handle when:
NumCopies
, Orientation
, or PrinterIndex
property is set.SetPrinter()
method is called (internally by the PrinterIndex
property setter and SetToDefaultPrinter()
method, and by TPrintDialog
and TPrinterSetupDialog
).Without doing that, calling TPrinter.BeginDoc()
multiple times will just keep re-using the same printer handle. And apparently something about the recent Microsoft security update has now affected that handle reuse.
So, in short, (without uninstalling the Microsoft update) in between calls to BeginDoc()
you need to do something that causes TPrinter
to release and recreate its printer handle, and then the problem should go away. At least until Embarcadero can release a patch to TPrinter
to address this issue. Maybe they could update TPrinter.EndDoc()
or TPrinter.Refresh()
to release the current printer handle (they currently do not).
Therefore, the following workaround resolves the printing issue without requiring any changes to the user interface:
void __fastcall TForm1::PrintButtonClick(TObject *Sender)
{
// Test Print:
TPrinter *Prntr = Printer();
Prntr->Title = "Test_";
Prntr->Copies = 1; // Here is the workaround
Prntr->BeginDoc();
if (Prntr->Printing) {
Prntr->Canvas->Font->Size = 10;
Prntr->Canvas->TextOut(300,1050,"* * * Printing Test * * *");
Prntr->EndDoc();
}
}