【実務・中級編】Application.ActivePrinterの動的制御:図面サイズ(A4/A3/A1)に応じたプリンター自動振り分け印刷 – Visio VBA解析バイブル

スポンサーリンク

Visio VBAを掌握する極限の知見:用紙サイズに応じたプリンター自動振り分け印刷の極意

こんにちは。開発プロジェクトの現場で、数々の非効率な自動化スクリプトを「保守性の高いプロダクションコード」へと生まれ変わらせてきたチーフアーキテクトだ。

Visioでの図面管理において、最も現場の担当者を疲弊させる作業の一つが「印刷」ではないか?
A4の標準図面から、A3、果てはA1やA0といった大型プロッター用紙まで混在する図面ファイル。これを手動でページごとにプリンターを切り替えながら印刷しているとしたら、それはエンジニアの怠慢と言わざるを得ない。

今回は、Visioのオブジェクトモデルの深淵を突く『`Application.ActivePrinter` を駆使した用紙サイズ別プリンター自動振り分け印刷』の全技術を伝授する。

単に動くだけのコードではない。Visioのバグやプリンタードライバの遅延特性、ネットワークプリンターの気まぐれな挙動をもねじ伏せる、極限まで堅牢な設計と思想をここで公開しよう。

なぜ「単純なVBA印刷」は現場で破綻するのか?

多くの初学者は、ネットで見つけた次のようなコードをそのまま社内システムに組み込んで失敗する。

‘ 【反面教師とすべき脆弱なコード】
ActiveDocument.Print

これの何が問題か?
1. 出力先プリンターの制御不在: デフォルトプリンターが意図しないものに変わっているだけで、A1の巨大図面がA4のデスクサイドプリンターに誤送信され、トレイエラーの嵐を引き起こす。
2. ページサイズとドライバのミスマッチ: Visioのページ設定(Page.PageSheet)とプリンタードライバの用紙サイズ・方向の同期が取れておらず、意図せぬ縮小やクリッピング(端切れ)が発生する。
3. エラーハンドリングの欠如: プリンターがオフライン、あるいはスプーラーが詰まった瞬間にVBAがハングアップする。

プロのエンジニアが実装すべきは、「現在のページサイズをプログラム側で正確に検出し、対応するプリンタードライバを動的にバインドし、安全に排他制御しながら送り出す仕組み」だ。

アーキテクチャ設計:堅牢な自動振り分けの要件

今回のソリューションでは、以下の要件を満たすアーキテクチャを構築する。

1. ページ単位の寸法動的取得: ページの `PageSheet` から `PageWidth` と `PageHeight` をミリ単位で算出し、A4/A3/A1を論理判定する。
2. プリンター名の抽象化(マッピング): ハードコードされたプリンター名を避けるため、定数または外部設定(INIや簡易マスタ)で「サイズ ⇔ プリンター名」を管理する。
3. プリンター接続性の事前検証: `ActivePrinter` プロパティ書き換え時のランタイムエラーを防止するため、指定プリンターが利用可能かチェックする。

プロダクションコード:用紙サイズ自動判定・印刷モジュール

以下のコードは、実務の現場でそのままコピー&ペーストして組み込めるプロダクション品質のVBAコードだ。エラーハンドリングとオブジェクトのライフサイクル管理を徹底している。

Option Explicit

‘ ==============================================================================
‘ モジュール名: modAutoPrintController
‘ 概要: ページサイズに応じたプリンターの動的切替と一括印刷制御
‘ ==============================================================================

‘ プリンター名の定義(環境に合わせて書き換えてください)
Private Const PRINTER_A4 As String = “Officejet-Pro-XXXX”
Private Const PRINTER_A3 As String = “Network-A3-Laser”
Private Const PRINTER_A1 As String = “DesignJet-Plotter-A1”

Public Sub ExecuteSmartPrint()
Dim vsoPage As Visio.Page
Dim originalPrinter As String
Dim targetPrinter As String
Dim pageWidthMm As Double
Dim pageHeightMm As Double
Dim processedCount As Long

‘ 1. 現在のデフォルトプリンターを退避(処理終了後の復元用)
originalPrinter = Application.ActivePrinter

On Error GoTo ErrorHandler

‘ 2. ドキュメント内の全ページを走査
processedCount = 0
For Each vsoPage in ActiveDocument.Pages
‘ バックグラウンドページは印刷対象外とする
If Not vsoPage.Background Then
‘ ページアクティブ化(Visioの仕様上、操作対象ページにする必要がある)
Application.ActiveWindow.Page = vsoPage.Name

‘ ページの幅と高さをミリ単位で取得 (Visio内部単位(Inch)をMmに変換: 25.4)
pageWidthMm = vsoPage.PageSheet.Cells(“PageWidth”).Result(“mm”)
pageHeightMm = vsoPage.PageSheet.Cells(“PageHeight”).Result(“mm”)

