AutoCAD VBAを掌握する極限の知見
【実務中級】AcadDocument.Plot.PlotToDeviceによる「物理プリンタへの自動出力」:ページ設定を無視した強制印刷制御
—
はじめに:なぜ「ページ設定」に依存してはならないのか
大規模なプラント設計、あるいは数千枚単位のインフラ図面を扱う現場において、レイアウトごとにバラバラの「ページ設定(PAGSETUP)」が埋め込まれている図面群を前に絶望したことはないだろうか。
「急遽、出力先プリンタを変更しなければならない」「全図面の用紙サイズを強制的にA1縦に統一して出力しろ」——こうした非情な現場の要求に対し、UIから手作業でページ設定マネージャを開き、一つひとつプロパティを書き換えていくアプローチは、エンジニアリングの放棄に等しい。
AutoCAD VBAの `AcadDocument.Plot.PlotToDevice` メソッドを極限まで理解すれば、図面側に保存されている腐ったページ設定を完全に無視し、VBAコード側から出力デバイス、用紙サイズ、印刷スタイル、そして配向を動的にねじ曲げ、高速かつ確実に物理プリンタへジョブを送り出すことが可能になる。
本稿では、レガシーなAutoCAD VBA環境において、メモリリークを完全に防ぎ、確実な排他制御とエラーハンドリングを伴う「物理プリンタへの強制出力システム」の設計思想と実装コードを詳解する。
—
1. オブジェクトモデルの深層とメモリ管理の鉄則
AutoCAD VBAにおける最大の罠は、COM(Component Object Model)の背後で稼働するC++ネイティブエンジンとの参照カウンタの不一致、すなわちメモリリークとゾンビプロセスの発生である。
`Plot` オブジェクトや `Layout` オブジェクトを操作する際、暗黙的なオブジェクト参照の連鎖(Chaining)を行うと、VBAのガベージコレクタが正しくメモリを解放できず、バックグラウンドで `acad.exe` のゾンビプロセスが蓄積していく。
【極限の知見:オブジェクト解放の作法】
1. 変数の型を厳密に定義する:`Dim` 省略によるバリアント型や遅延バインディング(`CreateObject` 等)は厳禁。明示的な型指定により、VBAランタイムに適切なVTable経由の呼び出しを強制する。
2. 参照の切り離し(`Nothing` 代入):ループ処理内で生成・取得したオブジェクト(特に `Layout` や `Plot`)は、イテレーションの最後に必ず `Set xxx = Nothing` を明示し、参照カウントを即座にデクリメントする。
3. エラー時クリーンアップ:`On Error GoTo` による例外処理を必ず実装し、異常終了時であっても確実にCOMオブジェクトの解放経路を通す。
—
2. `PlotToDevice` の内部挙動と制約事項
`PlotToDevice` は、指定されたレイアウトを現在の(あるいはコードから一時的にアサインされた)プロットコンフィギュレーションに基づき、デフォルトまたは指定のデバイスへスプールする。
しかし、ここで重大な制約がある。`PlotToDevice` は、原則として「レイアウトに紐付けられたPC3ファイル(プロットコンフィギュレーション)」の構成をベースに動作する。つまり、完全にゼロから用紙サイズやデバイスをコードだけで指定しようとすると、内部のドライバキャッシュと競合し、エラー番号 `-2145320939` (AutoCAD Plot Error) などを吐いて沈黙する。
この制約を突破するためには、以下の手順を踏む必要がある。
1. 対象レイアウトの `ConfigName` プロパティに、ターゲットとなる物理プリンタ名またはPC3ファイルを明示的にバインドする。
2. 必要に応じて `CanonicalMediaName`(用紙サイズの内部名称)をコードから書き換える。
3. `Plot.PlotToDevice` を実行する。
—
3. 実戦コード:ページ設定を無視する強制印刷エンジン
以下のコードは、指定フォルダ内の全図面(`.dwg`)を走査し、図面内の「モデル空間」およびすべての「レイアウト」に対し、指定した物理プリンタと用紙サイズを強制適用して一括出力する実用モジュールである。
Option Explicit
‘ ==============================================================================
‘ módulo名: modBatchPlotEngine
‘ 概要: ページ設定を完全に無視し、指定デバイス・用紙サイズで強制出力を行う
‘ ターゲット: AutoCAD 2020〜2025 (32bit/64bit VBA Runtime)
‘ ==============================================================================
Public Sub ExecuteForcedPlotToDevice()
Dim targetFolder As String
Dim targetPrinter As String
Dim targetMedia As String
Dim fso As Object
Dim folder As Object
Dim file As Object
Dim acadApp As AcadApplication
Dim doc As AcadDocument
Dim originalSDI As Integer
‘ — 設定パラメータ(環境に合わせて変更すること) —
targetFolder = “C:\AutoCAD_Work\TargetDrawings\”
targetPrinter = “HP LaserJet M609 PCL 6” ‘ 実際の物理プリンタ名またはPC3
targetMedia = “A4” ‘ 用紙サイズの内部名称(例: “A4”, “A3″)
‘ ————————————————–
‘ ファイルシステムオブジェクトの初期化
Set fso = CreateObject(“Scripting.FileSystemObject”)
If Not fso.FolderExists(targetFolder) Then
MsgBox “指定されたフォルダが存在しません: ” & targetFolder, vbCritical, “致命的エラー”
Exit Sub
End If
Set folder = fso.GetFolder(targetFolder)
‘ AutoCADアプリケーションの取得(実行中のインスタンスをフック、なければ起動)
On Error Resume Next
Set acadApp = GetObject(, “AutoCAD.Application”)
If acadApp Is Nothing Then
Set acadApp = New AcadApplication
End If
On Error GoTo 0
If acadApp Is Nothing Then
MsgBox “AutoCADの起動に失敗しました。”, vbCritical
Exit Sub
End If
‘ 高速化とメモリ保護のため、バックグラウンドでサイレント実行を推奨
‘ SDI環境の退避
originalSDI = acadApp.GetSystemVariable(“SDI”)
acadApp.SetSystemVariable “SDI”, 0 ‘ 複数ドキュメントモード
‘ バッチ処理の実行
For Each file In folder.Files
If LCase(fso.GetExtensionName(file.Name)) = “dwg” Then
On Error GoTo ErrorHandler
‘ 図面のオープン(ReadOnlyでロックを回避)
Set doc = acadApp.Documents.Open(file.Path, True)
‘ 描画・印刷の実行コア
Call ProcessAndPlotDocument(doc, targetPrinter, targetMedia)
‘ 変更を保存せずに閉じる
doc.Close False
Set doc = Nothing
On Error GoTo 0
End If
Next file
‘ 終了処理
acadApp.SetSystemVariable “SDI”, originalSDI
MsgBox “すべての図面の強制印刷バッチが完了しました。”, vbInformation, “完了”
CleanUp:
‘ 確実なオブジェクト解放
Set doc = Nothing
Set acadApp = Nothing
Set folder = Nothing
Set fso = Nothing
Exit Sub
ErrorHandler:
MsgBox “エラー発生 [ファイル: ” & file.Name & “]” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“詳細: ” & Err.Description, vbCritical, “実行時例外”
If Not doc Is Nothing Then
doc.Close False
Set doc = Nothing
End If
Resume CleanUp
End Sub
‘ ==============================================================================
‘ 内部プロシージャ: 単一ドキュメント内の全レイアウトに対する強制プロット制御
‘ ==============================================================================
Private Sub ProcessAndPlotDocument(ByRef targetDoc As AcadDocument, ByVal printerName As String, ByVal mediaName As String)
Dim layout As AcadLayout
Dim plotObj As AcadPlot
Dim activeLayoutName As String
Set plotObj = targetDoc.Plot
‘ モデル空間およびすべてのレイアウトを走査
For Each layout In targetDoc.Layouts
‘ 画面上の「モデル」または「レイアウト」タブに対応するレイアウトオブジェクト
‘ 有効なプロット領域を持つもののみ対象とする
If layout.ModelSpace = True Or layout.Block.Count > 0 Then
‘ レイアウトを一時的にアクティブ化(一部のバージョン依存のバグを回避するため)
targetDoc.ActiveLayout = layout
‘ 1. デバイス(プリンタ)の強制割り当て
layout.ConfigName = printerName
‘ 2. 用紙サイズの強制書き換え
‘ ※事前にプリンタ側がサポートしているメディア名であること
On Error Resume Next
layout.CanonicalMediaName = mediaName
If Err.Number <> 0 Then
‘ サポートされていない用紙サイズの場合はログを残しデフォルトを維持
Debug.Print “Warning: 用紙サイズ ‘” & mediaName & “‘ の設定に失敗しました。デフォルトを維持します。”
Err.Clear
End If
On Error GoTo 0
‘ 3. プロットスタイルの強制設定(必要に応じて)
‘ layout.PlotType = acExtents ‘ 図面範囲
‘ layout.StandardScale = acScaleToFit ‘ 尺度合わせ
‘ 4. 物理プリンタへの非同期/同期出力実行
‘ 第1引数: プレビューフラグ (False = 直接出力)
‘ 第2引数: 部数 (1固定)
‘ 第3引数: バックグラウンド印刷の有効化 (環境によるがFalse推奨)
If plotObj.PlotToDevice(printerName) Then
Debug.Print “Success Plot: ” & targetDoc.Name & ” -> Layout: ” & layout.Name
Else
Debug.Print “Failed Plot: ” & targetDoc.Name & ” -> Layout: ” & layout.Name
End If
End If
Set layout = Nothing
Next layout
Set plotObj = Nothing
End Sub
—
4. 現場で直面するトラブルシューティング(知見の共有)
シニアエンジニアとして、このコードを実際のプラントやゼネコンの環境に投入した際によく遭遇する「罠」と、その処方箋を共有しておく。
トラブルA:ネットワークプリンタのドライバ名差異によるエラー
- 症状:開発環境のローカルPCでは完璧に動作したのに、本番のプリンタサーバー経由のネットワークプリンタを指定した途端、`ConfigName` の代入時にエラーが発生する。
- 原因:AutoCADが認識しているプリンタ名は、Windowsのデバイスマネージャ上の名前ではなく、PC3ファイル内部にハードコードされたドライバ名、あるいは正確なUNCパス文字列である必要がある。
- 対策:あらかじめ正しい設定を保持した `.pc3` ファイルを AutoCADの plotter 検索パス(例:`C:\Users\
\AppData\Roaming\Autodesk\AutoCAD 202x\R24.x\enu\Plotters`)に配置し、コード側では `printerName` に `.pc3` の拡張子を含めたファイル名を渡すこと(例: `”A1_Plotter_HP.pc3″`)。
トラブルB:バックグラウンド印刷 (`BACKGROUNDPLOT`) による競合
- 症状:大量の図面を連続処理していると、途中でAutoCADがフリーズするか、一部の図面が白紙で印刷される。
- 原因:AutoCADのシステム変数 `BACKGROUNDPLOT` が `1`(有効)または `2` の場合、印刷プロセスが別タスクとして非同期で切り離される。VBA側が次々と図面をクローズしていくスピードにバックグラウンドの印刷スプーラが追いつきなくなり、メモリ上の図面データが破壊される。
- 対策:スクリプト実行の冒頭で `BACKGROUNDPLOT` を強制的に `0`(無効:同期印刷)に設定し、すべての処理が完全に完了してから元の値に戻す防衛策を講じること。
Dim originalBGPlot As Integer
originalBGPlot = acadApp.GetSystemVariable(“BACKGROUNDPLOT”)
acadApp.SetSystemVariable “BACKGROUNDPLOT”, 0
‘ … 印刷処理 …
acadApp.SetSystemVariable “BACKGROUNDPLOT”, originalBGPlot
—
おわりに
CADオペレーターの手作業から人間を解放し、完全な自動化パイプラインを構築することこそ、我々オートメーション・エンジニアの存在意義である。
`AcadDocument.Plot.PlotToDevice` をはじめとするCOMインターフェースは、一見するとレガシーで気難しい代物に見えるかもしれない。しかし、その内部構造(VTable、参照カウンタ、システム変数との同期)を完全に把握し尽くした者にとっては、図面群を意のままに操るための最強の剣となる。
手作業による印刷地獄から社内を救い出すために、この知見をあなたのシステムへ直ちに組み込んでほしい。
