【入門編】【中級者向け】Visioの「名前を付けて保存」をフックし、保存時に自動でPDFを生成するイベントハンドラ – Visio VBA解析バイブル

スポンサーリンク

Visio VBAの「名前を付けて保存」をフック!保存と同時にPDFを自動生成する魔法

こんにちは!Visio VBAの世界へようこそ。今回は、日々の図面管理をもっとスマートにする、ちょっとした「魔法」を伝授しましょう。Visioで図面を保存するたびに、自動でPDFも生成できたら便利だと思いませんか?

「え、そんなことできるの?」と思ったあなた、大丈夫です!Visio VBAの `DocumentSaved` イベントを使えば、これが驚くほど簡単に実現できるんです。マクロの記録だけではたどり着けない、一歩進んだ自動化の世界へ、一緒に踏み出してみましょう!

なぜ「名前を付けて保存」をフックするのか?

普段、Visioで図面を編集しているとき、「あ、このバージョンも保存しておこう」「後で共有するためにPDFも作っておこう」なんて思うことがありますよね。でも、毎回「名前を付けて保存」→「PDFとして保存」と手作業で繰り返すのは、正直ちょっと面倒…。

そこで役立つのが、Visio VBAのイベント処理です。Visioには、様々な操作が発生したときに自動的に実行される「イベント」が用意されています。今回注目するのは、`DocumentSaved` イベント。これは、Visioのドキュメント(図面ファイル)が保存された直後に自動的に発火するイベントなんです。

このイベントをうまく利用することで、「VSDXファイルが保存されたら、そのファイルと同じ名前でPDFも自動生成する」という、とっても便利なワークフローを構築できます。

`DocumentSaved` イベントとは? ~イベント処理の基本~

イベント処理は、プログラミングにおいて非常に強力な概念です。例えるなら、「誰かがドアを開けたら、自動で電気がつく」ようなものです。

Visio VBAでは、特定のオブジェクト(例えば、Visioアプリケーション全体や、開いているドキュメントなど)が、特定の「イベント」(例えば、保存、印刷、図形が追加された、など)を発生させたときに、あらかじめ用意しておいた「処理」(VBAコード)を実行させることができます。

`DocumentSaved` イベントは、`Visio.Document` オブジェクトに紐づいています。つまり、開いている特定のVisioドキュメントが保存されたときに、そのドキュメント自身に関連する処理を実行させたい、という場合に最適です。

イベント処理の仕組み(ざっくりと)

1. イベントソースの特定: どのオブジェクトがイベントを発生させるのかを決めます。(今回は、開いているVisioドキュメント)
2. イベントハンドラの設定: そのイベントが発生したときに実行したいVBAコード(イベントハンドラ)を記述します。
3. イベントへの「登録」: イベントソースに、発生したイベントを検知したら、どのイベントハンドラを実行してほしいかを「登録」します。

この「登録」の部分が、ちょっとだけ慣れが必要なところです。通常、イベントハンドラは、VBAエディタの標準モジュールに記述しても、自動的には実行されません。特定のオブジェクトのイベントを捕捉するには、そのオブジェクトのプロパティを通して「イベントを有効にする」必要があるのです。

実践!VSDX保存時にPDFを自動生成するVBAコード

それでは、いよいよコードを書いてみましょう!今回は、Visio VBAの標準モジュールにコードを記述する形で実装します。

【重要】 このコードは、VisioのVBAエディタ(`Alt + F11`で起動)の標準モジュール(`Module1`など)に記述することを想定しています。

‘
‘ Module: DocumentSaveEventHandler
‘ Author: あなたの名前 (またはニックネーム)
‘ Date: 2023-10-27
‘ Description: VisioのDocumentSavedイベントをフックし、VSDX保存時にPDFを自動生成する。
‘

‘ — グローバル変数 —
‘ どのドキュメントのイベントを捕捉するかを保持するための変数
Public WithEvents VisioDoc As Visio.Document

‘ — 初期化処理 —
‘ Visioアプリケーション起動時にイベントハンドラをセットアップするプロシージャ
Sub SetupDocumentSaveHandler()
‘ 現在アクティブなドキュメントを取得
Dim activeDoc As Visio.Document

On Error Resume Next ‘ エラーが発生しても処理を続行
Set activeDoc = Visio.ActiveDocument
On Error GoTo 0 ‘ エラーハンドリングを元に戻す

