CorelDRAW VBAを掌握する極限の知見:重たいブール演算バッチ処理を最速化するアーキテクチャ
開発現場のリーダーである私のもとに、よくこんな悲鳴が届く。
「数百個のデザインデータに対して `Weld` や `Trim` を連続実行したら、CorelDRAWがフリーズした」
「最初は動くのに、処理が進むにつれてどんどんメモリリークを起こし、最終的にOut of Memoryで落ちる」
無理もない。CorelDRAWのブール演算(溶接・トリム・結合)は、裏側で複雑な幾何学的トポロジーの再計算を行っている。これを素朴なループで回すのは、「目隠しをしたまま時速150キロで山道を走る」ようなものだ。
今回は、実務の現場で絶対に破綻しない、堅牢かつ極限まで最適化されたベクター一括変形バッチ処理のアーキテクチャを伝授する。
—
1. なぜ「素朴なブール演算ループ」は破綻するのか?
多くの初中級者が書くコードはこうだ:
‘ 【アンチパターン】絶対にやってはいけない実装
Dim sh As Shape
For Each sh In ActivePage.Shapes
‘ 選択して溶接を繰り返す
sh.CreateSelection
ActiveDocument.Weld sh, otherSh
Next sh
このアプローチが地獄行きになる理由は3つある。
1. 画面描画(UI Redraw)のオーバーヘッド:
CorelDRAWは、メソッドが呼ばれるたびにビューポートの再描画を試みる。これがバッチ処理の速度を数倍〜数十倍に引き下げる。
2. 選択セマンティクス(Selection Dependency)への依存:
`ActiveSelection` や `ActiveShape` を介した操作は、VBAとCorelDRAWのCOMコンポーネント間で無駄なマーシャリングが発生し、非常に重い。
3. オブジェクト参照のロスト(Stale References):
ブール演算を実行した瞬間、元のシェイプはメモリ上で破棄され、新しい結合シェイプが生成される。ループ変数や古いポインタを参照し続けた結果、ランタイムエラーやクラッシュを引き起こす。
—
2. 堅牢なバッチ処理を構築する3大鉄則
プロのエンジニアが現場で実装する際は、以下の3つを厳守する。
鉄則①:イベントと画面描画の完全な遮断
処理の開始前には必ず `Optimization = True` を宣言し、画面の更新とイベント通知を殺す。これだけで速度が文字通り桁違いになる。
鉄則②:セレクションレス(非選択)でのオブジェクト操作
UIを触らせず、メモリ上の `ShapeRange` に対して直接メソッドを叩く。`.CreateSelection` はバッチ処理においては「悪」だ。
鉄則③:エラーハンドリングと参照のクレンジング
幾何学演算は、パスの自己交差(Self-Intersection)や不正なノード構造データによって失敗する。1つのファイルのエラーでバッチ全体を止めない「防御的プログラミング」が必須となる。
—
3. 【プロダクションコード】極限最適化されたブール演算バッチ処理
以下のコードは、指定フォルダ内の全CorelDRAWファイル(CDR)を読み込み、ページ内の全ベクターシェイプに対して安全かつ高速に「溶接(Weld)」をかけ、別名で出力する実務対応スクリプトである。
Option Explicit
‘ ==============================================================================
‘ 処理名: 複数ドキュメント一括ブール演算(溶接)バッチプロセッサ
‘ 開発者: チーフアーキテクト
‘ 概要: UI描画を完全に抑制し、メモリリークとクラッシュを防ぐ安全な一括処理
‘ ==============================================================================
Sub BatchWeldProcessor()
Dim targetFolder As String
targetFolder = “C:\DesignData\Input\” ‘ 処理対象フォルダ
Dim outputFolder As String
outputFolder = “C:\DesignData\Output\” ‘ 出力先フォルダ
‘ フォルダ存在チェック
If Dir(targetFolder, vbDirectory) = “” Then
MsgBox “入力フォルダが存在しません: ” & targetFolder, vbCritical
Exit Sub
End If
‘ — 【鉄則①】パフォーマンスと安定性のための環境設定 —
EventsEnabled = False
CorelDRAW.Optimization = True
ActiveDocument.BeginCommandGroup “Batch Weld Operation”
On Error GoTo ErrorHandler
Dim fileName As String
fileName = Dir(targetFolder & “.cdr”)
Dim doc As Document
Dim processedCount As Long
processedCount = 0
Do While fileName <> “”
‘ ドキュメントを開く(UI非表示)
Set doc = CorelDRAW.OpenDocument(targetFolder & fileName)
‘ 各ページ、各レイヤーのシェイプを走査して安全に溶接を実行
Call ProcessDocumentGeometry(doc)
‘ 結果を別名で保存して閉じる
doc.SaveAs outputFolder & “Processed_” & fileName
doc.Close
processedCount = processedCount + 1
fileName = Dir() ‘ 次のファイルへ
Loop
‘ — 終了処理 —
ActiveDocument.EndCommandGroup
CorelDRAW.Optimization = False
EventsEnabled = True
MsgBox “バッチ処理が正常に完了しました。処理件数: ” & processedCount & ” ファイル”, vbInformation
Exit Sub
ErrorHandler:
‘ 異常終了時のフェイルセーフ
ActiveDocument.EndCommandGroup
CorelDRAW.Optimization = False
EventsEnabled = True
MsgBox “致命的なエラーが発生しました (Error ” & Err.Number & “): ” & Err.Description, vbCritical
End Sub
‘ ==============================================================================
‘ ドキュメント内の幾何学演算を安全に実行するサブ関数
‘ ==============================================================================
Private Sub ProcessDocumentGeometry(ByRef targetDoc As Document)
Dim p As Page
Dim sr As ShapeRange
Dim baseShape As Shape
Dim targetShape As Shape
For Each p in targetDoc.Pages
p.Activate
‘ ページ内の全シェイプをShapeRangeとして取得(セレクションを使わない)
Set sr = p.Shapes.All
‘ シェイプが2つ以上存在する場合のみブール演算を検討
If sr.Count >= 20 Then
‘ ここでは例として、レンジ内の最初のシェイプをベースに順次溶接するロジック
‘ ※実際の業務要件(レイヤー構造や重なり判定)に合わせてフィルタリングを推奨
Set baseShape = sr(1)
Dim i As Long
For i = 2 To sr.Count
Set targetShape = sr(i)
‘ ==================================================================
‘ 【最重要】幾何学エラーへの備え
‘ パスの構造によってはWeldが失敗するため、インラインエラー対策を行う
‘ ==================================================================
On Error Resume Next
‘ Weld実行: baseShapeにtargetShapeを溶接
‘ 返り値として新しい結合シェイプが返る
Dim weldedShape As Shape
Set weldedShape = baseShape.Weld(targetShape, False, False)
If Err.Number = 0 And Not weldedShape Is Nothing Then
Set baseShape = weldedShape ‘ 次の演算のベースを更新
Else
‘ エラー時はログを残しつつスキップ(バッチ全体を止めない)
Debug.Print “Weld failed on shape ID: ” & targetShape.StaticID & ” – ” & Err.Description
End If
On Error GoTo 0 ‘ エラー捕捉をリセット
Next i
End If
Next p
End Sub
—
4. コードの解説:なぜこの設計が「最強」なのか
1. `CorelDRAW.Optimization = True` の魔力
ループの頭でこれを宣言すると、CorelDRAWは画面の再描画、ドッキングウィンドウの更新、アンドゥバッファの動的構築を一時停止する。数千回の幾何学演算を伴うバッチ処理では、処理時間が1/10以下に短縮されるケースも珍しくない。
2. オブジェクトの生存管理(ライフサイクルの把握)
`Weld` メソッドを実行すると、引数に渡したシェイプはメモリ上から消滅する。素朴なコードだと、消滅したポインタを再度操作しようとして「Object variable or With block variable not set」エラーが起きる。
上記のコードでは、`Weld` の戻り値(新しく生成された `weldedShape`)を次のベースシェイプに正しくスワップし続けているため、ポインタ切れを起こさない。
3. `On Error Resume Next` による局所的防御
ベクターデータの不備(交差するパス、極端に小さなセグメントなど)は、どれほど綺麗に設計されたコードでも外部要因でブール演算エラーを引き起こす。
バッチ処理において「1つの汚いデータのせいで全100ファイルの処理が止まる」のは絶対に許されない。エラーが起きうる幾何学演算のラインだけを局所的にトラップし、失敗したシェイプを安全にスキップする設計がプロの条件だ。
—
5. チーフアーキテクトからの実践的アドバイス
データベースや外部CSV(Excel等)と連携して「どのファイルをどう処理するか」を制御する場合、ファイルパスのリストを一度 `Scripting.Dictionary` や配列に読み込み、CorelDRAWのドキュメントを開閉するメモリサイクルを管理してほしい。
CorelDRAWのVBAエンジンは、長時間の連続稼働でわずかにメモリ断片化を起こすことがある。もし数千ファイル規模のモンスター級のバッチを回す場合は、一定数(例: 50ファイルごと)処理したタイミングで一度CorelDRAWのプロセスを再起動するような外側からのタスクランナー設計(VBScriptやPowerShellからの制御)を組み合わせると、完全に鉄壁のシステムが完成する。
現場の生産性を極限まで高めるのは、小手先のテクニックではなく、こうした「オブジェクトのライフサイクルとパフォーマンスの重みを知り尽くした設計」だ。ぜひ、あなたの開発現場でもこのアーキテクチャを取り入れてほしい。
