我正在使用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纠缠在一起,但到目前为止我还无法验证这一点。
还有其他人有类似的问题吗?
最佳答案
使用TPrintDialog
或TPrinterSetupDialog
“工作”来修复该错误的原因是因为它们迫使单例TPrinter
对象(由Vcl.Printers.Printer()
函数返回)将当前的句柄释放给打印机(如果有),从而导致TPrinter.BeginDoc()
创建一个新的句柄。 TPrinter
在以下情况下释放其打印机句柄:
它正在被摧毁。
它的NumCopies
,Orientation
或PrinterIndex
属性已设置。
其SetPrinter()
方法被调用(在内部由PrinterIndex
属性设置器和SetToDefaultPrinter()
方法,以及由TPrintDialog
和TPrinterSetupDialog
调用)。
无需这样做,多次调用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();
}
}