‘ アクティブなドキュメントが存在する場合のみ処理を実行
If Not activeDoc Is Nothing Then
‘ グローバル変数にアクティブなドキュメントをセット
‘ これにより、VisioDocオブジェクトがアクティブなドキュメントのイベントを捕捉できるようになる
Set VisioDoc = activeDoc
Debug.Print “DocumentSaveEventHandler: ‘” & activeDoc.FullName & “‘ のDocumentSavedイベントを捕捉しました。”
Else
Debug.Print “DocumentSaveEventHandler: アクティブなドキュメントがありません。イベントハンドラはセットアップされませんでした。”
End If
End Sub

‘ — イベントハンドラ本体 —
‘ DocumentSavedイベントが発生したときに自動的に実行されるプロシージャ
Private Sub VisioDoc_DocumentSaved(ByVal doc As Visio.Document)
Dim pdfFilePath As String
Dim originalFilePath As String
Dim originalFileNameWithoutExt As String

‘ 保存されたドキュメントのフルパスを取得
originalFilePath = doc.FullName

‘ ファイル名(拡張子なし)を取得
originalFileNameWithoutExt = Left(originalFilePath, InStrRev(originalFilePath, “.”) – 1)

‘ PDFファイルのパスを生成(元のVSDXファイルと同じディレクトリ、同じファイル名、拡張子.pdf)
pdfFilePath = originalFileNameWithoutExt & “.pdf”

‘ 既存のPDFファイルがあれば削除する(任意)
‘ If Dir(pdfFilePath) <> “” Then
‘ Kill pdfFilePath
‘ Debug.Print “DocumentSaveEventHandler: 既存のPDFファイル ‘” & pdfFilePath & “‘ を削除しました。”
‘ End If

‘ PDFとしてエクスポートを実行
On Error Resume Next ‘ エラーが発生しても処理を続行
doc.ExportAsFixedFormat _
ThisDocument:=doc, _
FileName:=pdfFilePath, _
Format:=VisFixedFormatType.visFixedFormatPDF, _
Intent:=visPrint, _
IncludeDocProps:=True, _
KeepDiagramBackground:=True

‘ エラーチェック
If Err.Number <> 0 Then
MsgBox “PDFの生成中にエラーが発生しました。” & vbCrLf & _
“エラーコード: ” & Err.Number & vbCrLf & _
“エラー内容: ” & Err.Description, vbCritical, “PDF生成エラー”
Debug.Print “DocumentSaveEventHandler: PDF生成エラー – ” & Err.Description
Else
MsgBox “VSDXファイル ‘” & doc.Name & “‘ の保存が完了し、PDFファイル ‘” & Dir(pdfFilePath) & “‘ が自動生成されました。”, vbInformation, “PDF自動生成完了”
Debug.Print “DocumentSaveEventHandler: PDF生成完了 – ‘” & pdfFilePath & “‘”
End If
On Error GoTo 0 ‘ エラーハンドリングを元に戻す

‘ ドキュメントのイベント監視を解除(重要!)
‘ これをしないと、ドキュメントを閉じてもイベントハンドラが残り続ける可能性があります。
‘ ただし、このコード例ではVisio終了時に自動的に解除されることが多いため、必須ではない場合もあります。
‘ Set VisioDoc = Nothing
End Sub

‘ — Visio終了時のクリーンアップ処理 (オプション) —
‘ Visioアプリケーションが終了するときに、イベントハンドラを適切に解除するためのプロシージャ。
‘ ただし、直接呼び出すのではなく、VisioのApplication_Quitイベントなどを利用するのがより堅牢です。
‘ ここでは、手動で実行する想定で記述します。
Sub CleanupDocumentSaveHandler()
Set VisioDoc = Nothing
Debug.Print “DocumentSaveEventHandler: イベントハンドラをクリーンアップしました。”
End Sub

‘ — テスト用マクロ —
‘ SetupDocumentSaveHandler を実行し、イベント監視を開始するためのマクロ。
‘ このマクロを実行した後に、図面を保存するとPDFが生成されるようになります。
Sub StartPdfGenerationOnSave()
MsgBox “PDF自動生成機能を開始します。アクティブな図面を保存すると、同時にPDFが生成されます。”, vbInformation, “開始”
SetupDocumentSaveHandler
End Sub

‘ PDF自動生成機能を停止するためのマクロ。
Sub StopPdfGenerationOnSave()
CleanupDocumentSaveHandler
MsgBox “PDF自動生成機能を停止しました。”, vbInformation, “停止”
End Sub

コードの解説 ~魂を込めて~

このコードを理解できれば、Visio VBAのイベント処理の基本はマスターしたも同然です!一つずつ見ていきましょう。

