【CorelDRAW VBAを掌握する極限の知見】第1回:SelectionManagerの深淵と「未選択」という名の罠
CorelDRAW VBAの自動化において、最も頻発し、かつ最も開発者を絶望させるエラーは何か。それは、オブジェクトが選択されていない状態で `.Selection` プロパティや `ActiveSelectionRange` を叩いた瞬間に発生する、ランタイムエラーの嵐である。
素人の書いたコードは、ユーザーが何かを選択しているという「性善説」の上に成り立っている。しかし、現場のオペレーターは、何もないキャンバスの状態でショートカットキーを叩き、マクロを暴走させ、未保存のデータを吹き飛ばす天才たちだ。
シニアエンジニアたる者、ユーザーの「うっかり」をシステムのエラーとして返してはならない。今回は、`SelectionManager` のライフサイクルとオブジェクトモデルの挙動を完全に掌握し、極限まで堅牢な例外ハンドリングとユーザー誘導を実装するための知見を授ける。
—
1. CorelDRAWオブジェクトモデルにおける「選択」の正体
多くのVBAエンジニアは、選択状態を取得する際に `ActiveDocument.Selection` や `ActiveSelection` を安易に使用する。だが、これらはCorelDRAWの内部コンテキスト(ActiveContext)に強く依存しており、ドキュメントの構造変化やウィンドウのフォーカスロストによって、しばしば不整合を起こす。
真に堅牢なシステムを構築するためには、`Document.SelectionManager` を明示的に経由しなければならない。
`SelectionManager` の基本とパフォーマンスの罠
`SelectionManager` は、現在のドキュメントにおける選択状態を管理する高レベルインターフェースである。ここで注意すべきは、`SelectionManager.Count` や `Selection.Shapes.Count` を呼び出す際のメモリフットプリントとCOM境界のコストだ。
ループ内で幾度も選択カウントを参照するような愚行は、COMオブジェクトのMarshal(マーシャリング)オーバーヘッドを増大させ、パフォーマンスを致命的に低下させる。判定は必要最小限の回数にとどめ、取得した参照はローカル変数にキャッシュするのが鉄則である。
—
2. 実装:未選択時の例外ハンドリングとスマートなユーザー誘導
以下に、実務の現場で即座に採用できる、極限まで最適化されたVBAコードを示す。このコードでは、単にエラーを回避するだけでなく、ユーザーを迷わせないためのUI誘導と、メモリリークを防ぐためのオブジェクト解放(`Set … = Nothing`)を完璧に両立させている。
Option Explicit
‘ ==============================================================================
‘ 処理名: 堅牢な選択オブジェクト処理のテンプレート
‘ 概要: ユーザーの未選択状態を安全にハンドリングし、作業効率を阻害しない
‘ スマートなメッセージングと適切なメモリ解放を行う実戦的コード。
‘ ==============================================================================
Sub SafeExecuteWithSelectionCheck()
Dim doc As Document
Set doc = ActiveDocument
‘ ドキュメントが開かれていない場合のガード(システム連携時の基本)
If doc Is Nothing Then
MsgBox “アクティブなドキュメントが存在しません。” & vbCrLf & _
“処理を中止します。”, vbCritical, “CorelDRAW Automation System”
Exit Sub
End If
Dim selMgr As SelectionManager
Set selMgr = doc.SelectionManager
‘ 【極限の知見】: SelectionManager.Count または Shapes.Count による判定
‘ CorelDRAWのバージョン差異(X8から2024以降まで)を吸収するため、
‘ SelectionRangeの存在とCountプロパティを厳密に評価する。
If selMgr.Shapes.Count = 0 Then
‘ ———————————————————————-
‘ 未選択時のユーザー誘導(UXの最適化)
‘ 単に「エラーです」と突き放すのではなく、次に取るべき行動を提示する
‘ ———————————————————————-
Dim userAction As VbMsgBoxResult
userAction = MsgBox(“オブジェクトが選択されていません。” & vbCrLf & _
“処理を実行するには、対象となるシェイプを選択してください。” & vbCrLf & vbCrLf & _
“【操作ガイド】” & vbCrLf & _
“・[はい] を押すと、全選択(Ctrl+A)を試行して処理を続行します。” & vbCrLf & _
“・[いいえ] を押すと、マクロを安全に終了します。”, _
vbQuestion + vbYesNo + vbDefaultButton2, _
“選択オブジェクト未検出 – ガードシステム”)
If userAction = vbYes Then
‘ ユーザーの利便性を考慮し、全選択を代行するフォールバック処理
doc.ActiveLayer.Shapes.All.CreateSelection
‘ 再度カウントを評価
Set selMgr = doc.SelectionManager
If selMgr.Shapes.Count = 0 Then
MsgBox “キャンバス上に有効なシェイプが存在しません。”, vbExclamation, “処理中止”
GoTo CleanUp
End If
Else
‘ 安全な離脱
GoTo CleanUp
End If
End If
‘ ==========================================================================
‘ メイン処理の実行フェーズ
‘ ==========================================================================
ProcessSelectedShapes selMgr.Shapes
CleanUp:
‘ ————————————————————————–
‘ オブジェクトの明示的解放(メモリ最適化)
‘ VBScript / VBA のガベージコレクションに依存せず、COM参照を即座に破棄する
‘ ————————————————————————–
Set selMgr = Nothing
Set doc = Nothing
End Sub
‘ 実際のビジネスロジックをカプセル化するプロシージャ
Private Sub ProcessSelectedShapes(ByVal targetShapes As ShapeRange)
‘ 選択されたシェイプに対する高負荷な処理をここに記述
Dim sh As Shape
‘ 画面描画の凍結によるパフォーマンス爆発的向上
ActiveDocument.BeginCommandGroup “Batch Process”
Optimization = True
For Each sh in targetShapes
‘ 例: 選択された全オブジェクトの輪郭線を赤色に変更する等の処理
sh.Outline.Color.RGBAXColorAssign 255, 0, 0, 0
Next sh
Optimization = False
ActiveDocument.EndCommandGroup
MsgBox targetShapes.Count & ” 個のオブジェクトを正常に処理しました。”, vbInformation, “完了”
End Sub
—
3. シニアエンジニアが押さえておくべきアーキテクチャ上の注意点
1. ガベージコレクションとCOM参照のライフサイクル
VBAのランタイムは、プロセス終了時までCOMオブジェクトの参照を保持し続けることがある。特に `ActiveDocument` や `SelectionManager` のようなグローバルなエントリーポイントをローカル変数に代入して使い捨てにする場合、参照カウントが適切にデクリメントされないと、CorelDRAWのプロセス内にメモリリークが蓄積する。
コードの最後で `Set … = Nothing` を徹底することは、数百・数千回とマクロを回す印刷現場や自動化サーバー(COM Automation経由での外部制御)において、プロセスのクラッシュを防ぐ唯一の防壁となる。
2. `Optimization = True` との組み合わせによる例外時の罠
パフォーマンスを極限まで高めるために `Optimization = True`(画面描画やアンドゥのバッファリングを停止するモード)を多用するが、このモードが有効な間に予期せぬエラーや `Exit Sub` が発生すると、CorelDRAWのUIがロックされたままフリーズするという致命的な不具合を引き起こす。
本記事で提示したコードのように、終了処理(`CleanUp:` ラベル)を必ず経由する構造(GoToパターン、またはエラーハンドラの実装)にすることで、環境の破壊を未然に防がなければならない。
—
総括
たかが「選択されていない」という単純な条件分岐。されど、そこにはCorelDRAWのオブジェクトモデル、COMのメモリ管理、そして現場のオペレーターの心理学が凝縮されている。
小手先のコード記述から脱却し、インフラストラクチャとしての強靭さを備えたマクロを構築すること。それこそが、真のCorelDRAW自動化エンジニアの務めである。
