Win32APIの印刷プログラムの躓き

概要

Win32APIを使った印刷プログラムでキャノン製の数種類のプリンタだけで印刷できないことがありました。 エラーが出ることもなく、正常に印刷できたように振る舞うのですが何も出力されません。
Win32APIを使っての印刷は大まか次の手順です。

  • CreateDC() か PrintDlg()でデバイスコンテキストを作成
  • StartDoc() で文書の開始
  • StartPage() でページの開始
  • EndPage() でページの終わり
  • EndDoc() で文書の終わり
  • DeleteDC() でデバイスコンテキストを解放

躓いた原因は StartDOC() にありました。


プログラム

原因調査のためのプログラムをC++Builderで作成しました。
フォームにボタンとチェックボックスを配置し、ボタンをクリックするとフォームをPaintTo()で印刷します。
ボタンをクリックした時の処理を次に示します。

 1 void __fastcall TForm1::Button1Click(TObject *Sender)
2 {
3 PRINTDLG pd;
4 memset(&pd, 0, sizeof(pd));
5 pd.lStructSize = sizeof(pd);
6 pd.Flags = PD_RETURNDC;
7 pd.hwndOwner = Handle;
8 if (!PrintDlg(&pd))
9 return;
10
11 int ppix = GetDeviceCaps(pd.hDC, LOGPIXELSX);
12 int ppiy = GetDeviceCaps(pd.hDC, LOGPIXELSY);
13 SetMapMode(pd.hDC, MM_ANISOTROPIC);
14 // 画面を100DPIと仮定し、論理座標と物理座標をマッピング
15 SetWindowExtEx(pd.hDC, 100, 100, NULL);
16 SetViewportExtEx(pd.hDC, ppix, ppiy, NULL);
17 // 原点を縦、横1インチに移動
18 SetViewportOrgEx(pd.hDC, ppix, ppiy, NULL);
19
20 DOCINFO aDocInfo;
21 memset(&aDocInfo, 0, sizeof(aDocInfo));
22 aDocInfo.cbSize = sizeof(aDocInfo);
23
24 // チェックがあれば lpszDocName をセット
25 if (CheckBox1->Checked)
26 aDocInfo.lpszDocName = Edit1->Text.c_str();
27
28 if (StartDoc(pd.hDC, &aDocInfo) > 0) {
29 if (StartPage(pd.hDC) > 0) {
30 PaintTo(pd.hDC, 0, 0);
31 EndPage(pd.hDC);
32 }
33 EndDoc(pd.hDC);
34 }
35
36 if (pd.hDC != NULL)
37 DeleteDC(pd.hDC);
38 }

28行目のStartDoc()の引数 aDocInfo は21行目でクリアし、チェックボックスがチェックしてあれば26行目でlpszDocNameにEdit1の文字列のポインターをセットしてます。
26行目を通らないとlpszDocNameは値がNULLでStartDoc()に渡されることになります。
キャノン製の数種類のプリンタで印刷ができないのはこのケースです。
なかなか原因が掴めず苦労したのは、他の多くのプリンタでlpszDocNameがNULLでも印刷できたからでした。
同じキャノン製の同じ機種のプリンタでもプリンタドライバによって印刷可と不可になる場合もありました。
lpszDocNameがNULLならば印刷ができないという仕様でもいいのですが、ドライバ間の互換性を維持してもらいたいものです。

ソースファイル

原因調査のために作成したプログラムのソースを以下のファイルにまとめて置いておきます。

LipsLx.zip


コンパイルにはC++Builder 2007が必要です。


著作権・使用条件

本ソフトウェアを使用して発生したいかなる損害に対しても弊社は責任を負いません。利用者御自身の責任でお使いください。
ご意見、ご要望、バグ、等ございましたらメールでご連絡ください。


2012年4月10日