CorelDRAW VBA:数千ページのPDF出力でメモリ枯渇(OOM)を回避する「メモリ・マネジメントの極意」
CorelDRAWのVBA開発において、数千ページ規模のドキュメントを扱う際、多くのエンジニアが直面するのが「謎のフリーズ」と「メモリ不足(Out of Memory)」である。
CorelDRAWは優れた描画エンジンを持つが、VBAの背後にあるOLEオートメーションの層は、長時間の連続稼働に対して極めて脆弱だ。特にPDFエクスポート時にオブジェクトを生成し続け、適切に破棄しないコードは、Windowsのメモリ空間をじわじわと侵食し、最終的にアクセス違反でクラッシュする。
本稿では、数千ページを安定して処理するための、メモリを物理的に解放し、プロセスを「リフレッシュ」し続けるアーキテクチャについて論じる。
—
1. なぜ「`Set obj = Nothing`」だけでは足りないのか
VBAのガーベッジコレクションを信じてはいけない。VBAにおける`Nothing`は、あくまで「参照カウントを減らす」という合図に過ぎない。CorelDRAWの内部オブジェクト、特に`Page`や`Layer`、`ShapeRange`の背後にあるネイティブなメモリ領域は、COMサーバー側が即座に回収してくれるとは限らないのだ。
この問題に対する正解は、「メモリ管理の単位を小さくし、プロセスを強制的に休ませる」ことにある。
2. 極限のメモリ管理:ページ分割と強制リフレッシュ
数千ページを一気に処理するのではなく、例えば「50ページごと」にドキュメントを分割・保存し、メモリを強制的に掃き出す戦略を採る。
実践コード:メモリ効率を最大化するページ処理テンプレート
‘ メモリ解放を意識したバッチ処理の核
Sub ProcessLargeDocument()
Dim i As Long, batchSize As Long
batchSize = 50
For i = 1 To ActiveDocument.Pages.Count Step batchSize
‘ 特定のページ範囲を新規ドキュメントへコピーして処理
Call ProcessBatch(i, batchSize)
‘ 強制的なイベントループとメモリ解放
DoEvents
‘ Windows APIによるメモリ最適化(後述)
Call ForceGC
Next i
End Sub
Private Sub ForceGC()
‘ Windows APIを利用してプロセス自身のメモリ利用量をOSに返還させる
Dim procId As Long
procId = GetCurrentProcess()
SetProcessWorkingSetSize procId, -1, -1
End Sub
3. Windows APIによるメモリ最適化の真髄
VBAの`ForceGC`としてよく紹介される `SetProcessWorkingSetSize`。これは「プロセスがOSから確保しているワーキングセットを最小化する」という劇薬だ。これを定期的に呼び出すことで、CorelDRAWが溜め込んだ「再利用可能なのに解放されていないキャッシュ」をOSに返還させることができる。
If VBA7 Then
Private Declare PtrSafe Function SetProcessWorkingSetSize Lib “kernel32” (ByVal hProcess As LongPtr, ByVal dwMinimumWorkingSetSize As LongPtr, ByVal dwMaximumWorkingSetSize As LongPtr) As Long
Private Declare PtrSafe Function GetCurrentProcess Lib “kernel32” () As LongPtr
Else
Private Declare Function SetProcessWorkingSetSize Lib “kernel32” (ByVal hProcess As Long, ByVal dwMinimumWorkingSetSize As Long, ByVal dwMaximumWorkingSetSize As Long) As Long
Private Declare Function GetCurrentProcess Lib “kernel32” () As Long
End If
警告: このAPIをループのたびに実行するのはオーバーヘッドが大きすぎる。50〜100ページ程度の節目に限定して呼ぶのが、パフォーマンスと安定性の最適解である。
4. オブジェクトのライフサイクルを完結させるための掟
- `ShapeRange`の再利用禁止: `ShapeRange`は非常に強力だが、ループ内で頻繁に生成するとメモリリークの温床になる。処理が終わった直後に必ず `Set sr = Nothing` を実行せよ。
- イベントの停止: 大量処理中は `Application.Optimization = True` を使用し、画面描画とイベント通知を完全に停止せよ。これにより、CorelDRAWの内部UI更新によるメモリ浪費を90%以上カットできる。
- ドキュメントの「使い捨て」: 数千ページある場合、同一ファイルを編集し続けるのではなく、必要なページを抽出した一時ドキュメントを作成し、出力後は即座に `Close` せよ。
5. チーフアーキテクトからの忠告
CorelDRAWの自動化において、最も美しいコードとは「何もしないコード」である。しかし、どうしても数千ページのPDFが必要な場合、それはもはやVBAの範疇を超えた「システム」であると認識すべきだ。
もし上記を行ってもなおメモリが枯渇するなら、それはVBAの限界ではなく、CorelDRAWそのもののリソース管理の限界だ。その際は、VBAでドキュメントを分割するだけに留め、PDF変換はコマンドラインツールや外部のサーバーサイドPDFライブラリに切り出すという決断を恐れてはならない。
技術とは、ツールを盲信することではなく、その限界を正確に把握し、その外側を設計することにある。諸君のシステムが、堅牢に動作することを期待する。
