【Visio VBA】図面を閉じる瞬間をキャッチせよ:`Document_BeforeDocumentClose`で実現する「PDF自動更新」の極限設計
こんにちは。エンタープライズ領域の業務自動化アーキテクトだ。
これまでの現場で、何百回とこんな悲劇を目撃してきた。
- 「図面の最新版(VSDX)を修正したのに、上流工程や現場に配るPDFを更新し忘れた」
- 「古いPDFを参考にして作業が進んでしまい、手戻りが発生した」
- 「毎回手動で『名前を付けて保存>PDF』とする作業が、エンジニアの工数を無駄に削っている」
人間に「忘れずにやれ」と指導するのは、設計の敗北だ。システム側で強制し、かつ裏で完結させるべき領域である。
今回は、Visioのドキュメントイベントである `Document_BeforeDocumentClose` を完璧に手なずけ、「図面が閉じられるその瞬間、未保存の変更があれば問答無用で最新PDFを生成・上書き保存する」 堅牢なイベント駆動型自動化の実装を伝授しよう。
—
1. なぜ「イベント駆動」なのか?(非効率な設計からの脱却)
よくある素朴なマクロは、標準モジュールに書いたプロシージャをユーザーに「手動で実行」させるものだ。
- 「図面を修正する」
- 「PDFを出力するマクロボタンを押す」
- 「保存して閉じる」
このワークフローは、人間の「うっかり」を防げない。
真にプロフェッショナルな設計とは、業務フローそのものにシステムを溶け込ませることだ。Visioのドキュメントイベントを `ThisDocument` クラスに実装することで、ユーザーがどのルート(×ボタン、ファイルメニュー、VBAからのクローズなど)でファイルを閉じようとも、確実にコードのフックを通過させることができる。
—
2. 実装における3つの技術的罠(ハマりポイント)
この自動化をプロダクション環境(実務)に投入するにあたり、アマチュアが必ず踏み抜く「3つの地雷」が存在する。
1. 無限ループとネストイベントの恐怖
PDF出力や保存の過程で、意図せず別のイベントが発火し、スタックオーバーフローや予期せぬダイアログの嵐を引き起こす。
2. 「未保存変更」の正確な判定
`Saved` プロパティの評価タイミングを誤ると、意図しないタイミングでPDFが走ったり、逆に変更が検知されなかったりする。
3. ファイルパスの解決(未保存新規ファイルのハンドリング)
一度も保存されていない(パスが `””` の)状態で閉じようとした場合、PDFの出力先を決定できずにエラー落ちする。
これらを完全にクリアした、実務投入レベルのコードを次節で公開する。
—
3. プロダクションコード例(コピペ&即導入可能)
このコードは、標準モジュールではなく、Visioの「`ThisDocument`」クラスモジュールに直接記述する必要がある。
手順
1. VisioでVSDXファイルを開き、`Alt + F11` でVBE(Visual Basic Editor)を開く。
2. 左側のプロジェクトエクスプローラから、対象図面の `Microsoft Visio Objects` > `ThisDocument` をダブルクリック。
3. 以下のコードを貼り付ける。
Option Explicit
‘ =========================================================================
‘ クラス名: ThisDocument
‘ 概要: ドキュメントクローズ前のイベントをフックし、未保存変更がある場合に
‘ 自動的に同一階層・同名でPDFを強制エクスポートする
‘ =========================================================================
Private Sub Document_BeforeDocumentClose(ByVal Doc As Visio.Document)
‘ エラーハンドリングの要
On Error GoTo ErrorHandler
‘ 【重要】一時ファイルやアドイン、ステンシル (.vssx/.vstm) は処理対象外とする
If Doc.Type <> visTypeDrawing Then Exit Sub
‘ 新規未保存ファイル(まだ一度もファイルパスが割り当てられていない)の場合はスキップ
If Doc.Path = “” Then
‘ 必要に応じてここで「名前を付けて保存」を促すことも可能だが、
‘ 今回はクローズを妨げないためにスルーする設計とする
Exit Sub
End If
‘ ドキュメントに「未保存の変更」があるか判定
‘ Saved = False は、最後に保存されてから変更が加わっている状態を指す
If Not Doc.Saved Then
‘ ユーザーへの無言の強制(必要に応じてメッセージを出しても良いが、
‘ 完全自動化を目指すならサイレント処理が望ましい)
Application.ScreenUpdating = False
Application.StatusText = “図面の変更を検知しました。最新PDFを自動生成しています…”
‘ PDFの出力パスを構築 (拡張子を .vsdx から .pdf に置换)
Dim pdfPath As String
pdfPath = GetPdfPath(Doc.FullName)
‘ Visioの標準機能である Export メソッドでPDF化
‘ ※ フィルターID “pdf” は全Visio共通で利用可能
Doc.Export pdfPath
‘ (オプション)PDF出力によってドキュメント自体がさらに変更扱いになるのを防ぐ、
‘ あるいはここで自動的にVSDX本体も上書き保存する場合は Doc.Save を実行する
‘ 今回は「PDFの同期」が目的なので、VSDXのセーブ状態は変えないか、
‘ あるいはユーザーの意志に委ねる。
End If
CleanUp:
Application.ScreenUpdating = True
Application.StatusText = “”
Exit Sub
ErrorHandler:
‘ 予期せぬエラーでVisioごと落ちるのを防ぐ防壁
MsgBox “PDFの自動更新中にエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“エラー内容: ” & Err.Description, vbCritical, “PDF自動生成ガード”
Resume CleanUp
End Sub
‘ =========================================================================
‘ 補助関数: フルパスから拡張子を置換してPDFのパスを生成する
‘ =========================================================================
Private Function GetPdfPath(ByVal fullPath As String) As String
Dim extPos As Long
‘ 右側からドットの位置を探す
extPos = InStrRev(fullPath, “.”)
If extPos > 0 Then
GetPdfPath = Left(fullPath, extPos – 1) & “.pdf”
Else
GetPdfPath = fullPath & “.pdf”
End If
End Function
—
4. アーキテクトが解説するコードの急所
① `Doc.Type visTypeDrawing` によるフィルタリング
Visioには、図面(`visTypeDrawing`)の他に、ステンシル(`visTypeStencil`)やテンプレートが存在する。これらに対してPDFエクスポートを試みると、当然ながら実行時エラーを引き起こす。「図面ファイルのみを対象にする」というガードを最初に張るのがプロの作法だ。
② `Application.ScreenUpdating = False` によるパフォーマンス最適化
PDFをエクスポートする際、裏で描画処理やページ走査が発生する。画面のちらつきを防ぎ、処理速度を限界まで引き上げるために、イベント発火と同時に描画をロックし、終了時に確実に解除(`CleanUp` ラベル)する構造にしている。
③ エラーハンドリングの絶対厳守
`Document_BeforeDocumentClose` の中で未処理のエラーが発生すると、最悪の場合、「図面が閉じられないままVisioのセッションが不安定になる」というユーザーにとって最もストレスフルな状況を生む。
必ず `On Error GoTo ErrorHandler` を配置し、万が一PDFドライバの競合などでエクスポートに失敗しても、ファイルのクローズ処理自体を阻害しない、あるいは安全にメッセージを出して抜ける設計にしなければならない。
—
5. 運用への展開とさらなる高みへ
このコードを仕込んだVSDXを、チーム共用のテンプレート(`.vstx`)として配備すれば、そのテンプレートから作られたすべての図面で「PDFの更新漏れ」が原理的に撲滅される。
さらに発展させるのであれば、ローカルパスではなく、社内の SharePoint / Microsoft Teams (SharePoint裏のドキュメントライブラリ) や、データベース(Kintoneや独自SQL Serverなど)のストレージパスに対して直接PDFをアップロード・上書きするロジックへと拡張することも可能だ。
「人が手動でやる作業」は、技術で片っ端から潰していく。
それこそが、我々エンジニアが提供すべき真の価値である。ぜひ今日の開発から導入してみてほしい。
