【テクニカル・上級編】ActiveSelectionRangeオブジェクトを活用した複数選択図形のプロパティ(塗りつぶし・輪郭)一括置換マクロ – CorelDRAW VBA解析バイブル

スポンサーリンク

はじめに:COMインターフェースのボトルネックと「ループ処理の罪」

CorelDRAWのVBA(Visual Basic for Applications)を用いた自動化において、数千から数万に及ぶベクターオブジェクトのプロパティを書き換えるシナリオは日常茶飯事である。しかし、多くの現場で散見されるのは、以下のような「素朴すぎる」ループ処理だ。

‘ 悪しき典型例:反復的なCOM呼び出しとUI描画の頻発
Dim s As Shape
For Each s In ActiveSelection.Shapes
s.Fill.ApplyUniformFill CreateRGBColor(255, 0, 0)
s.Outline.Color.ColorAssign CreateRGBColor(0, 0, 0)
s.Outline.Width = 0.5
Next s

シニアエンジニアであれば、このコードが抱える深刻なアーキテクチャ上の欠陥に即座に気づかねばならない。

VBAからCorelDRAWのオブジェクトモデルを操作する際、プロシージャは内部的にCOM(Component Object Model)の境界を越えてプロセス間/モジュール間通信を行っている。`For Each` ループ内で個々の `Shape` オブジェクトに対してプロパティ変更を呼び出すたびに、以下のオーバーヘッドが発生する。

1. COMバウンダリの大量往復(コンテキストスイッチコスト)
2. CorelDRAWグラフィックエンジンへの頻繁な再描画シグナル送信
3. Undoスタック(元に戻す履歴)の細分化とメモリ圧迫

本稿では、CorelDRAW VBAのオブジェクトモデルの深層に切り込み、`ActiveSelectionRange`(`ShapeRange`)を活用したプロパティの一括置換テクニックと、グラフィックエンジンを極限まで高速化するためのシステムアーキテクチャを解説する。

CorelDRAWにおける Selection と ShapeRange の内部構造

最適化の第一歩は、CorelDRAWが保持するオブジェクトの内部構造を正確に把握することにある。

[Application]

└── [Document]

├── [ActiveSelection] ───> (動的な選択状態ビュー)

└── [ActiveSelectionRange] ───> [ShapeRange オブジェクト]

├── Shape 1
├── Shape 2
└── Shape N

ActiveSelection と ActiveSelectionRange の決定的な違い

  • `ActiveSelection` (Selectionクラス)

現在のUI上でアクティブになっている選択状態そのものを指す。UIのイベント状態やフォーカスに強く依存しており、プロパティ変更のたびに画面更新イベントをトリガーしやすい。

  • `ActiveSelectionRange` (ShapeRangeクラス)

選択中の全シェイプの参照を格納したメモリ上の静的コレクション(配列同等構造)である。`ShapeRange` 自体がプロパティやメソッドを持っており、自身に含まれる全シェイプに対して一括(In-place Batch)で操作を適用する能力を備えている。

`ActiveSelectionRange` を取得して直接操作することは、個別の `Shape` へのCOM参照を削減し、CorelDRAWの内部C++エンジンに対して「一括命令」を叩き込むことを意味する。

究極の高速化アプローチ:ActiveSelectionRangeへの一括バッチ処理

画面描画・Undoグループ・イベントの完全制御

単に `ShapeRange` を使うだけでは不十分だ。CorelDRAWの最適化プロセスには以下の三位一体の設定が不可欠である。

1. `Application.Optimization = True`
ビューポートのリアルタイム描画を停止する。これにより、属性変更に伴うGDI/DirectXの描画パイプラインが完全にブロックされる。
2. `Application.EventsEnabled = False`
ドキュメントやシェイプの変更通知イベント(`Document_SelectionChange` 等)を遮断し、外部プラグインや内部リスナーの不必要な割り込みを防ぐ。
3. `Document.BeginCommandGroup` / `EndCommandGroup`
一連のプロパティ変更を単一のアトミックな操作としてUndoバッファに登録する。Undoスタックの肥大化を防ぎ、メモリ消費を大幅に抑制する。

