2016-08-11 1 views
1

私はC++ Builder XE7 VCLを使用しています。TPrinter(XE7)が突然今日問題を抱えているのはなぜですか?

2016年8月11日午後2:00午後2:00 UTCでは、印刷に関する問題についてユーザーから複数の苦情が寄せられました。これらの印刷モジュールのほとんどは長年にわたって安定しており、過去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ファイルが破損し、ファイルハンドルが残っているように見えます。私はボタンを3回目をクリックした場合

、私は印刷を取得していないと、次のエラーメッセージが表示されます。

Printer is not currently printing. 

私自身のテストはPDFプリンタドライバを使用して行われたが、苦情は、私は、ユーザーから受け付けております

私の実際のプロジェクトでは、try/catch例外処理があるため、実際の結果はわずかに異なりますが、この結果と実質的に似ています。結果は、エラーメッセージに関してあまり意味をなさない不安定性および/またはメモリリークの特徴を示す。

私は、Embarcadero DLLに絡み合っているMicrosoft Windowsアップデートがいくつかあったと思われますが、これまでこれを確認することはできませんでした。

誰もが同様の問題を抱えていますか?

+0

はSTARTDOCが失敗したときに設定されていません。私は 'BeginDoc'の直後に 'Printing'をテストし、印刷しない場合はGetLastErrorを呼び出すことをお勧めします。 –

+0

今日は同じエラーの報告があります。私たちの技術サポート担当者は、昨晩から顧客の1人にアップデートをロールバックさせ、印刷が開始されました。ちょうどそれを自分自身で調べ始める。 –

+0

Windows 7 64には今週のアップデートがインストールされています。(Snagit 11プリンタを選択するために1行追加する)コードを使った簡単なテストはDelphi 10.1 Berlinでもうまくいきます。私はこれを入力すると、キャプチャされたページを見ています。 –

答えて

4

TPrintDialogまたはTPrinterSetupDialogを使用した理由がエラーを修正するために、「作品は、」これまでTPrinter.BeginDoc()を引き起こし、あります新しいハンドルを作成します。 TPrinterは、次の場合にプリンタハンドルを解放します。

  • 破棄されています。
  • NumCopies,OrientationまたはPrinterIndexプロパティが設定されています。
  • そのSetPrinter()メソッドが呼び出されます(内部ではPrinterIndexプロパティセッターとSetToDefaultPrinter()メソッド、TPrintDialogTPrinterSetupDialog)。

TPrinter.BeginDoc()を複数回呼び出すと、同じプリンタハンドルをそのまま使用し続けることになります。そして、明らかに、最近のマイクロソフトのセキュリティアップデートに関する何かが、再利用を処理するのに影響しています。

だから、BeginDoc()に呼び出しの間に(マイクロソフトのアップデートをアンインストールせずに)、要するに、あなたは解放し、そのプリンタのハンドルを再作成し、問題が離れて行く必要があるためにTPrinterを起こし何かを行う必要があります。この問題を解決するには、少なくともEmbarcaderoがTPrinterへのパッチをリリースするまで。おそらく、TPrinter.EndDoc()またはTPrinter.Refresh()を更新して、現在のプリンタハンドルを解放することができました(現在はそうしていません)。

従って、次の回避策は、ユーザーインターフェースに変更を必要とせずに、印刷の問題を解決:VCLコードFPrintingをみる

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

印刷に関連するEmbarcadero DLLはありません。 TPrinterはWin32 API GDIベースの印刷機能を直接呼び出します。

次のいずれかの操作をTPrinter上で実行されたときにそのPrintingプロパティがfalseの場合、エラーが発生する "プリンタが現在印刷されていません":

  • TPrinter::NewPage()
  • TPrinter::EndDoc()
  • TPrinter::Abort()
  • a TPrinter::Canvasサブプロパティが変更されています。
  • TPrinter::Canvasが描かれています。

表示されたテストコードでこれらの操作の半分を実行していますが、実際にエラーを投げているコード行は指定していません。

