ひとこと

― 今日のひとこと ―
年に1回くらい、EVE Onlineやりたくなるのよね。
(2025.12.24)
ラベル WMI の投稿を表示しています。 すべての投稿を表示
ラベル WMI の投稿を表示しています。 すべての投稿を表示

2015年10月28日水曜日

C# Windowsプログラミングおぼえがき No.2 - 印刷ジョブ -

バックグラウンドプログラムで印刷ジョブから印刷情報を取得して、
それをActionで出力するというプログラムをC#で作った。
結構ちゃんと作ってたら長くなったので近いうちに要点だけまとめて載せよう

悩んだところがいくつかある。
・印刷ジョブをどうやって検知する?スレッド終了イベントの待機は?
→WaitForMultipleObjects()とFindNextPrinterChangeNotification()で解決。
・印刷情報はどうやって取得する?
→PRINTER_NOTIFY_INFO_DATA構造体とか定義してメモリから読むのとWMIのWin32_PrintJob

上記2つをWhile(!stopFlg)といった感じで
終了フラグがONになるまで印刷ジョブか終了イベントを検知する度に実行。

これでだいたい「印刷される度に値を自動出力」は可能になる。
しかしここで最大の難所が!!

・1度の印刷の終了はどうやって判定する?

Whileループの中で、印刷ジョブを検知して情報を取得したら、
次のWaitForMultipleObjects()が実行されるまでに
終了判定(印刷完了したから値を出力するよって処理)を行う必要がある。
じゃないと値を出力しないままプログラムがイベント待機に入ってしまうから。

なので、どの段階で印刷情報をすべて取得できたかは
印刷ページ数を正しく取得できたか否かで判定しよう、と考えた。

でも、1度の印刷で印刷情報の通知は何回も受信するわけだけど
それぞれ「ページ数」が違うんだよね。通知が届くごとに変わる。
基本的には増えていく。でもたまに0になる。
ページ数は正確に取得したいから避けては通れない。なんか法則があるのか?

紙印刷しまくって検証するわけにもいかないから、
仮想プリンタでPDF出力してジョブの中身を眺めてみた。
するとどうだ。あるパターンを見出すに至った。

「ページ数が過去最高かつそれが2回連続している場合、それが実際のページ数と一致する」
これだ!印刷ページダブルマウンテンの法則と名付けよう。

で、両面印刷だったり片面複数ページ印刷だったりするならそれらも考慮し
取得したページ数を再計算してやれば実際の印刷用紙枚数になるわけだ。
ちなみに片面複数ページ印刷の情報はPrintTicketクラスを使って取得した。
PrintTicketは最低サポートOSがVistaSP2だのServer2008R2だのとされているが
XPやServer2003でも使えてた。WMIのクラスもXPで使えるものは多い。


そんなこんなで実装完了。と思いきや・・・
XPS Docment Writerの印刷情報がたまに取れないorz

どうやらXPSには印刷ページダブル云々の法則が当てはまらないようだ。。

仕方ないので改めてネットで情報収集していたところ
JOB_STATUSとかいういかにもな値が存在することが判明。
状態の種類はたくさんあるんだけど、DELETEDになった時点で出力した値が正確だった。
これプリンタドライバの仕様によっては変わるのか?

まぁXPSでもPDFでもちゃんと値を取得できるようになったからいいや。


印刷情報を取得するコードは意外とサンプルが少ない。
苦労して見つけてもすごく単純で、物足りないものが多かった。
それも英語ばっかりだしね・・・

ああ疲れた





最後に小技をひとつ
仮想プリンタで両面印刷を実現する荒業について。

基本的に仮想プリンタは両面印刷に対応していない。
ファイルに裏面などないから当然といえば当然だな。
なので仮想プリンタの印刷設定には両面印刷の項目すら存在しない。

ではどうやって両面印刷のジョブを仮想プリンタに送るのか?
仮想マシンで物理マシンの仮想プリンタを使うのだ!

仮想だの物理だのややこしいが、整理するととっても単純である。

たとえば、VMware上で印刷を実行するとする。
プリンタには物理マシンにインストールされた仮想プリンタを指定する。
そしてその仮想プリンタの印刷設定画面を開くと・・・
両面印刷の項目があるではないか!!!
そいつを設定してやると、実際にジョブで両面印刷が指示される。

偶然見つけた方法だけどデバッグには有効だよね。
でもこれ、片面複数ページ印刷の項目は消えるんだけどね。

[追記 2018/02/20]
プログラムのアップロードをすっかり忘れていました。
改めてコードを若干整理してMITライセンスで公開中。
PrintMonitor - https://bitbucket.org/marolabo/public

2015年7月24日金曜日

C# Windowsプログラミングおぼえがき

■インストール済みソフトウェア一覧取得
PCにインストールしたソフトの一覧がほしいとき。
WMIでは取得できないものも多々あると思われるので、レジストリから情報を得る。

1.Uninstallからソフトウェア名を取得
uninstallのサブキーのうちDisplayNameが記載されたものを取得する。
これがコントロールパネルの「プログラムのアンインストール」で表示されるものだと思われる。
ちなみにUninstallではインストール日時やバージョンなども取得できる。

2.App Pathsから実行ファイル名を取得
uninstallからは実行ファイル名を取得できないので、
uninstallでInstallLocationを取得した上でApp Pathsのサブキー順次取得し、
そのPathの値がInstallLocationで取得したものと一致した場合に既定値を実行ファイルパスとして取得する。

64bitOSと32bitOSではレジストリパスが若干異なるので注意。
64bitの場合はSoftwareの直下にWow6432nodeというキーが入る。


■インストール済みセキュリティソフト一覧取得
WMIのAntiVirusProductを使う。XPより後のOSではSecurityCenter2を指定すること。
取得できるproductStateは、その製品にどんな機能があるかを表す3Bytesの値。
値ごとの具体的な意味はこちら
別に製品の設定で機能のON/OFFを変えてもそれが反映されるわけではないので注意。
製品の実際の設定値を知るには製品ごとのレジストリを読むしかない。


■Windows Defenderについて
ウィルス定義ファイルの更新タイミングはざっと見た感じだと次の3パターンがある。

①WindowsUpdate
②スキャン直前
③定期チェック

定期チェックはWindows8以降だけ対応しているのかも。Windows7では設定できなさそうだった。
スキャン直前に更新チェックするかどうかは設定次第なんだけど、
Windows7までではそれを除くと更新タイミングはWindowsUpdateだけということに・・・

そしてレジストリを眺めただけではどんな設定項目があるのか分かりづらい。
手動で設定してはじめて作成されるキーとかあるし。
他のアンチウィルスソフト使ってたら設定いじれないし。