プリンターのお話し。
直接会ったことのある方々にはお話ししているように、ここ数年、Windows から離れて Linux にかかりっきりです。プリンターに関わっているのですが、その為に、WinSpool.drv 等のプリンターに関わる API をいじらなくてはならないことも。基本、Windows じゃないのですけどね。でも、もう、C++、というか、Visual C++(Managed C++, C++/CLR 含む)は、いじりたくないです。何とか、C# でやっています。
で、プリンターの試験を行っていると、特定の条件で特定のファイルを何度も印刷することがあります。一般社団法人電子情報技術産業協会という協会があるのですが、そこから印刷における標準的なドキュメントが出ているので、それを印刷します。アプリケーションから印刷するとき、印刷ダイアログに「ファイルに出力する」というチェックボックスがあって、これをチェックしたときに出来る .prn ファイル。こいつは、スプーラーが貯め込んでいるファイルとほぼ同じで、ドライバーがプリンター用に作ったデータそのものです(他にスプーラー用の情報ファイルが出来るので、「ほぼ同じ」と表現した)。この .prn ファイルを作っておいて、ポイっと投げてやれば、いちいちアプリケーションを起動したり、印刷設定をし直したり、Microsoft Word だったら終了時に問い合わせがなくなっていいよね!!というのが狙い。ああ、ファイルは、Microsoft Excel、Microsoft PowerPoint、Microsoft Word、Adobe Reader 用のものがあります。それぞれ、別のアプリケーションで開かなきゃいけないのも、1つで事足りるよね!
やり方は、Windows GDI の、Printing and Print Spooler で定義されている、OpenPrinter 関数でプリンターを開き、StartDocPrinter 関数でこれからデータを投げることを通知して、WritePrinter 関数で書き込む、全部書き終わったら ClosePrinter 関数で閉じる、と。アプリケーション フォームには、ファイルを指定する方法と、プリンターを指定する方法を用意しておけばよい、と。そんなに難しいものではありませんね。チャチャっと作っちゃいましょう。
で、作ったのですが、欲が出てきた。プリンターとしての機能の試験をしているので、印刷指示をした後、プリンターが動かないと、プリンター(のファームウェア)が悪いのか、印刷指示が出来ていないのか、わからない(複数のファイルを交互に投げることがあるので、印刷ファイルを列挙するために ListView を使うと、クリックするところによって選択状態が外れてしまい、「印刷指示が出来ていない」という状態が出来てしまった)。プリンター フォルダーを表示していればわかりますが、そうでなくてもたくさん開いているウィンドウを、少なくしたい。ならば、プリンター一覧に、プリンター フォルダーで表示しているのと同じ内容を表示してやれば良いではないか。
ということで、情報を表示させます。EnumPrinters 関数は、PRINTER_INFO という構造体の配列を作って呼び出すと、そこへデータを放り込んでくれます。PRINTER_INFO 構造体は、1~9の9種類あるのですが、いくつかは廃止されています。EnumPrinters 関数に対して有効なのは、1, 2, 4, 5 の4種類です。プリンターの状態は、PRINTER_INFO_2 構造体(英語)に、Status として定義されています。こいつを拾い出しましょう。
という修正を施して、実行しました。Statu に0が返ってきています。USB で接続したプリンターの、USB ケーブルを抜きました。プリンター フォルダーでは、「オフライン」表示に変わりました。PRINTER_INFO_2 構造体は…0のままです。なんで?
Bing って回ったところ、どうも、ドライバーが正しい情報を返していないらしいです。なんてこったい!!そうすると、ちゃんと「オフライン」と表示されるプリンター フォルダーは、どこから情報を得ているんだろう?
ついでに書いておくと、WMI で、Win32_Printer にアクセスすると、同じように情報が取れます。HTML と組み合わせて Windows Sidebar Gadget にすれば、コンパクトにプリンターの状態を監視することも可能です。とはいえ、ネットワーク プリンターの場合、他の人が印刷しているのはわからないのですが。あ、それもドライバーがプリンターと通信していればわかるのか。Web Service on Devices に対応しているなら、デバイスに直接尋ねに行くから、必ず正しいステータスがわかるのね。
おっともうひとつ。上記方法では、EnumPrinters を実行したときのプリンターの状態しかわかりません。それで情報が取れるのを確認してから、FindFirstPrinterChangeNotification 関数などを使おうとしていたのですが、ドライバーが情報を返していないのなら、仕方ないですよね。
なんて原稿を用意した後、メールの整理をしていたら、2008年9月に、こんな問い合わせをしていた。なんてこったい、FindFirstPrinterChangeNotification 関数を使えだって!
[お問い合わせの概要]
メーカー提供のポートモニタに対して WMI の Win32_Printer:PrinterStatus を問い合わせると、印刷後 "Unknown"となり、標準の TCP/IP ポートモニタと異なるが、この原因を解明する方法、対処方法を知りたい。
[回答]
ポート モニタを含めて、スプーラ サービス内のステータスに依存して、WMI の Win32_Printer:PrinterStatus の値が異なることがございます。また、このようなスプーラ サービスのステータスは、拡張コンポーネントに依存性が高く、本件では、ポート モニタやランゲージ モニタに依存している可能性が高いと思われます。
具体的な例といたしまして、これらのプリント モニタは、少なくとも EndDocPort 関数内にて、SetJob 関数に以下のコマンドを指定する必要がございます。
ポート モニタ: JOB_CONTROL_SENT_TO_PRINTER
ランゲージ モニタ: JOB_CONTROL_LAST_PAGE_EJECTED
また、このコマンドの指定のタイミングや順番などに依存して、標準の TCP/IP ポートモニタとは異なる可能性がございます。併せまして、標準の TCP/IP ポートモニタでは、SNMP でプリンタのステータスを監視し、その状態をスプーラ サービスのステータスに反映するため、ステータスの差異が発生する可能性がございます。
続きまして、本件では、印刷が開始できる状態なのかどうかを判断するために、Win32_Printer:PrinterStatus を確認されているとお伺いいたしました。このステータスは、一般的なステータスに分類分けをされておりますが、細かなステータスをチェックすることができません。印刷が開始できる状態なのかどうかを判断は、プリンタに依存するステータスもあるため、プリンタの細かなステータスで、判断されたほうがよいかと存じ上げます。
そのような細かなステータスを取得する観点より、Win32_Printer:PrinterState は、スプーラ サービスから取得した情報 (PRINTER_INFO_6 構造体) の値となっておりますため、より詳細な状態をチェックすることが可能でございます。ただ、このプロパティは、obsolete であり将来的には、未サポートとなる可能性がございますことをご注意ください。
また、Win32 API とはなりますが、以下にご案内申し上げますサンプル コードにて、コントロールパネルの [プリンタと FAX] にて表示されるプリンタ キューの情報と同等な方法で、同等な情報が取得可能でございます。
Microsoft サポートオンライン「PrintMon.exe Demonstrates the Win32 Spooler API」
http://support.microsoft.com/kb/196805/en-us(英語)
※ Windows 9x/Me では、ポーリング形式
※ Windows 2000 以降では、イベント形式 (FindFirstPrinterChangeNotification/FindNextPrinterChangeNotification)
以上のとおりお伝えいたします。
投稿日時 : 2012年1月25日 22:35