Printingプロパティは単にのみfalseに設定されTPrinter::FPrintingデータメンバの現在の値を返す:

  • TPrinterオブジェクトが最初に作成されている(Printer()関数のために再利用されるシングルトンオブジェクトを返します実行可能ファイルの存続期間)。
  • Win32 API StartDoc()の機能がTPrinter::BeginDoc()FPrintingがtrueに設定され、StartDoc()が呼び出される前に)に失敗します。
  • TPrinter::EndDoc()Printingがtrueの場合に呼び出されます。だから、

、あなたが示されたテストコード与えられ、二つの可能性があります。

  • StartDoc()が失敗し、その状態をチェックされていませんが。 BeginDoc()はエラー(VCLのバグ?!)をスローしませんが、単に正常に終了しますが、Printingはfalseになります。そのためのチェックを追加します:あなたが何かを印刷する過程にある一方で

    Prntr->BeginDoc(); 
    if (Prntr->Printing) { // <-- here 
        Prntr->Canvas->Font->Size = 10; 
        Prntr->Canvas->TextOut(300,1050,"* * * Printing Test * * *"); 
        Prntr->EndDoc(); 
    } 
    
  • Printingプロパティが早まっfalseに設定きています。

    • ランダムメモリが破損している、とTPrinterは被害者であることを起こる:場合示されているコードで発生する可能性がのみの方法があります。
    • 複数のスレッドが同時に同じオブジェクトを操作しています。TPrinterオブジェクト。 TPrinterはスレッドセーフではありません。

あなたは、開発システムで問題を再現することができますので、私はあなたがデバッガでアプリケーションを実行し、プロジェクトオプションでデバッグDCUをを有効にし、TPrinter::FPrintingデータメンバにデータブレークポイントを入れてお勧めします。 FPrintingが値を変更すると、ブレークポイントがヒットし、コールスタックを調べて、どのコードがその変更を行っているかを正確に確認することができます。

この情報に基づいて、私は手足に出て、エラーの原因がStartDoc()ではないと思われます。残念なことに、StartDoc()を返すと文書化されていません。理由はになります。確かにそれにはGetLastError()を使用できません(ほとんどのGDIエラーはGetLastError()によって報告されません)。 Win32 API Escape()またはExtEscape()関数を使用して、プリンタドライバ自体からエラーコードを取得することができます(HDCをクエリとして使用)。しかし、それがうまくいかない場合、Windowsがイベントログにエラーメッセージを報告していない限り、失敗の理由を特定することはできません。

StartDoc()が本当に失敗しているのは、Win32 APIの障害であり、VCLの失敗ではないためです。ほとんどの場合、プリンタドライバ自体が内部的に故障している可能性があります(特に、PDFプリンタドライバがPDFファイルに開いているファイルハンドルを残している場合)。あるいは、Windowsがドライバと正しく通信できません。いずれにしても、VCLの外にあります。これは、アプリに何も変更を加えずにエラーが発生したことと一貫しています。Windows Updateが原因で、プリンタドライバが改ざんされた可能性があります。

+0

