我正在使用C ++ Builder XE7 VCL。

在世界标准时间2016年8月11日下午2:00左右,我开始收到用户群关于打印问题的多次投诉。这些打印模块中的大多数已证明稳定多年,并且在过去的24小时内我的项目没有更新。我能够在开发/测试环境中重现类似的问题。

在不涉及项目细节的情况下,让我介绍一个非常简单的失败的打印程序:

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


第一次尝试打印时,一切都会按预期完成。如果我第二次单击该按钮,TPrinter会生成一个小的PDF,但是PDF文件实际上已损坏,并且似乎有文件句柄被卡住了。

如果我第三次单击该按钮,则无法打印,并出现以下错误消息:

Printer is not currently printing.


我自己的测试是使用PDF打印机驱动程序完成的,但是我收到的用户投诉包括各种本地打印机,网络打印机,PDF打印机等。

在我的实际项目中,我具有try/catch异常处理,因此实际结果略有不同,但与该结果基本相似。结果显示出不稳定和/或内存泄漏的特征,而没有太多错误消息。

我怀疑可能已经有一些Microsoft Windows更新与Embarcadero DLL纠缠在一起,但到目前为止我还无法验证这一点。

还有其他人有类似的问题吗?

最佳答案

使用TPrintDialogTPrinterSetupDialog“工作”来修复该错误的原因是因为它们迫使单例TPrinter对象(由Vcl.Printers.Printer()函数返回)将当前的句柄释放给打印机(如果有),从而导致TPrinter.BeginDoc()创建一个新的句柄。 TPrinter在以下情况下释放其打印机句柄:


它正在被摧毁。
它的NumCopiesOrientationPrinterIndex属性已设置。
SetPrinter()方法被调用(在内部由PrinterIndex属性设置器和SetToDefaultPrinter()方法,以及由TPrintDialogTPrinterSetupDialog调用)。


无需这样做,多次调用TPrinter.BeginDoc()只会继续重复使用相同的打印机句柄。显然,有关最近的Microsoft安全更新的某些问题现在已经影响到处理重用。

因此,简而言之,在两次调用BeginDoc()的过程中(无需卸载Microsoft更新),您需要做一些导致TPrinter释放并重新创建其打印机句柄的操作,然后该问题将消失。至少直到Embarcadero可以发布TPrinter补丁来解决此问题为止。也许他们可以更新TPrinter.EndDoc()TPrinter.Refresh()来释放当前的打印机手柄(当前不这样做)。

因此,以下解决方法可解决打印问题,而无需对用户界面进行任何更改:

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

10-02 01:06