1. `Public WithEvents VisioDoc As Visio.Document`:

  • `Public`: この変数が、VBAプロジェクトのどこからでもアクセス可能であることを示します。
  • `WithEvents`: これが、イベント処理の「肝」です!このキーワードを付けることで、`Visio.Document` 型の変数(ここでは `VisioDoc`)に対して、発生するイベントを捕捉できるようになります。
  • `Visio.Document`: Visioのドキュメントオブジェクトを表す型です。
  • ポイント: `WithEvents` を使うと、VBAエディタのコードウィンドウの上部にあるオブジェクトドロップダウンリストに、この変数の名前が表示され、その横のドロップダウンリストに発生可能なイベント(`DocumentSaved` など)が表示されるようになります。これが、イベントハンドラを自動生成するための便利な機能です。

2. `Sub SetupDocumentSaveHandler()`:

  • このプロシージャは、イベントハンドラを「セットアップ」するためのものです。Visioを起動したときや、特定の図面を開いたときに、このプロシージャを実行することで、`VisioDoc` 変数に現在のドキュメントを代入し、イベント捕捉の準備をします。
  • `Set activeDoc = Visio.ActiveDocument`: 現在アクティブになっている(一番手前で開いている)Visioドキュメントを取得します。
  • `Set VisioDoc = activeDoc`: ここで、`WithEvents` で宣言した `VisioDoc` 変数に、実際のアクティブドキュメントを代入しています。これにより、`VisioDoc` はそのドキュメントのイベントを監視するようになります。
  • `Debug.Print`: これは、VBAエディタの「イミディエイトウィンドウ」(`Ctrl+G`で表示)にメッセージを出力するコマンドです。コードが正しく動いているかを確認するのに役立ちます。

3. `Private Sub VisioDoc_DocumentSaved(ByVal doc As Visio.Document)`:

  • これが、まさに `DocumentSaved` イベントが発生したときに自動的に実行される「イベントハンドラ」です。
  • `VisioDoc_` という接頭辞は、`WithEvents` で宣言した変数名 (`VisioDoc`) に由来しています。Visio VBAが、`VisioDoc` という変数に紐づいた `DocumentSaved` イベントが発生したときに、このプロシージャを探して実行してくれるのです。
  • `ByVal doc As Visio.Document`: イベントが発生したときに、Visioからイベントに関する情報が引数として渡されます。ここでは、保存されたドキュメントオブジェクトそのものが `doc` という変数名で渡ってきます。`VisioDoc` 変数にセットしたドキュメントと同じものです。
  • `originalFilePath = doc.FullName`: 保存されたVSDXファイルのフルパス(例: `C:\Users\…\MyDrawing.vsdx`)を取得します。
  • `originalFileNameWithoutExt = Left(originalFilePath, InStrRev(originalFilePath, “.”) – 1)`: `InStrRev` 関数で最後の”.”の位置を探し、それより前の文字列を `Left` 関数で切り出すことで、拡張子なしのファイル名(例: `C:\Users\…\MyDrawing`)を取得しています。
  • `pdfFilePath = originalFileNameWithoutExt & “.pdf”`: 取得したファイル名に `.pdf` を付けて、PDFファイルの保存先パスを生成します。
  • `doc.ExportAsFixedFormat …`: これがPDF出力の本体です。
  • `visFixedFormatPDF`: 出力形式をPDFに指定します。
  • `visPrint`: PDFの目的に「印刷」を指定します。
  • `visFixedFormatType.visFixedFormatPDF`: PDF形式を指定します。
  • エラーハンドリング: `On Error Resume Next` と `Err.Number` を使って、PDF出力中にエラーが発生した場合に、エラーメッセージを表示するようにしています。これは、ファイルがロックされていたり、パスが存在しない場合などに役立ちます。
  • `Set VisioDoc = Nothing` (コメントアウト部分): イベントハンドラを捕捉しているオブジェクトを解放する(監視をやめさせる)ためのコードです。ドキュメントを閉じるときなどに明示的に実行すると、リソースの無駄遣いを防げます。ただし、Visioアプリケーション自体を閉じると、通常は自動的に解放されるため、必須ではない場合もあります。

4. `Sub StartPdfGenerationOnSave()` / `Sub StopPdfGenerationOnSave()`:

  • これらは、イベントハンドラを「開始」および「停止」するための、ユーザーフレンドリーなマクロです。
  • `StartPdfGenerationOnSave` を実行すると、`SetupDocumentSaveHandler` が呼ばれ、イベント監視が開始されます。
  • `StopPdfGenerationOnSave` を実行すると、`CleanupDocumentSaveHandler` が呼ばれ、イベント監視が停止されます。