実践コード:ActiveSelectionRangeプロパティ一括置換モジュール

以下のコードは、選択された全シェイプの「塗りつぶしカラー」および「輪郭線(太さ・カラー)」を一括置換する、プロダクションレベルの最速化済マクロである。

Option Explicit

‘ ==============================================================================
‘ 局所的パフォーマンス計測用 Win32 API
‘ ==============================================================================
Private Declare PtrSafe Function QueryPerformanceCounter Lib “kernel32” (lpPerformanceCount As Currency) As Long
Private Declare PtrSafe Function QueryPerformanceFrequency Lib “kernel32” (lpFrequency As Currency) As Long

‘ ==============================================================================
‘ Function: ApplyPropertiesToSelection
‘ Purpose: ActiveSelectionRangeを利用したプロパティの一括超高速適用
‘ ==============================================================================
Public Sub ApplyPropertiesToSelection()
Dim doc As Document
Set doc = ActiveDocument

‘ 選択範囲の事前検証
If doc Is Nothing Then Exit Sub
If ActiveSelection.Shapes.Count = 0 Then
MsgBox “対象となるシェイプが選択されていません。”, vbExclamation, “処理中断”
Exit Sub
End If

‘ パフォーマンス計測準備
Dim startTime As Currency, endTime As Currency, freq As Currency
QueryPerformanceFrequency freq
QueryPerformanceCounter startTime

‘ ————————————————————————–
‘ 1. アプリケーション状態のフリーズ(最適化フラグのオン)
‘ ————————————————————————–
Dim oldOptimization As Boolean
Dim oldEvents As Boolean

oldOptimization = Application.Optimization
oldEvents = Application.EventsEnabled

Application.Optimization = True
Application.EventsEnabled = False

‘ 単一Undoグループの開始
doc.BeginCommandGroup “Batch Shape Property Apply”

On Error GoTo ErrorHandler

‘ ————————————————————————–
‘ 2. ShapeRangeの取得と一括プロパティ適用
‘ ————————————————————————–
Dim sr As ShapeRange
Set sr = doc.ActiveSelectionRange

‘ 塗りつぶしの一括適用(Uniform Fill)
‘ ※内部C++レイヤーで一括処理されるため、VBA側のループは存在しない
Dim fillColor As Color
Set fillColor = CreateRGBColor(30, 144, 255) ‘ DodgerBlue
sr.Fill.ApplyUniformFill fillColor

‘ 輪郭線(Outline)属性の一括適用
‘ Outlineオブジェクトのプロパティを一括設定
Dim outlineColor As Color
Set outlineColor = CreateRGBColor(15, 15, 15) ‘ Dark Charcoal

‘ SetProperties等を利用した効率的適用、または直接プロパティ設定
sr.SetOutlineProperties _
LineWidth:=0.03937, _ ‘ 1.0mm (インチ単位に換算: 1mm = 0.03937 inch)
Style:=OutlineStyles(0), _
Color:=outlineColor, _
BehindFill:=cdrFalse, _
ScaleWithShape:=cdrTrue

‘ ————————————————————————–
‘ 3. 後処理とクリーンアップ
‘ ————————————————————————–
CleanExit:
‘ Undoグループの終了
doc.EndCommandGroup

‘ アプリケーション状態の復元
Application.EventsEnabled = oldEvents
Application.Optimization = oldOptimization
Application.Refresh

‘ COMオブジェクトの明示的参照解除
Set fillColor = Nothing
Set outlineColor = Nothing
Set sr = Nothing
Set doc = Nothing

QueryPerformanceCounter endTime
Debug.Print “Execution Time: ” & Format$((endTime – startTime) / freq 1000, “0.000”) & ” ms”
Exit Sub

