Visio VBAを掌握する極限の知見:トランザクション擬似処理による図面の完全防衛設計
こんにちは。大規模な業務自動化プロジェクトをいくつも渡り歩いてきたシニア・アーキテクトの私だ。
Visioを使った業務システムや自動レイアウトツールを開発していると、必ずと言っていいほど直面する悪夢がある。それは、「数千個のシェイプを処理するマクロの途中でエラーが発生し、中途半端に書き換わったゴミ図面が保存されてしまう」という現象だ。
「エラー処理で `MsgBox` を出して終わり」にしていないか?
実務の現場において、不完全な状態で破損した図面データほどタチの悪いものはない。ユーザーは泣く。そしてあなたのコードは信頼を失う。
今回は、Visio VBAにおいて「アトミック(不可分)なトランザクション処理」を実現し、例外発生時に確実に図面を原状回復(ロールバック)させるための極限の知見を授けよう。
—
なぜ素のVisio VBAは危険なのか?
Visioのオブジェクトモデルは、ExcelやWordに比べて独特の重みを持っている。特に `Shape` の生成、接続(Glue)、パラメータ書き換えは、実行のたびに描画エンジンとUndoスタックに負荷をかける。
ここで多くの開発者が犯す致命的なミスがこれだ。
‘ 【アンチパターン】絶対にやってはいけない実装
Sub BadBatchProcess()
Dim shp As Shape
‘ 1000個のシェイプを処理するループ
For i = 1 to 1000
‘ ここでオーバーフローや型不一致などの予期せぬエラーが起きたら…
Set shp = ActivePage.DrawRectangle(0, 0, i, i)
shp.Text = “Data ” & i
Next i
End Sub
このコードの何が問題か。ループの500回目でエラーが起きた瞬間、1から499個までの「中途半端な変更」が図面に残ったままマクロが停止する。ユーザーがこれを上書き保存でもしようものなら、二度と元のデータには戻らない。
業務アプリケーションにおいて、「途中までうまくいきました」という言い訳は通用しない。「全て成功するか、1つも変更されないか(All or Nothing)」の保証、すなわちトランザクションが不可欠なのだ。
—
Visioにおけるトランザクションの正体:`Application.Undo`
幸いなことに、Visioのアプリケーションスコープには強力なUndo機能が備わっている。
Visioの操作は、VBAから実行したものであっても、内部で「スコープ(Undoスコープ)」としてスタックに積まれている。
しかし、VBAからこれを完全にコントロールするためには、以下の2つの壁を突破しなければならない。
1. エラー発生時に自動でUndoが発動する仕組みがない
2. 複数の処理を1つの「不可分な操作(トランザクション単位)」としてVisioに認識させる必要がある
これらを解決するのが、今回紹介する「Undoスコープ監視型・安全トランザクション設計パターン」だ。
—
プロダクションコード:安全なトランザクション基盤
実務の現場でそのまま使える、堅牢なテンプレートコードを提示しよう。
エラーハンドリング、トランザクションの開始とコミット、そして異常時の自動ロールバックを完璧にカプセル化している。
このコードをそのまま標準モジュールに貼り付けてほしい。
Option Explicit
‘ ==============================================================================
| 模範的モジュール名: 業務自動化トランザクション制御基盤
| 概要: Visioの変更処理をアトミックに実行し、例外時に完全なロールバックを保証する
==============================================================================
Public Sub ExecuteTransactionalProcess()
Dim lScopeID As Long
Dim blnSuccess As Boolean
blnSuccess = False
‘ 画面描画を停止し、パフォーマンスを極限まで高める(お約束の最適化)
Application.ScreenUpdating = False
Application.ShowAlerts = False
‘ 1. Undoスコープの開始(ここからがトランザクションの境界)
‘ 引数には、VisioのUndo履歴に表示される名前を指定する
lScopeID = Application.BeginUndoScope(“一括図形生成トランザクション”)
On Error GoTo ErrorHandler
‘ ==========================================================================
‘ 【ビジネスロジック記述エリア】
‘ この中に、実際のシェイプ生成やDB連携からのデータ流し込みを記述する
‘ ==========================================================================
Call BusinessLogicCoreRoutine
‘ ==========================================================================
‘ 正常終了フラグを立てる
blnSuccess = True
ErrorHandler:
If Not blnSuccess Then
‘ 異常終了時: エラー内容を保持したままトランザクションを終了し、即座にUndoを実行
Dim lErrNum As Long: lErrNum = Err.Number
String lErrDesc As String: lErrDesc = Err.Description
‘ Undoスコープを閉じる(この時点ではまだロールバックは確定していない)
Application.EndUndoScope lScopeID, False ‘ 2番目の引数 ‘False’ が肝!
‘ 画面描画を復元
Application.ScreenUpdating = True
Application.ShowAlerts = True
‘ ユーザーに致命的エラーを通知し、図面が保護されたことを伝える
MsgBox “処理中に重大なエラーが発生しました。” & vbCrLf & _
“図面は変更前の状態に完全に復元(ロールバック)されました。” & vbCrLf & _
“エラー詳細: ” & lErrDesc, vbCritical, “トランザクション異常終了”
Exit Sub
End If
‘ 正常終了時: スコープをコミット(確定)する
Application.EndUndoScope lScopeID, True ‘ 2番目の引数 ‘True’ で変更を確定
‘ 画面描画を復元
Application.ScreenUpdating = True
Application.ShowAlerts = True
MsgBox “すべての処理が正常に完了し、データベースと同期されました。”, vbInformation, “完了”
End Sub
‘ 実際のビジネスロジックをシミュレートするコアプロシージャ
Private Sub BusinessLogicCoreRoutine()
Dim pg As Page
Set pg = ActivePage
Dim shp As Shape
Dim i As Long
‘ わざとエラーを起こすテストをしたい場合は、i = 50 あたりで例外を発生させよ
For i = 1 To 100
‘ 矩形を描画
Set shp = pg.DrawRectangle(i 0.5, i 0.5, (i 0.5) + 1, (i 0.5) + 1)
shp.Text = “Node_” & i
‘ 【シミュレーション用】50個目で強制エラー発生
If i = 50 Then
Err.Raise 9999, “BusinessLogic”, “意図的なシミュレーション例外の発生”
End If
Next i
End Sub
—
匠のコード解説:なぜこの設計なのか?
このコードには、数々の修羅場をくぐり抜けてきたアーキテクトの知見が凝縮されている。ポイントを解説しよう。
1. `Application.BeginUndoScope` と `EndUndoScope` の真価
VisioのAPIには、トランザクション専用の命令は存在しない。その代わりにあるのが Undoスコープ(変更履歴の括り) だ。
`BeginUndoScope` を呼び出した瞬間から、Visioはその後のオブジェクト操作を一つの「パッケージ」として記録し始める。
そして、`EndUndoScope lScopeID, [CommitFlag]` の第2引数に `False` を渡すことで、「このスコープ内の操作はなかったことにする(すなわち完全なUndo)」という極めて強力なロールバック命令が実行される。
2. 画面描画の凍結(`ScreenUpdating = False`)との共存
大量のシェイプを扱う自動化では、描画の都度CPUとメモリが消費されるため、`ScreenUpdating = False` は必須だ。
しかし、エラーハンドラーを通る経路であっても、必ず描画設定を元の `True` に戻すことを忘れてはならない。これを怠ると、Visioがフリーズしたような見た目のままバックグラウンドで動き続ける最悪のUXを生む。コード内の構造化されたエラーハンドリングは、この復元処理を確実に担保している。
3. ファイルや外部データベース連携時の注意点
実務では、Visioの図面変更と「外部データベース(SQL ServerやAccessなど)への書き込み」を同時に行うことが多い。
ここで注意すべきは、「VisioのUndoはVisioの図面しか元に戻せない」という点だ。
もし外部DBへの書き込みが成功した後にVisio側でエラーが起きた場合、Visioはロールバックされるが、DB側のデータは進んだままになり、システム間に致命的な整合性崩壊(データ不整合)が起きる。
堅牢なシステムを構築する場合の鉄則:
1. フェーズ1(データ取得・検証): 外部DBからデータを取得し、Visio上で処理可能か事前バリデーションを行う(この時点ではVisioを汚さない)。
2. フェーズ2(Visioトランザクション): 本稿で紹介した `BeginUndoScope` を用いて図面を一気に描画する。
3. フェーズ3(外部DB更新): Visioの描画が完全に成功したことを確認してから、外部DBのコミットを行う。
この順序を守ることで、Visio図面と外部リソースの整合性を美しく保つことができる。
—
まとめ:プロフェッショナルなVBA開発者であれ
動くだけのコードを書くのは素人でもできる。しかし、「異常系を制する者が、真の業務自動化を制する」。
今回紹介した「Undoスコープ監視型トランザクション設計」を取り入れることで、あなたの作るVisioマクロは、単なる「動くスクリプト」から、商用システムに匹敵する「堅牢な業務アプリケーション」へと昇華する。
現場のエンジニアとして、妥協のない安全設計を貫いてほしい。健闘を祈る。