実行手順

1. Visioを開きます。
2. `Alt + F11` キーを押してVBAエディタを開きます。
3. 「挿入」メニューから「標準モジュール」を選択し、新しいモジュール(`Module1`など)を作成します。
4. 上記のVBAコードを、作成したモジュールにコピー&ペーストします。
5. Visioに戻り、図面を開きます(または新規作成します)。
6. VBAエディタに戻り、`StartPdfGenerationOnSave` マクロ(コードの最後の方にある `Sub`)にカーソルを置きます。
7. 「実行」ボタン(緑色の三角形)をクリックするか、`F5` キーを押します。
8. 「PDF自動生成機能を開始します…」というメッセージが表示されたら、「OK」をクリックします。
9. Visioに戻り、開いている図面を「名前を付けて保存」してみてください。
10. 保存が完了すると、「VSDXファイル~PDFファイル~が自動生成されました。」というメッセージが表示され、VSDXファイルと同じ場所にPDFファイルが生成されているはずです!

陥りやすいエラーと、その回避策

エラー1: イベントハンドラが実行されない

  • 原因: `SetupDocumentSaveHandler` プロシージャが実行されていない。
  • 回避策: 手動で `StartPdfGenerationOnSave` マクロを実行するか、Visio起動時に自動実行されるように設定する(後述)。
  • 確認方法: VBAエディタのイミディエイトウィンドウ(`Ctrl+G`)に、`SetupDocumentSaveHandler` の `Debug.Print` メッセージが表示されているか確認する。

エラー2: PDFファイルが生成されない、またはエラーメッセージが表示される

  • 原因:
  • PDF出力先のフォルダに書き込み権限がない。
  • PDFファイルが既に開かれていてロックされている。
  • ファイルパスが長すぎる、または無効な文字が含まれている。
  • `ExportAsFixedFormat` メソッドの引数が間違っている。
  • 回避策:
  • 保存先のフォルダの権限を確認する。
  • PDFファイルが開いていないか確認する。
  • ファイル名やパスに問題がないか確認する。
  • コードの `ExportAsFixedFormat` の引数を再確認する。特に `Intent` や `IncludeDocProps` の設定が意図通りか確認する。
  • 確認方法: エラーメッセージをよく読み、原因を特定する。イミディエイトウィンドウにエラーの詳細が出力されていないか確認する。

エラー3: ドキュメントを閉じてもイベントハンドラが残り続ける(メモリリークの可能性)

  • 原因: `WithEvents` で宣言したオブジェクト変数が、明示的に解放 (`Set VisioDoc = Nothing`) されないまま、Visioアプリケーションが終了する。
  • 回避策:
  • `CleanupDocumentSaveHandler` マクロを、不要になったタイミングで手動実行する。
  • より高度な方法として、VisioのApplicationオブジェクトに `Quit` イベントハンドラを設定し、Visio終了時に自動的に `CleanupDocumentSaveHandler` を呼び出すようにする。

さらに高度な活用 ~応用編~

Visio起動時に自動でイベントハンドラをセットアップする

毎回 `StartPdfGenerationOnSave` を実行するのは面倒ですよね。Visio起動時に自動でイベントハンドラをセットアップするには、`ThisDocument` モジュール(VBAエディタのプロジェクトエクスプローラーで「ThisDocument」となっている箇所)に以下のコードを追加します。

‘ ThisDocument モジュールに記述

Private Sub Document_DocumentCreated(ByVal doc As Document)
‘ 新規ドキュメント作成時にもイベントハンドラをセットアップする(任意)
If ThisDocument.VisioDoc Is Nothing Then ‘ まだセットアップされていなければ
Set ThisDocument.VisioDoc = doc
Debug.Print “ThisDocument: DocumentCreated – ‘” & doc.Name & “‘ のイベントハンドラをセットアップしました。”
End If
End Sub

Private Sub Document_DocumentOpened(ByVal doc As Document)
‘ ドキュメントを開いたときにもイベントハンドラをセットアップする(任意)
‘ ただし、既に開いているドキュメントのイベントを捕捉するには、
‘ SetupDocumentSaveHandler のようにアクティブなドキュメントを明示的にセットする必要がある場合が多い。
‘ より厳密には、VisioのApplication.Documentsコレクションをループして
‘ 各ドキュメントに対してイベントハンドラをセットする必要がある。
‘ ここでは、アクティブなドキュメントを再セットする例を示す。
If ThisDocument.VisioDoc Is Nothing Or ThisDocument.VisioDoc.ID <> doc.ID Then ‘ まだセットアップされていなければ、または開いたドキュメントが違う場合
Set ThisDocument.VisioDoc = doc
Debug.Print “ThisDocument: DocumentOpened – ‘” & doc.Name & “‘ のイベントハンドラをセットアップしました。”
End If
End Sub