ErrorHandler:
‘ エラー発生時の安全な回復
Dim errDesc As String
errDesc = Err.Description
Resume CleanExit
End Sub

高度なプロパティ一括変換の技術的分析

異種シェイプ混在時の例外処理とプロパティの安全な適用

`ActiveSelectionRange` に対して一括操作を試みる際、選択範囲内に「テキストオブジェクト」「グループオブジェクト」「ビットマップ」「ロックされたシェイプ」が混在している場合、標準的なプロパティ一括変更は例外を投げることがある。

真に堅牢なシステムを構築するには、`ShapeRange` 内から処理可能なシェイプタイプのみを高速にフィルタリングする `Shapes.FindShapes` メソッド または `ShapeRange.Filter` を組み合わせる。

‘ ロックされたシェイプを除外した純粋なベクターオブジェクトのRangeを作成する手法
Public Function GetValidVectorRange(ByRef rawRange As ShapeRange) As ShapeRange
Dim filteredRange As New ShapeRange
Dim s As Shape

‘ Rangeに対する内部フィルタリング(メモリ上のみで高速に判定)
For Each s In rawRange.Shapes
If Not s.Locked Then
Select Case s.Type
Case cdrRectangleShape, cdrEllipseShape, cdrCurveShape, cdrPolygonShape
filteredRange.Add s
Case cdrGroupShape
‘ グループ内部の再帰的抽出が必要な場合はここに記述
filteredRange.Add s
End Select
End If
Next s

Set GetValidVectorRange = filteredRange
End Function

Win32 APIを用いた超高精度パフォーマンス計測と描画ロックの補強

CorelDRAWの `Application.Optimization = True` は強力だが、大規模な描画ツリーの再構成が行われる場合、OSレベルでのウィンドウメッセージの発生を完全に遮断できないケースがある。極限の性能を求めるエンタープライズ環境では、Win32 APIの `LockWindowUpdate` または `SendMessage` による `WM_SETREDRAW` の制御を併用する。

If VBA7 Then
Private Declare PtrSafe Function GetCorelHWND Lib “user32” Alias “FindWindowA” (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr
Private Declare PtrSafe Function LockWindowUpdate Lib “user32” (ByVal hwndLock As LongPtr) As Long
Else
Private Declare Function GetCorelHWND Lib “user32” Alias “FindWindowA” (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function LockWindowUpdate Lib “user32” (ByVal hwndLock As Long) As Long
End If

‘ 画面描画のOSレベル完全ロック
Public Sub DynamicLockUI(ByVal enableLock As Boolean)
Static corelHWND As LongPtr

If enableLock Then
‘ CorelDRAWのメインウィンドウハンドルを取得(バージョン非依存)
corelHWND = Application.AppWindow.Handle
LockWindowUpdate corelHWND
Else
LockWindowUpdate 0
End If
End Sub

結論:スケールするDTP自動化基盤の構築

CorelDRAW VBAにおけるパフォーマンス最適化は、単なる「テクニック」ではなく、COMアーキテクチャとグラフィックエンジンの関係性を理解した上での設計思想(アーキテクチャ)である。

1. `For Each` によるプロパティ変更の反復はアンチパターンである。
2. `ActiveSelectionRange`(`ShapeRange`)のバッチ適用メソッドを活用し、処理をC++コアエンジン側に委譲せよ。
3. `Optimization` / `EventsEnabled` / `CommandGroup` の三位一体による状態制御を徹底せよ。
4. Win32 APIおよび明示的なオブジェクト解放(`Set obj = Nothing`)により、メモリリソースとスレッド応答性を完全に掌握せよ。

数万点のパーツを扱う自動組版システムやパッケージデザインの大量自動生成において、処理時間を数分から数秒へと短縮する鍵は、この「COMバウンダリオーバーヘッドの撲滅」に他ならない。本稿のパターンを基幹モジュールとして標準化し、揺るぎない自動化基盤を構築されたい。

タイトルとURLをコピーしました