‘ 3. 用紙サイズに基づくプリンターの動的振り分けロジック
targetPrinter = SelectPrinterBySize(pageWidthMm, pageHeightMm)

‘ 4. プリンターの切り替え実行
If SwitchActivePrinter(targetPrinter) Then
‘ 5. 印刷実行
vsoPage.Print
processedCount = processedCount + 1

‘ プリンターのスプール溢れを防ぐためのわずかなウェイト(高負荷環境対策)
DoEvents
Else
MsgBox “警告: プリンター ‘” & targetPrinter & “‘ への切り替えに失敗しました。” & vbCrLf & _
“対象ページ: ” & vsoPage.Name, vbExclamation, “印刷スキップ”
End If
End If
Next vsoPage

‘ 正常終了時の処理
Application.ActivePrinter = originalPrinter
MsgBox “印刷プロセスが完了しました。” & vbCrLf & “処理ページ数: ” & processedCount, vbInformation, “完了”
Exit Sub

ErrorHandler:
‘ 異常終了時も必ず元のプリンターに戻す(プリンター泥棒の防止)
Application.ActivePrinter = originalPrinter
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“Error No: ” & Err.Number & vbCrLf & _
“Description: ” & Err.Description, vbCritical, “致命的エラー”
End Sub

‘ ==============================================================================
‘ 補助関数: 寸法からプリンターを決定するロジック
‘ ==============================================================================
Private Function SelectPrinterBySize(ByVal w As Double, ByVal h As Double) As String
‘ 長辺・短辺を正規化して判定(縦・横置きを吸収)
Dim maxLen As Double
Dim minLen As Double

If w > h Then
maxLen = w: minLen = h
Else
maxLen = h: minLen = w
End If

‘ A4基準: 297 x 210 (許容誤差 5mm)
If maxLen <= 302 And minLen <= 215 Then SelectPrinterBySize = PRINTER_A4 ' A3基準: 420 x 297 (許容誤差 5mm) ElseIf maxLen <= 425 And minLen <= 302 Then SelectPrinterBySize = PRINTER_A3 ' それ以上は大型プロッター (A1等) Else SelectPrinterBySize = PRINTER_A1 End If End Function ' ============================================================================== ' 補助関数: 安全なプリンター切り替えラッパー ' ============================================================================== Private Function SwitchActivePrinter(ByVal printerName As String) As Boolean On Error GoTo PrinterError ' VisioのActivePrinterプロパティの書き換え Application.ActivePrinter = printerName SwitchActivePrinter = True Exit Function PrinterError: SwitchActivePrinter = False End Function --- 解説:このコードが「実務で耐えうる」理由 1. エラー時のリストア保証 (`On Error GoTo ErrorHandler`)
VBAで最もやってはいけないのが、途中でエラー落ちした際にユーザーのPCの「既定のプリンター」を書き換えたまま放置することだ。このコードでは、例外発生時や正常終了時に必ず `originalPrinter`(元のプリンター)を復元する設計を強制している。
2. 寸法判定の「揺れ」への対策 (`SelectPrinterBySize`)
Visioの図面作成者は、用紙設定を厳密なミリ数(例: 297mm)で作っているとは限らない。小数点以下のズや、縦置き・横置きの差異を吸収するため、長辺・短辺を正規化し、かつ許容誤差(5mm)を持たせたロジックにしている。これにより「A4なのにプロッターに流れる」という誤判定を完全に排除する。
3. `DoEvents` によるスプーラー保護
大量のページを一括印刷する場合、Visioからプリンタードライバへデータが流し込まれる速度が、OSのスプーラー処理速度を超えることがある。`DoEvents` を挟むことで、OSに制御を少し返し、メモリ溢れやプリンターの取りこぼしを防ぐ。

現場への導入とさらなる高みへ(データベース連携)

今回のコードではプリンター名をコード内の定数として持たせたが、拠点やプロジェクトごとにプリンター名が変わるような組織であれば、これをExcelマスタやSQLiteなどの外部データベースから動的に読み込むよう拡張してほしい。

「図面サイズ」「部署ID」「対応プリンター名」をテーブル化し、実行時にSQLで引き当てる。ここまで作り込めば、あなたの組んだ自動化ツールは、単なる「マクロ」を超えて、社内インフラストラクチャの一部としての価値を持つ。

Visio VBAの限界を嘆く前に、オブジェクトモデルを正しく理解し、堅牢なガードを構築せよ。あなたの開発するツールが、現場の無駄な残業を撲滅する強力な武器になることを確信している。

タイトルとURLをコピーしました