Private Sub Document_DocumentSaved(ByVal doc As Document)
‘ ThisDocument モジュールで直接 DocumentSaved イベントを捕捉することも可能ですが、
‘ 複数のドキュメントを扱う場合や、より柔軟な管理のためには、
‘ 標準モジュールで WithEvents を使う方が一般的です。
‘ もしThisDocumentで直接処理する場合は、SetupDocumentSaveHandler のような
‘ 初期化処理は不要になります。
‘ 例:
‘ Dim pdfFilePath As String
‘ Dim originalFileNameWithoutExt As String
‘ originalFileNameWithoutExt = Left(doc.FullName, InStrRev(doc.FullName, “.”) – 1)
‘ pdfFilePath = originalFileNameWithoutExt & “.pdf”
‘ doc.ExportAsFixedFormat …
End Sub

‘ Visioアプリケーション起動時に実行されるプロシージャ(自動実行されないため、手動実行または別の方法で呼び出す必要がある)
‘ Application_Startup イベントなどを利用するのが一般的だが、
‘ VSTOやCOMアドインを使わない限り、直接的な実現は難しい。
‘ ここでは、手動実行を想定した初期化処理の例を示す。
Sub InitializeEventHandlersOnStartup()
‘ アクティブなドキュメントに対してイベントハンドラをセットアップ
SetupDocumentSaveHandler ‘ 標準モジュールにあるプロシージャを呼び出す
End Sub

注: Visio VBAでVisio起動時に自動実行される仕組みは、Excel VBAの`Auto_Open`のような単純なものではありません。VSTO (Visual Studio Tools for Office) やCOMアドインを使用しない場合、Visio起動時に自動でマクロを実行させるのは少し工夫が必要です。簡単なのは、Visioを開いた後に、VBAエディタから `InitializeEventHandlersOnStartup` や `StartPdfGenerationOnSave` を実行する方法です。

特定のフォルダに保存されたらPDFを生成する

`DocumentSaved` イベントの `doc.Path` プロパティなどを利用して、保存先のパスが特定のフォルダだった場合にのみPDFを生成する、といった条件分岐も可能です。

‘ VisioDoc_DocumentSaved プロシージャ内に追加する例

Dim targetFolder As String
targetFolder = “C:\VisioExports\” ‘ PDFを生成したいフォルダパス

If doc.Path = targetFolder Then
‘ ここにPDF生成コードを記述
MsgBox “指定フォルダへの保存のため、PDFを生成します。”, vbInformation
‘ … PDF生成処理 …
Else
Debug.Print “DocumentSaveEventHandler: 指定フォルダ以外への保存のため、PDF生成はスキップします。”
End If

複数のドキュメントのイベントを同時に管理する

より複雑なアプリケーションでは、複数のVisioドキュメントのイベントを同時に管理する必要が出てきます。その場合、`Collection` オブジェクトなどを使って、開いているドキュメントごとに `WithEvents` 変数を管理する、といった高度なテクニックが必要になります。これは、中級者から上級者へのステップアップの際に挑戦してみると良いでしょう。

まとめ:Visio VBAで「自動化」の扉を開こう!

いかがでしたか? `DocumentSaved` イベントをフックしてVSDX保存時にPDFを自動生成する、という一見難しそうな処理も、Visio VBAを使えば意外とシンプルに実現できることがお分かりいただけたかと思います。

今回ご紹介した `WithEvents` キーワードとイベントハンドラの仕組みは、Visio VBAを使いこなす上で非常に強力な武器になります。これをマスターすれば、

  • 図形が追加されたら特定の処理を実行する
  • ドキュメントが印刷されたらログを記録する
  • 保存前に特定のチェックを行う

など、様々な自動化のアイデアが現実のものとなります。

プログラミング初学者の方も、マクロの記録だけでは物足りなくなってきた方も、ぜひこのコードを参考に、ご自身の業務に合わせてカスタマイズしてみてください。「ここをクリアすれば、Visio VBAの基本はバッチリですよ」と、自信を持って言えるようになるはずです。

ぜひ、この「魔法」をあなたのVisioライフに取り入れて、日々の作業をより効率的で、もっと楽しいものにしていきましょう!

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