【テクニカル・上級編】【初心者向け】開いている全てのVisioドキュメントをループ処理で一括保存し、メモリを解放する手順 – Visio VBA解析バイブル

スポンサーリンク

Visio VBAを掌握する極限の知見:全ドキュメント一括保存とメモリ解放の鉄則

Visio VBAにおける最大の罠は、「オブジェクトを開いたままにする怠慢」と「見せかけだけのクローズ処理」にある。

多くの開発者は、`ActiveDocument` という甘美なワードに依存し、複数開かれた図面のコンテキストを見失う。特に、社内システムや自動化バッチの一部としてVisioをバックグラウンド稼働させる場合、ドキュメントのライフサイクル管理の不手際は、そのままVBAランタイムのメモリリーク、ひいてはOLEオートメーションの暴走(ゾンビプロセスの生成)へと直結する。

今回は、`Documents`コレクションを正確に走査し、未保存の変更を検知し、安全に永続化(Save)した上で、メモリ空間から完全にパージするプロフェッショナルなルーチンを解説する。

—

1. Visioドキュメント管理におけるアーキテクチャ上の核心

Visioのオブジェクトモデルにおいて、`Application.Documents` コレクションはすべての開かれているVSDXファイルを統括する根幹である。ここをループ処理する際、以下の3つの鉄則を破ってはならない。

1. 逆順ループ(Decrement Loop)の原則
コレクション内のドキュメントを「閉じる(Close)」操作を行う場合、先頭から順に処理(`For i = 1 To Documents.Count`)してはならない。要素が削除されるたびにインデックスがずれるためだ。必ず末尾から先頭に向かって処理する(`For i = Documents.Count To 1 Step -1`)。
2. 新規未保存ドキュメント(Untitled)のハンドリング
一度も保存されていないドキュメントは `Path` プロパティが空文字を返す。これをそのまま上書き保存しようとするとエラーが発生するため、専用のパス指定(あるいは名前を付けて保存ダイアログ、または自動命名)が必要となる。
3. 完全なオブジェクトの解放
VBAのガベージコレクションは気まぐれだ。参照を切ったつもりでも、背後でCOMの参照カウンタが残ることがある。ローカル変数としてドキュメントを取得した場合は、明示的に `Nothing` を代入してメモリプレッシャーを軽減する。

—

2. 実装コード:全ドキュメント一括安全保存・クローズルーチン

以下のコードは、現在Visioアプリケーションで開かれているすべてのドキュメントを走査し、変更があれば保存し、メモリを解放しながら安全に閉じるプロダクション品質のマクロである。

Option Explicit

Public Sub SaveAndCloseAllVisioDocuments()
‘ —————————————————————-
‘ 処理名: 全Visioドキュメント一括安全保存・クローズルーチン
‘ 概要 : 開いている全ドキュメントの保存状態を確認・保存し、
‘ メモリリークを防ぎながら確実にクローズする。
‘ —————————————————————-

Dim docCount As Long
Dim i As Long
Dim targetDoc As Visio.Document
Dim savePath As String

‘ 画面描画と警告を抑制し、処理速度を極限まで高める
Application.ScreenUpdating = False
Application.DisplayAlerts = False

On Error GoTo ErrorHandler

docCount = Application.Documents.Count

If docCount = 0 Then
MsgBox “現在開かれているドキュメントはありません。”, vbInformation, “一括処理”
GoTo Finally
End If

‘ 【重要】コレクション操作時の鉄則:末尾から先頭へ逆順ループを回す
For i = docCount To 1 Step -1
Set targetDoc = Application.Documents(i)

‘ ステンシル(Stencil)やVisio内部ドキュメントを除外する場合の判定
‘ (必要に応じて Type == visTypeStencil などを弾く)
If targetDoc.Type = visTypeDrawing Then

‘ 変更されている、または未保存の場合のみ処理
If targetDoc.Saved = False Then

‘ パスが存在するか(新規ファイルか既存ファイルか)の判定
If targetDoc.Path = “” Then
‘ 【新規未保存ファイルの場合】
‘ 本番運用ではここで専用フォルダへの自動保存パスを構築することを推奨
‘ 例: ThisDrawing.Path & “\AutoSave_” & Format(Now, “yyyymmdd_hhmmss”) & “.vsdx”
‘ 今回は安全のため、既定のドキュメントフォルダに仮保存する例とする
savePath = CreateObject(“WScript.Shell”).SpecialFolders(“MyDocuments”) & “\AutoSaved_” & targetDoc.Name & “.vsdx”
targetDoc.SaveAs savePath
Else
‘ 【既存ファイルの場合】
targetDoc.Save
End If

End If

‘ ドキュメントを閉じる
targetDoc.Close

End If

‘ オブジェクト変数の明示的解放(メモリ最適化)
Set targetDoc = Nothing
Next i

MsgBox “すべてのドキュメントの保存とクローズが正常に完了しました。”, vbInformation, “処理完了”

Finally:
‘ 抑制したアプリケーション設定を必ず復元する
Application.ScreenUpdating = True
Application.DisplayAlerts = True
Exit Sub

ErrorHandler:
‘ 異常系ハンドリング:ログ出力や緊急の復旧処理をここに記述
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “致命的エラー”
Resume Finally

End Sub

—

3. チーフアーキテクトが指摘する「現場の罠」と最適化の極意

なぜ `Application.ScreenUpdating = False` が不可欠なのか?

複数のVisio図面がアクティブな状態で、それらを次々と閉じたり保存したりすると、VisioのUIスレッドが図面の切り替え描画(ウィンドウフォーカスの移動)を必死に追従しようとする。これがパフォーマンスを著しく低下させるだけでなく、OS側のウインドウハンドル(HWND)枯渇を引き起こす主原因となる。描画抑制はパフォーマンス上のテクニックではなく、システム安定稼働のための防衛策である。

ステンシル(`.vssx` / `.vss`)への配慮

上記のコードでは `targetDoc.Type = visTypeDrawing` で図面ファイルに絞っているが、もしカスタムステンシルを開いている場合、ステンシル自体に変更が加わっていると `targetDoc.Save` でダイアログがポップアップするリスクがある。完全な自動化(ヘッドレス実行)を目指すのであれば、ステンシルの読み取り専用判定や保存除外ロジックを厳密に組み込むべきだ。

COMインターフェースの解放とガベージコレクション

VBAはVB.NETのような明示的な `Dispose` パターンを持たないが、「オブジェクト変数に `Nothing` を代入する」という作法を怠ると、VBE(Visual Basic Environment)のメモリ空間にCOMラッパーが残り続ける。特にマクロを何度も繰り返し実行する検証フェーズや、ExcelからVisioを `CreateObject` 経由で操作する外部連携アーキテクチャにおいては、このメモリリークが積もり積もってExcel/Visioプロセスのフリーズを引き起こす。

—

総括

Visio VBAにおけるドキュメント制御は、単なるメソッドの呼び出しではない。アプリケーションのライフサイクル、OSのリソース、そしてメモリの境界を完全に掌握した上でのみ成り立つ厳格なエンジニアリングである。

「動けばいい」という妥協を捨て、逆順ループの徹底、新規ファイルのパス検証、そして確実なオブジェクト解放を実装に組み込むこと。それこそが、何千回実行してもビクともしない、真に堅牢なVisio自動化システムを構築唯一の道である。

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