Visio VBAの「守り」の技術:共有フォルダの排他制御を制し、堅牢な保存処理を実装する
Visio VBAで自動化ツールを開発する際、多くのエンジニアが「ファイル保存」の泥沼に足を取られます。特に共有サーバー上のファイルを扱う際、誰かが開いていることによる「読み取り専用」状態への考慮漏れは、致命的な実行時エラーを引き起こします。
今回は、単に保存するだけのコードではなく、「ファイルの状態を事前検知し、ユーザーに賢明な判断を促す」ためのプロフェッショナルな実装手法を伝授します。
—
1. なぜ「そのまま保存」してはいけないのか
Visioがファイルを保存する際、`ActiveDocument.Save` を呼ぶだけでは不十分です。ファイルシステムが「読み取り専用」とマークしている場合、VBAは容赦なく「実行時エラー 1004」を吐き出し、最悪の場合、ユーザーの未保存データがメモリ上で浮遊することになります。
エンジニアが避けるべき「素人設計」:
- エラーハンドリングを `On Error Resume Next` で強引に無視する。
- 保存できなかった事実を無視してプログラムを続行する。
- ユーザーに「保存できませんでした」という冷淡なエラーメッセージだけを出す。
真の自動化ツールは、「保存できないなら、どこに逃がすべきか」までをユーザーと対話しながら解決する設計であるべきです。
—
2. 堅牢な保存処理のアーキテクチャ
以下のコードは、単なる保存命令ではなく、ファイルの「属性」と「排他状態」をチェックする防衛的なプロシージャです。
実装:堅牢な保存制御モジュール
Option Explicit
‘ ————————————————————————-
‘ 目的: ファイルの排他状態を考慮した安全な保存処理
‘ ————————————————————————-
Public Sub SafeSaveDocument(ByVal doc As Visio.Document)
Dim savePath As String
savePath = doc.Path & doc.Name
‘ 1. ファイルの読み取り専用属性をチェック
If GetAttr(savePath) And vbReadOnly Then
MsgBox “このファイルは現在「読み取り専用」です。” & vbCrLf & _
“別名保存を行うか、現在開いているユーザーが閉じるのを待ってください。”, _
vbCritical, “保存エラー:排他ロック”
‘ 別名保存ダイアログを強制的に表示
Call doc.SaveAsEx(doc.Path & “Backup_” & Format(Now, “HHMMSS”) & “.vsdx”, _
Visio.VisSaveAsOptions.visSaveAsList)
Exit Sub
End If
‘ 2. 正常保存の試行
On Error GoTo ErrorHandler
doc.Save
Exit Sub
ErrorHandler:
MsgBox “保存処理中に予期せぬエラーが発生しました: ” & Err.Description, vbCritical
End Sub
このコードの「設計思想」
1. `GetAttr` 関数による先読み: APIを叩く前に、OSレベルでファイルの属性(`vbReadOnly`)を確認します。これにより、Visioの保存エンジンが悲鳴を上げる前に制御を奪い返せます。
2. `SaveAsEx` の活用: 単なる `SaveAs` ではなく、オプション引数を持つ `SaveAsEx` を使用することで、保存時の挙動(ダイアログの表示など)を細かく制御可能です。
3. ユーザーへの道筋提示: エラーで止めるのではなく、「バックアップとして保存する」というリカバリー策をコード内で提示しています。これがツール利用者の信頼を得るポイントです。
—
3. さらに一歩先へ:PDF出力における注意点
Visio VBAでPDF出力を行う際は、さらに注意が必要です。PDF出力は「ファイル」ではなく「エクスポート」のプロセスであるため、保存時とは異なるエラーハンドリングが求められます。
- PDF出力時の罠: PDFの出力先が「読み取り専用」フォルダや、既にPDFが開かれている(Adobe Reader等でロックされている)場合、エラーが発生します。
- 対策: PDF出力前に、出力先パスに書き込みが可能かを確認する小関数を挟むのが定石です。
‘ 出力先が書き込み可能か判定するヘルパー関数
Private Function IsFileWritable(ByVal filePath As String) As Boolean
Dim fileNum As Integer
On Error Resume Next
fileNum = FreeFile
Open filePath For Append As #fileNum
Close #fileNum
IsFileWritable = (Err.Number = 0)
On Error GoTo 0
End Function
—
4. プロの視点:アーキテクトからのアドバイス
実務で自動化ツールを作る際、最も重要なのは「コードの美しさ」ではなく、「ツールが壊れた時に誰が責任を取るか(ユーザーがどうリカバリーできるか)」です。
- ログを残す: エラーが発生した際、どのパスで、どんなエラーが出たかをテキストファイルに書き出す「ログ出力」を必ず実装してください。
- 共有フォルダの特性を理解する: ネットワーク遅延による「見かけ上の読み取り専用」が発生することもあります。保存直前に `DoEvents` を挟み、Visioの描画やファイルI/Oの完了を待つ余裕を持たせることも、大規模環境では有効なテクニックです。
Visio VBAは古い言語だと言われますが、制御さえ握ってしまえば、これほど強力なオートメーションツールはありません。今日のコードを組み込み、貴方の自動化ツールを「動くもの」から「止まらないもの」へと進化させてください。
健闘を祈ります。
