CorelDRAW VBAを掌握する:巨大バッチ処理におけるCOMメモリリークの「完全撲滅」論
CorelDRAW VBAで何千枚ものファイルを処理する際、多くのエンジニアが「300枚を超えたあたりでCorelDRAWが突如としてフリーズする」という悪夢に直面する。
断言しよう。それはCorelDRAWのバグではない。君の書いたコードによる「COM参照の放置」が引き起こしたメモリの飽和だ。
VBAのガベージコレクション(GC)は、お世辞にも賢いとは言えない。特にCorelDRAWのオブジェクトモデルは、階層が深く、明示的に解放しなければバックグラウンドでメモリ上に「ゾンビオブジェクト」が蓄積され続ける。本稿では、数千枚規模のファイル処理を完遂させるための、極限のメモリ管理術を伝授する。
—
1. 悲劇を招く「参照の連鎖」を断ち切る
VBAにおいて、`Document` や `Layer`、`Shape` オブジェクトを安易に変数に代入し、そのまま放置するのは自殺行為だ。以下のコードは、典型的なメモリリークの温床である。
‘ 【NGコード】リークを誘発する典型的な書き方
Dim doc As Document
Set doc = Application.OpenDocument(path)
‘ この後、docをNothingにせず、次のループへ進むとメモリが開放されない
鉄則:スコープの局所化と明示的破棄
ループ内でのインスタンス生成は、必ず「小さなスコープ」に閉じ込め、処理が終わるたびに強制解放せよ。
—
2. 実践:巨大バッチ処理を完遂するプロダクション・コード
このコードは、ファイルを開き、加工し、エクスポートして閉じるまでのサイクルを、メモリリークを最小限に抑える設計で実装している。
Public Sub BatchProcessFiles(ByVal folderPath As String)
Dim fso As Object
Dim folder As Object
Dim file As Object
Dim doc As Document
Set fso = CreateObject(“Scripting.FileSystemObject”)
Set folder = fso.GetFolder(folderPath)
For Each file In folder.Files
If LCase(fso.GetExtensionName(file.Name)) = “cdr” Then
‘ プロセスの安全性を担保するため、エラーハンドリングを必須とする
On Error Resume Next
Set doc = Application.OpenDocument(file.Path)
If Err.Number = 0 Then
‘ — ここで加工処理を行う —
Call ProcessDocument(doc)
‘ 保存して閉じる
doc.SaveAs file.Path & “_processed.cdr”
doc.Close
End If
‘ 【超重要】明示的なメモリ解放
Set doc = Nothing
‘ GCを促すためにDoEventsを挟む(非常に重要)
DoEvents
On Error GoTo 0
End If
Next file
‘ 最後にオブジェクトを綺麗に掃除
Set folder = Nothing
Set fso = Nothing
End Sub
Private Sub ProcessDocument(ByRef doc As Document)
‘ 複雑な形状処理は別関数に分け、参照を局所化する
Dim shp As Shape
For Each shp In doc.ActivePage.Shapes
‘ 加工ロジック
Next shp
‘ ここでshpはループの終了とともにスコープを抜けるが、
‘ 大規模な処理の場合はSet shp = Nothingをループ内に書くべき
End Sub
—
3. なぜこれで安定するのか?
① `DoEvents` の真の役割
VBAはシングルスレッドで動いている。`DoEvents` を呼ぶことで、Windowsのメッセージキューを処理させ、CorelDRAWが溜め込んだバックグラウンド処理(オブジェクトの破棄など)にCPUの時間を一時的に割かせることができる。これがないと、CorelDRAWは「処理しきれない」と判断し、メモリを抱え込んだまま停止する。
② 明示的な `Set = Nothing` の必要性
VBAの参照カウント方式は、変数がスコープから外れても、メモリ上のオブジェクトが「使われている」と誤認されることが多い。特に `Application` や `Document` オブジェクトは、明示的に `Nothing` を代入して参照カウントをゼロにしない限り、ヒープ領域に鎮座し続ける。
③ 巨大処理中の「定期再起動」
どれほど完璧にコードを書いても、CorelDRAW自体のメモリ管理には限界がある。500ファイルに一度、`Application.Quit` し、タスクスケジューラでバッチ処理を再起動させるような「外側からの制御」を設計に組み込むのが、真のプロフェッショナルの仕事だ。
—
結論:コードは「芸術」ではなく「運用」である
君が書くコードは、単なる機能実装ではない。CorelDRAWという巨大なCOMサーバーを操るための「指揮権」だ。
メモリを解放せず、ゾンビオブジェクトを放置することは、システムの崩壊を招く。
- スコープを最小化せよ。
- ループの最後には必ず `Nothing` を代入せよ。
- `DoEvents` でOSに息を継がせろ。
この作法を守れば、何千枚のファイルも君の思いのままだ。健闘を祈る。
