こんにちは! Visio VBAの自動化の世界へようこそ。
マクロの記録ボタンを押すだけのステージから一歩進んで、「自分の手でVisioを自在にコントロールしたい」と願うあなたへ、今日はとても実践的で、かつ現場で必ず直面する「大きな壁」を突破する技術をお伝えします。
ここをクリアすれば、Visio VBAの挙動の裏側にある「Visioの呼吸(イベントループ)」が手に取るようにわかるようになりますよ。しっかりついてきてくださいね!
—
巨大なVSDXの保存でVisioが「フリーズ」する絶望感
何百枚ものシェイプが緻密に配置された巨大なネットワーク図や、複雑なプラント図。その労作をVBAで「さあ、保存しよう!」と `ActiveDocument.Save` を実行した瞬間……。
画面が真っ白になり、タイトルバーには「応答なし」の文字。
「あぁ、またフリーズした……。クラッシュして今日の作業が消えたかもしれない」と、冷や汗をかいた経験はありませんか?
Visioは非常に高機能なグラフィックエンジンを持っているがゆえに、ファイルサイズが大きくなると、保存処理の間、画面の描画やユーザーからの入力を完全にシャットダウンしてしまいます。シングルスレッドの宿命ですね。
ユーザーは「止まっているのか、動いているのか」すら分からず、イライラしてタスクマネージャーから強制終了してしまう……。これをスマートに解決するのが、今回テーマにする「DoEventsによる非同期的な制御と進捗(プログレスバー)の表示」です。
—
そもそも `DoEvents` って何をしているの?
プログラミング初学者のうちは、「魔法の呪文」のように見えがちな `DoEvents` 関数。
これの本質は、一言で言うと「Visioに深呼吸をさせる命令」です。
VBAが重い処理(ファイル保存や巨大ループ)を始めると、Visioは他のすべての作業を放棄してその計算に没頭します。そのため、Windowsからの「生きてる?」というシグナル(メッセージ)を無視し続け、結果として「応答なし」と判定されます。
そこで、コードの合間に `DoEvents` を挟むと、VBAは一時的に処理の手を止め、Windowsにコントロールを返します。
これにより、
- ユーザーが動かしたマウスの動きを検知する
- 「キャンセル」ボタンが押されていないか確認する
- 画面のプログレスバーの表示を新しく書き換える
といった「裏で行うべき仕事」を処理する余裕が生まれるのです。
—
実践! ユーザーフォームを使った「フリーズしない保存」の実装
百聞は一見に如かず。実際に動くコードを見ていきましょう。
ここでは、以下の2ステップで実装します。
1. 進捗状況を表示するユーザーフォーム(`frmProgress`)の作成
2. 重い保存処理と `DoEvents` を組み合わせた標準モジュールの記述
ステップ1:ユーザーフォームの準備
VBE(Visual Basic Editor)を開き、新しいユーザーフォーム(名前:`frmProgress`)を追加してください。
- フォームの中に `Label` コントロールを1つ置き、名前を `lblStatus` にします(進捗メッセージ用)。
- もう一つ、横長の `Label`(または `Frame`)を置き、名前を `barProgress` にします(これが進捗バーの代わりになります)。
ステップ2:標準モジュールにコードを書く
以下のコードを標準モジュールに貼り付けてください。
Option Explicit
‘ ==============================================================================
‘ 巨大VSDXファイルの安全な保存と、進捗表示を行うプロシージャ
‘ ==============================================================================
Sub SaveDocumentSafelyWithProgress()
Dim targetDoc As Document
Set targetDoc = ActiveDocument
‘ 新規保存などでファイル名がまだない場合のガード
If targetDoc.Path = “” Then
MsgBox “一度手動で名前を付けて保存してから実行してください。”, vbExclamation, “保存エラー”
Exit Sub
End If
‘ 1. 進捗表示用フォームをモードレス(画面を固まらせない形式)で表示
Dim progForm As frmProgress
Set progForm = New frmProgress
progForm.Show vbModeless
progForm.lblStatus.Caption = “保存の準備を行っています…”
progForm.barProgress.Width = 0
‘ 2. 画面の自動更新を停止(描画コストをカットして高速化)
Application.ScreenUpdating = False
On Error GoTo ErrorHandler
‘ — 擬似的に進捗を刻みながら保存処理を行うステップ —
‘ ※Visioの標準Saveメソッドは一瞬で実行されるため、大きな図面での
‘ 「データ整理・最適化」のフェーズを模したループと組み合わせます。
progForm.lblStatus.Caption = “図面データを最適化中… (1/3)”
progForm.barProgress.Width = 50 ‘ 50ピクセル進むイメージ
DoEvents ‘ ← ここでVisioに呼吸をさせ、「応答なし」を防ぐ!
‘ (もしここで独自の複雑なシェイプ操作やデータ処理があれば挟む)
progForm.lblStatus.Caption = “ディスクへの書き込み中… (2/3)”
progForm.barProgress.Width = 120
DoEvents
‘ 実際の保存実行
targetDoc.Save
progForm.lblStatus.Caption = “保存を完了しています… (3/3)”
progForm.barProgress.Width = 200
DoEvents
‘ わずかに完了の余韻を見せるためのウェイト(必要に応じて)
Dim startTime As Double
startTime = Timer
Do While Timer < startTime + 0.5
DoEvents
Loop
' 後片付け
Unload progForm
Application.ScreenUpdating = True
MsgBox "図面の保存が正常に完了しました!", vbInformation, "完了"
Exit Sub
ErrorHandler:
' エラー時の後片付け
On Error Resume Next
Unload progForm
Application.ScreenUpdating = True
MsgBox "保存中にエラーが発生しました: " & Err.Description, vbCritical, "エラー"
End Sub
---
ここでエンジニアとして伝授したい「重要なお約束」
上記のコードで、`DoEvents` と並んで非常に重要な役割を果たしているのが、この行です。
Application.ScreenUpdating = False
Visioは、VBAからシェイプが1つ変更されるたび、あるいはプロパティが書き換わるたびに、「画面を再描画せねば!」と裏で必死にグラフィックボードを叩きます。これがマクロを劇的に遅くする最大の原因です。
「重い処理の直前で画面更新を止め、終わったら `True` に戻す」。これはプロのVisio VBAプログラマにとって常識中の常識です。
⚠️ 注意点:`DoEvents` の魔力に溺れるな
`DoEvents` は救世主ですが、諸刃の剣でもあります。
`DoEvents` を挟むと、ユーザーが再びVisioを操作できるようになります。つまり、「保存処理の真っ最中に、ユーザーが別のシェイプを削除したり、もう一度ボタンを押してしまうリスク」が生まれます。
これを防ぐために、実務では以下のような対策を組み合わせます。
- 処理中はフォーム以外の操作を受け付けない工夫をする
- 2重クリックを防ぐためのフラグ変数(`isSaving As Boolean` など)を用意する
—
まとめ:ここをクリアすればVisio VBAは怖くない!
今回は、VSDX保存時のフリーズを防ぐための `DoEvents` と進捗表示のテクニックを解説しました。
- 「応答なし」の正体は、VisioがCPUを独占してWindowsの声を聞けなくなっている状態。
- `DoEvents` を挟むことで、Visioに「深呼吸(イベント処理)」をさせ、フリーズを防ぐ。
- `Application.ScreenUpdating = False` との合わせ技で、高速かつスマートなUIを実現する。
このパターンをマスターすれば、ただ動くだけのプログラムから、「ユーザーに優しい、プロフェッショナルなツール」へとあなたのVBAスキルは大きく飛躍します。
ここをクリアできれば、Visio VBAの基本構造はもうバッチリです!
ぜひ、あなたの現場の巨大な図面ファイルで試してみてくださいね。次回の解説もお楽しみに!
