こんにちは!Visio VBAの自動化の世界へようこそ。
マクロの記録ボタンを押してコードを生成するフェーズを卒業し、いよいよ「現場で本当に使える堅牢なシステム」を作ろうとしているあなたへ、最高にエキサイティングな知見をお届けします。
今回は、実務の現場で意外と多くのエンジニアを悩ませる「PDF出力時のファイルロック問題(排他制御)」に焦点を当てます。
「プログラムでVisio図面をPDF化して共有フォルダに保存したいのに、誰かがそのPDFを開きっぱなしにしていてエラーで止まった……」
こんな経験はありませんか?
今回は、エラーをただ放置するのではなく、「相手がファイルを閉じるのをスマートに待ち、自動でリトライするタフなエラーハンドリング」を実装する方法を、優しく、そして徹底的に解説していきます。ここをクリアすれば、あなたのVBAスキルは間違いなくプロの領域に到達しますよ!
—
なぜPDF出力で「ファイルが使われています」エラーが起きるのか?
VisioのVBAからPDFを出力するには、主に `Document.ExportAsFixedFormat` メソッドを使用します。非常に便利な機能ですが、裏側のOS(Windows)の挙動を考えてみてください。
PDF出力先(例: `C:\Reports\Status.pdf`)にすでに同名のファイルが存在し、かつ、別のユーザーや他のアプリケーション(Adobe Readerやブラウザなど)がそのファイルを開いている状態だと、Windowsはセキュリティとデータ破損防止のために「書き込み禁止(ファイルロック)」を発動します。
この状態でVBAから強制的に上書き保存を試みると、容赦なく「実行時エラー: 5(プロシージャの呼び出しまたは引数が不正です)」や「ファイル アクセスが拒否されました」といったエラーが発生し、マクロが強制終了してしまいます。
夜間のバッチ処理や、大量の図面を一括変換している最中にこれで止まったら……絶望的ですよね。だからこそ、「ロックされていたら、少し待ってからもう一度トライする(リトライ機構)」が必要不可欠なのです。
—
堅牢な排他制御の全体像
今回目指すのは、以下のようなスマートなフローです。
1. PDFのエクスポートを試みる。
2. もしファイルロック関連のエラーが発生したら、即座に諦めずエラー番号をチェックする。
3. 指定した回数(例: 5回)に達するまで、数秒間(例: 3秒間)処理を一時停止(スリープ)する。
4. ファイルが解放されたら、何事もなかったかのように保存を完了させる。
これを実現するために、VBAの `On Error` 構文と、Windows APIの `Sleep` 関数(またはVBA標準の `Timer` 関数)を組み合わせます。今回は、コードの美しさと移植性を考慮し、VBAだけでスマートに待機時間を実現するテクニックを使いましょう。
—
実装コード:リトライ機能付きPDFエクスポートプロシージャ
以下のコードを、あなたのVisioVBAエディタ(標準モジュール)にそのまま貼り付けてみてください。実務でそのまま使えるレベルのコメントを盛り込んでいます。
Option Explicit
‘ メイン処理:安全なPDFエクスポート
Sub ExportVisioToPDF_WithRetry()
Dim targetDoc As Document
Dim pdfPath As String
Dim success As Boolean
‘ アクティブなドキュメントを対象とする
Set targetDoc = ActiveDocument
‘ 保存先のパス(必要に応じて変更してください)
pdfPath = “C:\Work\NetworkDiagram_Output.pdf”
‘ ドキュメントが保存されていてパスがあるか確認
If targetDoc.Path = “” Then
MsgBox “この図面はまだ一度も保存されていません。一度保存してから実行してください。”, vbExclamation, “警告”
Exit Sub
End If
‘ リトライロジックを実行
success = ExportWithRetry(targetDoc, pdfPath, 5, 3) ‘ 最大5回リトライ、間隔3秒
If success Then
MsgBox “PDFのエクスポートに成功しました!” & vbCrLf & pdfPath, vbInformation, “完了”
Else
MsgBox “PDFのエクスポートに失敗しました。” & vbCrLf & _
“出力先のファイルが他のユーザーまたはアプリケーショに占有されている可能性があります。”, vbCritical, “エラー”
End If
End Sub
‘ リトライ制御を行うコア関数
‘ 戻り値: True (成功), False (失敗)
Function ExportWithRetry(doc As Document, filePath As String, maxRetries As Long, waitSeconds As Long) As Boolean
Dim currentTry As Long
Dim errNum As Long
currentTry = 0
ExportWithRetry = False ‘ 初期値は失敗に設定
Do While currentTry < maxRetries currentTry = currentTry + 1 ' エラーハンドリングを有効化 On Error GoTo ErrorHandler ' --- VisioのPDFエクスポート実行 --- ' VisioFixedFormatTypes.visFixedFormatPDF = 1 doc.ExportAsFixedFormat visFixedFormatPDF, filePath, _ visIntentPrint, visPrintAllPages, , , , False, False, False ' ここまで来たらエラーなし(成功) ExportWithRetry = True On Error GoTo 0 ' エラートラップ解除 Exit Function ErrorHandler: errNum = Err.Number On Error GoTo 0 ' エラーリセット ' 【重要】ファイルロックやアクセス権エラーに該当するか判定 ' 70: 書き込み権限がない / 5: プロシージャの呼び出し不正(ファイル使用中等) If errNum = 70 Or errNum = 5 Then If currentTry < maxRetries Then ' ログ出力やイミディエイトウインド우への通知 Debug.Print "【試行 " & currentTry & " 回目】PDFが使用中です。「" & waitSeconds & "秒」後に再試行します..." ' 指定秒数待機するサブルーチンを呼び出し Call WaitSeconds(waitSeconds) Else ' 最大試行回数オーバー Debug.Print "【エラー】最大リトライ回数に達しました。処理を中断します。" End If Else ' ファイルロック以外の予期せぬエラーの場合は、即座にループを抜けて失敗とする Debug.Print "【致命的エラー】予期せぬエラーが発生しました (Error " & errNum & ": " & Err.Description & ")" Exit Do 5 End If Loop End Function ' 指定した秒数だけ処理を一時停止するヘルパー関数 (VBA標準のTimerを使用) Sub WaitSeconds(sec As Long) Dim startTime As Single startTime = Timer Do While Timer < startTime + sec ' 画面がフリーズしないようにイベントに処理を返す DoEvents ' 日付を跨いだ場合の対策(深夜0時付近での無限ループ防止) If Timer < startTime Then startTime = Timer End If Loop End Sub ---
コードのここがスゴい!エンジニアのこだわりポイント
1. `DoEvents` を入れたスマートな待機 (`WaitSeconds`)
APIの `Sleep` 関数を使うとWindows全体の動きが止まってしまいますが、`Timer` と `DoEvents` を組み合わせたこの待機手法なら、「待機中もVisioがフリーズせず、ユーザーがキャンセルしたり画面を動かしたりできる」優れものです。深夜0時を跨いだときのタイマーリセット対策も完璧に行っています。
2. エラー番号の厳密な選別
すべてのエラーを一律でリトライさせるのは素人プログラミングの証拠です。今回は「ファイルが使用中・アクセス拒否」を意味するエラー(`Err.Number` が 70 や 5 など)の時だけリトライし、ファイルが存在しないなどの致命的な別エラーは即座に弾く設計にしています。
3. イミディエイトウインドウへのログ出力
裏で何回リトライが行われているかが `Debug.Print` でVBEのイミディエイトウインドウに出力されるため、開発時のデバッグが非常にスムーズに行えます。
—
まとめ:ここをクリアすれば、Visio VBAの基本はバッチリ!
今回は、ワンランク上のVisio VBAエンジニアを目指すための「PDF出力時の排他制御(リトライ処理)」について解説しました。
- 単にコードを書くだけでなく、「外部環境(ファイルが開かれている状況)の変化に耐えられるか?」を考慮することが、プログラミングの醍醐味であり、実務で信頼されるシステムを作る極意です。
このコードをあなたの業務ツールに組み込めば、「またファイルが開いててマクロが止まったんだけど!」という社内からのクレームを綺麗にゼロにすることができますよ。
ぜひ、あなたの開発現場で試してみてください。応援しています!
