Visio VBAの深淵:共有ファイルにおける「読み取り専用」排他制御の極意
多くのエンジニアがVisio VBAの自動化において躓くのが、ネットワーク共有フォルダ上のVSDXファイルのハンドリングだ。複数ユーザーによる同時アクセスは、容易に「ファイルが使用中です」という排他エラーや、ゾンビ化したロックファイルを生成する。
これは単なるVBAのコード記述の問題ではない。Windowsのファイルシステム、Visioのオブジェクトモデル、そしてメモリ管理という、エンジニアが避けて通れない「重厚な現実」の境界線に位置する問題だ。
本稿では、レガシーな環境下でも安定して動作し、システムのリソースを食いつぶさないための「極限の排他制御」を伝授する。
—
1. OpenExメソッドの「真の力」を解放せよ
多くのエンジニアは単純に `Documents.Open` を使用するが、共有フォルダにおいては脆弱だ。我々が頼るべきは `Documents.OpenEx` である。
このメソッドには、Visioの挙動を低レイヤーから制御する強力なフラグが存在する。特に重要なのが `visOpenRO`(読み取り専用)と `visOpenNoWorkspace`(ワークスペースを復元しない)の組み合わせだ。
実装コード:堅牢なファイルオープン制御
‘ ネットワーク上の共有ファイルを安全に開くためのラッパー
Public Function OpenSharedDocument(ByVal filePath As String) As Visio.Document
Dim vsoDoc As Visio.Document
‘ エラーハンドリングはVBAの生命線。ファイルシステム起因の例外を捕捉する
On Error Resume Next
‘ visOpenRO: 読み取り専用で開く
‘ visOpenNoWorkspace: 既存のワークスペース設定を無視し、クリーンな状態で開く
‘ 複数ユーザー環境では必須のフラグ構成
Set vsoDoc = Application.Documents.OpenEx(filePath, visOpenRO Or visOpenNoWorkspace)
If Err.Number <> 0 Then
‘ ログ出力と適切な例外処理をここに記述
Debug.Print “Error ” & Err.Number & “: ” & Err.Description
Set OpenSharedDocument = Nothing
Else
Set OpenSharedDocument = vsoDoc
End If
On Error GoTo 0
End Function
—
2. メモリ最適化:オブジェクトの「死」を管理する
VBAのガーベジコレクションは信用してはならない。特にVisioのように巨大なDOM(Document Object Model)を構築するアプリケーションでは、明示的な解放を怠れば、数時間の運用でメモリリークが発生し、ExcelやVisioプロセスが突然死する。
確実なオブジェクト解放の作法
‘ 処理終了時には必ずオブジェクトをNothingに設定する
‘ 複雑なネスト構造を持つVisioオブジェクトでは、下層から上層へ向かって解放するのが鉄則
Public Sub SafeCloseDocument(ByRef vsoDoc As Visio.Document)
If Not vsoDoc Is Nothing Then
‘ 保存せずに強制的に閉じる(読み取り専用で開いた場合)
vsoDoc.Close
Set vsoDoc = Nothing
End If
‘ 最後にApplicationオブジェクトの解放も考慮すべき場合がある
End Sub
—
3. レガシー環境における「ロック」の予兆を察知する
共有フォルダの悲劇は、ファイルが「読み取り専用」で開かれているにもかかわらず、OS側でロックが維持され続ける現象だ。これを検知するためには、Windows API `_lopen` (または `CreateFile`)を呼び出し、共有モードでファイルを開けるかどうかを事前にテストする手法がある。
しかし、現代的なアーキテクチャでは、「ファイルを開いてエラーが出るなら、それは仕様」と割り切り、リトライロジックを実装する方が保守性は高い。
指数バックオフによるリトライ処理
‘ 単純なループではなく、待機時間を指数的に増やすことでサーバー負荷を軽減する
Public Function OpenWithRetry(ByVal path As String, ByVal retries As Integer) As Visio.Document
Dim i As Integer
Dim doc As Visio.Document
For i = 1 To retries
Set doc = OpenSharedDocument(path)
If Not doc Is Nothing Then
Set OpenWithRetry = doc
Exit Function
End If
‘ 待機(ミリ秒単位で負荷を分散)
Application.Wait (Now + TimeValue(“00:00:02”))
Next i
End Function
—
4. チーフアーキテクトからの提言
Visio VBAを用いて共有ファイルに挑む際、最も避けるべきは「手動操作と自動化ツールの混在」だ。
1. 強制的な読み取り専用化: 自動化ツールが書き込み権限で開く必要があるなら、ファイルサーバー側で「共有編集」を禁止し、排他権をプログラムのみに限定させる設計が望ましい。
2. プロセス監視: `TaskKill` を利用したゾンビプロセスの強制終了をバッチに組み込むのは最終手段だが、それが必要なアーキテクチャ自体を見直すべきだ。
エンジニアよ、コードを書く前に「そのファイルは誰が所有しているのか」を問い続けよ。システムは、あなたが定義したルールよりも、OSが強制する制約に忠実である。
この技術的洞察が、あなたのシステムの安定性を一段高めることを願っている。現場の泥臭い課題こそが、真のエンジニアを鍛え上げるのだ。
