Visioを殺すな:大規模VSDXのPDF出力におけるメモリリークとの決別
多くのエンジニアがVisio VBAの自動化で壁に突き当たるのは、数千ページに及ぶ帳票や複雑なダイアグラムを連続処理する際、数時間でシステムがフリーズ、あるいはOut of Memoryエラーで強制終了する現象だ。
VisioはOfficeスイートの中でも特に「COMオブジェクトの生存管理」が複雑なアプリケーションである。VBAを書く多くの者が「`Set obj = Nothing` を呼べば大丈夫」と信じているが、それは幻想に過ぎない。本稿では、レガシーとモダンが交差する現場で生き残るための、極限のリソース管理手法を解き明かす。
—
1. なぜVisioのメモリは解放されないのか
Visioはバックグラウンドで隠れたCOMオブジェクトを生成し続ける。特に `ExportAsFixedFormat` を多用するPDF出力処理では、レンダリングエンジンがメモリ上に残留しやすく、ガベージコレクションが追いつかない。
単なる `Nothing` 代入では、親プロセスが子プロセスを掴んだままとなり、参照カウントがゼロにならない。これを防ぐには、「オブジェクトの階層を意識した順序立てた解放」と、必要に応じた 「Visioプロセスそのものの再起動」 を戦略的に組み込む必要がある。
—
2. 実践:メモリをクリーンに保つための設計指針
大規模バッチ処理を安定稼働させるためのアーキテクチャは以下の3点に集約される。
1. オブジェクトのスコープ限定: ループ内でオブジェクトを生成せず、可能な限り共通化する。
2. 明示的なプロセス監視: Windows APIを使い、メモリ消費量を監視して閾値を超えたらVisioを再起動する。
3. PDF出力後のコンテキスト・フラッシュ: 出力後にドキュメントを即座に閉じ、メモリキャッシュをクリアする。
実装例:リソース解放を徹底したPDFエクスポート関数
‘ メモリリークを最小化するためのPDF出力ルーチン
Public Sub ExportToPDF_Optimized(ByVal filePath As String, ByVal pdfPath As String)
Dim visApp As Visio.Application
Dim visDoc As Visio.Document
‘ Visioインスタンスを取得(または新規生成)
Set visApp = New Visio.Application
visApp.Visible = False
On Error GoTo Cleanup
Set visDoc = visApp.Documents.Open(filePath)
‘ PDF出力 – 非常にメモリを食う処理
visDoc.ExportAsFixedFormat visFixedFormatPDF, pdfPath, , , , , , , , , False
Cleanup:
‘ 逆順で解放を行うのが鉄則
If Not visDoc Is Nothing Then
visDoc.Close SaveChanges:=False
Set visDoc = Nothing
End If
‘ Visioアプリの終了
If Not visApp Is Nothing Then
visApp.Quit
Set visApp = Nothing
End If
‘ 最後にメモリの断片化をOSに強制通知(推奨はしないが、極限環境では有効)
DoEvents
End Sub
—
3. Windows APIによるプロセス生存監視
長時間稼働のバッチでは、数千ファイルに一度、どうしても解放しきれない「お化けプロセス」が生まれる。これを放置するとメモリが枯渇する。シニアエンジニアは、Visioに頼らずプロセス自体を管理する。
プロセス監視と強制終了のロジック
VBAから `TaskKill` を叩くのは乱暴だが、安定稼働が最優先の業務システムでは「死んだら蘇らせる」のが鉄則だ。
‘ 実行中のVisioプロセスを強制終了するコマンド
Public Sub KillVisioProcess()
Dim shell As Object
Set shell = CreateObject(“WScript.Shell”)
‘ メモリリークが疑われる場合、容赦なくVisioプロセスを全殺しする
shell.Run “taskkill /F /IM visio.exe”, 0, True
End Sub
これをバッチのループカウンタに組み込み、例えば「50ファイル処理するごとにプロセスを再起動する」という「ターンアラウンド管理」を実装せよ。これが大規模処理を完遂させる唯一の現実解である。
—
4. チーフアーキテクトからの助言
Visio VBAで最も陥りやすい罠は、「イベントハンドラの残留」だ。`Application` オブジェクトに対して `WithEvents` を使用している場合、そのオブジェクトを `Nothing` にしてもイベントリスナーが残存し、メモリリークの温床となる。
- 鉄則: `WithEvents` を使用したモジュールでは、必ず終了処理として `Set obj = Nothing` を明示し、かつ可能であればクラス自体を破壊する設計にすること。
- レガシーの知恵: 大規模PDF生成を行う場合、VisioのUIスレッドに負荷をかけないよう、必ず `Visible = False` で動作させ、かつOSの `Sleep` 関数(Windows API)を挿入して、レンダリングが終わるまでの「余白」をCPUに与えること。
技術とは、単にコードを書くことではなく、マシンの挙動とリソースの制約を完全に掌握することだ。Visioという巨大で不器用なCOMの塊を扱うなら、我々はそれ以上に冷静で、徹底した管理者でなければならない。
成功を祈る。
