Access VBAでプリンタを制する:Application.Printerオブジェクトの深淵と「負けない」自動化設計
Access開発において、帳票出力は避けて通れない聖域だ。しかし、多くのエンジニアが「デフォルトプリンタの切り替え」という単純な罠に足を取られ、環境依存のトラブルに翻弄されている。
Windowsのプリンタ制御は古く、かつ複雑だ。`Application.Printer`オブジェクトを単に「`Set`して`DoCmd.OpenReport`する」だけでは不十分である。なぜなら、Accessはプリンタ情報を内部でキャッシュし、プロセス終了まで掴み続けるからだ。
今日は、数多の現場を渡り歩いてきた私が、この「不安定な領域」をいかにして堅牢なシステムへと昇華させるか、その極限の知見を授けよう。
—
1. 悲劇を繰り返さない:プリンタの「状態管理」の真実
まず、最も重要な原則を叩き込んでおこう。「デフォルトプリンタを直接操作してはならない」。
Windowsのデフォルトプリンタを変更するスクリプトを書いてはならない。それは他のアプリケーションを巻き込む破壊行為であり、マルチユーザー環境では即座に衝突を引き起こす。我々が制御すべきは、Accessが保持する`Printer`オブジェクトの動的割り当てだ。
実装の勘所
Accessの`Application.Printers`コレクションは、Windowsが認識している全プリンタを列挙する。ここで特定のプリンタを検索し、`Application.Printer`へセットする際は、必ず元の設定を退避させ、処理後に復元する「トリアージ」が必要だ。
‘ 堅牢なプリンタ切り替えの定石
Public Sub PrintReportControlled(reportName As String, targetPrinterName As String)
Dim prtDefault As Printer
Dim prtTarget As Printer
‘ 現在のプリンタ設定を退避(メモリスタックのイメージ)
Set prtDefault = Application.Printer
‘ 対象プリンタを探し、適用する
For Each prtTarget In Application.Printers
If prtTarget.DeviceName = targetPrinterName Then
Set Application.Printer = prtTarget
Exit For
End If
Next
‘ 帳票出力:ウィンドウは非表示でリソースを節約
DoCmd.OpenReport reportName, acViewNormal
‘ 出力終了後、必ず元の設定に戻す(重要:メモリリーク回避)
Set Application.Printer = prtDefault
‘ オブジェクトの明示的解放(VBAのGCに頼らない強い意志)
Set prtDefault = Nothing
Set prtTarget = Nothing
End Sub
—
2. Windows APIによるデバイスモードの「真の制御」
`Application.Printer`プロパティだけでは、給紙トレイやトナー濃度、両面印刷といった詳細なドライバ設定までは制御しきれない場合がある。ここで諦めるのが凡人、APIに手を伸ばすのがエンジニアだ。
`DEVMODE`構造体を直接操作することで、プリンタの挙動を物理層から制御可能になる。これには`winspool.drv`の知識が必要となる。
極限の知見:DEVMODE構造体の重要性
Accessの`Printer.DeviceName`だけでは不十分な環境(特に共有プリンタサーバー経由)では、`OpenPrinter` APIを呼び出し、プリンタのハンドルを取得してドライバ情報を書き換える必要がある。
- 注意点: 32bit/64bitのポインタサイズの違いを意識せよ。`PtrSafe`属性の付与はもちろんのこと、`LongPtr`を用いたメモリレイアウトの最適化が必須となる。
—
3. なぜ「帳票」はズレるのか?:プリンタ情報のキャッシュ問題
Accessは帳票デザイン時に「どのプリンタで設計したか」という情報をレポートオブジェクトに埋め込む。これが環境依存の元凶だ。
現場で「開発環境と本番環境でレイアウトが崩れる」という悲鳴を聞いたら、以下のコードを各レポートの`Open`イベントに仕込め。
‘ レポートの読み込み時にプリンタ設定を「なし」にリセットする
Private Sub Report_Open(Cancel As Integer)
‘ 帳票に埋め込まれたプリンタのキャッシュを破棄し、
‘ システム側で動的に割り当てた設定に追従させる
Me.Printer = Nothing
End Sub
この一行で、Accessは固定されたレイアウト情報から解放され、その時々の`Application.Printer`の設定に最適化されるようになる。
—
4. チーフアーキテクトからの提言:保守性の極致へ
システム管理者にとって、プリンタ名のハードコーディングは「時限爆弾」である。プリンタの入れ替えやサーバー統合で、システムは確実に停止する。
- 設定テーブルの活用: `T_Printers` のようなテーブルを作成し、「帳票名」「プリンタ名」「用紙サイズ」をメタデータとして管理すること。
- 例外処理の徹底: `On Error Resume Next`で誤魔化すな。プリンタが見つからない場合は、ユーザーにどのプリンタを使うかを選択させるUI(`Application.FileDialog`ではなく、自作のリストボックス)を提示するのが、プロフェッショナルの矜持だ。
最後に
Access VBAは、現代の高度な言語に比べればレガシーかもしれない。しかし、その「泥臭い制御」の先には、業務を止めないというエンジニアの哲学がある。
オブジェクトを生成したら必ず解放する。環境に依存せず、かつ環境を汚さない。この基本を徹底するだけで、あなたの構築するシステムは、10年後も文句を言われずに稼働し続けるはずだ。
次は、APIによるスプールファイルの監視と、出力エラーの完全自動リカバリについて深掘りするとしよう。準備はいいか。