StartDocは、関数が失敗したときにGetLastErrorを使用することが以前に文書化されていました。あなたはXE2 api docsでそれを見ることができます。ディスカッション[こちら](https://social.msdn.microsoft.com/Forums/en-US/07323a9e-355b-463f-a167-1acaa70eb7e6/the-return-value-startdoc-function-is-not-compatible-with -last-error-code-by-getlasterror-function?forum = windowsgeneraldevelopmentissues)は、以前のドキュメントが間違っている可能性があることを示唆しています... –

+0

興味深い、MSのサポート記事[here](https://support.microsoft.com/en-us/kb/983224)は、現在のドキュメントが間違っている可能性があることを示唆しています。 –

+0

そのスレッドでは、MSのRob Caplan氏は次のように述べています。 "*現在のOSでStartDocがエラーパスに最後のエラーを設定していないことを確認しました。*"そうです、ドキュメントは間違っていて修正されました。そのため、現在のMSDNのドキュメントでは、 'StartDoc()'エラーコードを 'GetLastError()'から取り出すことができなくなりました。 –

1

今日もここで起き始めました。私のWindows 10のセットアップでは、これはTFormのPrint()を3回呼び出した後に起こります。私は、Microsoft Print to PDFとMicrosoft XPS Document Writerの両方を試してみましたが、両方とも同じエラーが発生しました。

私はいくつかの簡単なデバッグを行なったし、それがSTARTDOCへの呼び出し(だことがわかった)私は本当にこれは再作成することです引き起こしているかを把握できるようになるまで値= 0 <

一時修正を返しますPrinterオブジェクトを使用しているものを呼び出した後に、

Vcl.Printers.SetPrinter(TPrinter.Create).Free; 

を呼び出して、プリンタのプリンタオブジェクトを呼び出します。それをすることはお勧めできないかもしれませんが、今は私の問題を解決しました。

(EndDocメソッドを呼び出すときに何かが正しく解放されていないように見える)

+0

あるいは、 'Vcl.Printers.Printer()'関数を使ってシングルトン 'TPrinter'オブジェクトを使うのを避け、代わりに何かを印刷したいときは一時的な' TPrinter'オブジェクトを作成してください。 'TPrinter * Prntr = new TPrinter; try {...} __finally {delete Prntr; } 'またはそれより単純です:' #include ... std :: auto_ptr Prntr(新しいTPrinter); ... ' –

0

のWindows 10 64ビットにXE7に建てられた32ビットのDelphiアプリケーションを実行しているとき、私は、同じ時間多かれ少なかれで同じ奇妙な行動を経験し始めましたシステム。

Windows 10用の最新のセキュリティ更新プログラム(KB3176493)をアンインストールすると、これらのアプリケーションからの印刷が正常に機能します。この更新プログラムをアンインストールするのではなく、驚くべき副作用は、ファイルの関連付けということのようです

- 特定のファイルタイプを処理するための既定のプログラムです - マイクロソフト、Windowsのデフォルト値に戻っされている...

0

問題のコードの以下のばらつきが問題を解決しますが、フォームに追加TPrinterSetupDialogコンポーネントが必要です:プログラムの使用方法について

void __fastcall TForm1::PrintButtonClick(TObject *Sender) 
{ 
    // Test Print: 
    TPrinter *Prntr = Printer(); 
    Prntr->Title = "Test_"; 
    PrinterSetupDialog1->Execute(); 
    Prntr->BeginDoc(); 
    if (Prntr->Printing) { 
     Prntr->Canvas->Font->Size = 10; 
     Prntr->Canvas->TextOut(300,1050,"* * * Printing Test * * *"); 
     Prntr->EndDoc(); 
    } 
} 

を、ユーザーが前に、プリンタの設定ダイアログが表示されます印刷に進む。 「なぜ」として、この時点で私の最高の推測では、Microsoft Windowsのセキュリティ更新(KB3177725)が8月に実施された後、単独で使用TPrinterは、Windowsから必要なすべてのリソースにアクセスするために必要なすべての権限を持っていないこと

ですBeginDoc()を呼び出す前に何らかの形でTPrinterSetupDialog(またはTPrintDialog)を呼び出すと、TPrinterが正常に実行されるために必要な条件が設定されます。

+0

'TPrinterSetupDialog'は、Win32 API [' PrintDlg() '](https://msdn.microsoft.com/en-us/library/windows/desktop/ms646940.aspx)を呼び出し、その結果を渡しますプリンタ情報を 'TPrinter.SetPrinter()'メソッドに渡します。 –

1

これは実際にはMicrosoft problemと思われ、このバグの修正を修正する必要があります。詳しくは、company siteをご覧ください。

プリンタセットアップダイアログを使用するのは、実際の解決策ではなく、このMicrosoftのバグの回避策に過ぎません。確認後、プリンタの設定ダイアログでは常にプリンタの新しいハンドルが作成されます。次の1つまたは2つの印刷ジョブが成功します。

このパッチは、EmbracaderoからではなくMicrosoftからのものでなければなりません。何千ものプログラムが影響を受けており、MSの更新プログラムにバグがある場合は、そのすべてで回避策を実装するのに膨大な時間と費用がかかります。彼らはそれが1つを持っている場合、プリンタに現在のハンドルを解放する(Vcl.Printers.Printer()関数によって返された)シングルトンTPrinterオブジェクトを強制するため

関連する問題