【テクニカル・上級編】SelectionManager徹底解説:オブジェクト未選択時の例外ハンドリングとユーザー誘導UIの設計 – CorelDRAW VBA解析バイブル

スポンサーリンク

SelectionManager徹底解説:オブジェクト未選択時の例外ハンドリングとユーザー誘導UIの設計

CorelDRAW VBAの現場において、最も頻発し、かつ最も開発者を絶望させるエラーの源泉をご存知だろうか。
それはランタイムエラーでもメモリリークでもない。「ユーザーが何も選択していない状態でマクロを実行したことによる、`Selection`プロパティの無慈悲な暴走」である。

素人が書いたコードは、オブジェクトが選択されていない状態で `ActiveSelection.Shapes.Count` やプロパティにアクセスした瞬間、容赦なく実行時エラーを吐いてクラッシュする。あるいは、エラートラップのつもりで `On Error Resume Next` を仕込み、そのまま後続の処理でヌルポインタ例外を引き起こす。

我々シニアエンジニアに求められるのは、単なる「エラー回避の `If` 文」ではない。CorelDRAWのオブジェクトモデルの深層を理解し、COMのライフサイクルをコントロールしながら、極限まで洗練された例外ハンドリングとユーザー誘導UIを構築することだ。

今回は、CorelDRAW VBAにおける `SelectionManager` の挙動を完全に解剖し、実務の現場で絶対に破綻しない堅牢なアーキテクチャを提示する。

1. CorelDRAWオブジェクトモデルにおける「選択」の真実

CorelDRAWのVBA環境において、オブジェクトの選択状態を管理するのは `ActiveDocument.Selection` または `ActiveSelection` である。だが、ここにはVBA初学者が陥る最初の罠がある。

`ActiveSelection` は、プロパティであると同時に、内部的にはコレクションを内包する特殊なインターフェースだ。何も選択されていない状態のとき、`ActiveSelection` 自体が `Nothing` を返すわけではないケースが多い点が、CorelDRAWの厄介な仕様である。多くの場合、オブジェクトは存在するが「カウントが0」であるか、あるいはドキュメントのコンテキストによっては不正なポインタを参照する。

ここで安易に以下のようなコードを書くのは、エンジニアの恥だ。

‘ 【アンチパターン】これでは防げないケースが存在する
Sub BadExample()
Dim sh As Shape
‘ 何も選択されていないと、ここで実行時エラーまたは予期せぬ挙動が発生する
Set sh = ActiveSelection.Shape(1)
sh.Fill.UniformColor.CmykAssign 0, 100, 100, 0
End Sub

このコードは、オブジェクトが1つ以上選択されているという「性善説」に立っている。業務自動化ツールにおいて、ユーザーの操作ミスを「性善説」でカバーしようなどというのは、設計の放棄に他ならない。

2. SelectionManager 駆動型:堅牢な判定ロジックの構築

オブジェクト未選択時のクラッシュを防ぐには、厳密な型チェックと、`Shapes.Count`(または `Selection.Shapes.Count`)の検証が不可欠である。さらに、CorelDRAWのCOMオブジェクトは、処理の過程で適切にメモリ解放を行わなければ、長時間のバッチ処理で確実にメモリリークを引き起こす。

以下に、実務の現場で使用すべき「極限まで最適化された選択判定&エラーハンドリングテンプレート」を提示する。

Option Explicit

”———————————————————————-
” @title 安全な選択オブジェクト取得・処理テンプレート
” @description ユーザーの選択状態を厳密に検証し、未選択時は優雅にUIへ誘導する
”———————————————————————-
Sub ExecuteWithSafeSelectionCheck()
Dim doc As Document
Dim sr As ShapeRange
Dim targetShape As Shape

‘ 1. アプリケーションとドキュメントの生存確認
If Application.Documents.Count = 0 Then
MsgBox “アクティブなドキュメントが存在しません。”, vbCritical, “システムエラー”
Exit Sub
End If

Set doc = Application.ActiveDocument

‘ 2. SelectionManager (ActiveSelectionRange) の取得
‘ ActiveSelection よりも ActiveSelectionRange を使うのがプロの選択である。
‘ ActiveSelectionRange は強力なメソッド群を内包しており、操作の安定性が段違いに高い。
Set sr = doc.ActiveSelectionRange

‘ 3. 未選択、または無効な選択状態のハンドリング
If sr.Count = 0 Then
Call HandleNoSelectionState(doc)
GoTo Cleanup
End If

‘ 4. 複数選択時のポリシー適用(例:単一選択のみ許可する場合)
If sr.Count > 1 Then
MsgBox “この機能は1つのオブジェクトのみ選択した状態で実行してください。” & vbCrLf & _
“現在の選択数: ” & sr.Count & ” 個”, vbExclamation, “入力値の検証”
GoTo Cleanup
End If

‘ 5. 安全領域でのメイン処理実行
Set targetShape = sr.Shapes(1)
Call ProcessCoreLogic(targetShape)

Cleanup:
‘ 6. COMオブジェクトの明示的解放(メモリ最適化の極意)
‘ VBAのガベージコレクションを信用するな。ローカル変数の参照は明示的にNothingへ倒す。
Set targetShape = Nothing
Set sr = Nothing
Set doc = Nothing

Exit Sub

ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “致命的エラー”
Resume Cleanup
End Sub

”———————————————————————-
” メインのビジネスロジック(プレースホルダー)
”———————————————————————-
Private Sub ProcessCoreLogic(ByRef sh As Shape)
‘ ここに実際の処理を記述
sh.Outline.SetNoOutline
MsgBox “オブジェクトの処理が正常に完了しました。”, vbInformation, “完了”
End Sub

3. 単なるエラー回避を超えた「ユーザー誘導UI」の設計

業務自動化のゴールは「エラーを出さないこと」ではない。「エラーを出さずに、ユーザーを正しい操作へ導くこと」である。

何も選択されていないときに、冷淡に `MsgBox “オブジェクトを選択してください”` とだけ表示してマクロを終了させるのは、UI/UXの観点から最悪の設計だ。現場のオペレーターは、「なぜ怒られたのか」「次に何をすればいいのか」を理解できずフラストレーションを溜める。

ここで、ユーザーを迷子にさせないための高度な誘導UI設計(プロシージャ)を導入する。

”———————————————————————-
” 未選択時のユーザー誘導UIハンドラー
”———————————————————————-
Private Sub HandleNoSelectionState(ByRef doc As Document)
Dim userChoice As VbMsgBoxResult

userChoice = MsgBox( _
“CorelDRAW上でオブジェクトが選択されていません。” & vbCrLf & _
“この処理を実行するには、対象となるシェイプを1つ選択する必要があります。” & vbCrLf & vbCrLf & _
“【アクションの選択】” & vbCrLf & _
“・[はい] : レイヤー内の全オブジェクトを自動選択して処理を続行する” & vbCrLf & _
“・[いいえ] : マクロを中止し、手動で選択し直す”, _
vbYesNo + vbExclamation + vbDefaultButton2, _
“ユーザー誘導: オジェクト未選択の検知” _
)

If userChoice = vbYes Then
‘ 代替処理:アクティブレイヤーの全シェイプを選択状態にする
If doc.ActiveLayer.Shapes.Count > 0 Then
doc.ActiveLayer.Shapes.All.CreateSelection
MsgBox “アクティブレイヤーの全オブジェクトを選択しました。” & vbCrLf & _
“再度マクロを実行してください。”, vbInformation, “誘導完了”
Else
MsgBox “アクティブレイヤーにオブジェクトが存在しません。”, vbCritical, “処理中断”
End If
Else
‘ ユーザーによる明示的な中断
‘ あえてログを残す、または静かに抜ける
End If
End Sub

このアプローチにより、マクロは単なる「プログラムの実行体」から「オペレーターをサポートするインタラクティブなシステム」へと昇華する。

4. レガシー環境とメモリ最適化の鉄則

CorelDRAWの古いバージョン(X7, 2018, 2020など)において、VBAのメモリ管理は非常にデリケートである。特に `SelectionRange` や `ShapeRange` をループ処理や条件分岐の中で生成・破棄を繰り返すと、COMの参照カウントが適切にデクリメントされず、ヒープ領域を圧迫していく。

これが数千個のオブジェクトを扱う大規模なDTP自動化スクリプトであれば、数回実行しただけでメモリ不足(Out of Memory)によるCorelDRAW自体のクラッシュを引き起こす。

チーフアーキテクトからの提言:

1. `ActiveSelection` よりも `ActiveSelectionRange` を使え
`ActiveSelection` はラッパーとしてのオーバーヘッドが大きく、内部状態の同期に不安がある。常に明示的な `Range` オブジェクトとして取得し、スコープを限定せよ。
2. オブジェクト変数は必ず `Nothing` で解放せよ
プロシージャの脱出前(`Exit Sub` やエラーハンドラ内)には、`Set doc = Nothing` のように、生成したすべてのCOMオブジェクトの参照を断ち切れ。VBAのランタイムに後始末を任せるな。
3. `On Error Resume Next` の乱用を禁ず
エラーを隠蔽するコードは、バグの温床でしかない。選択状態の判定は `If sr.Count = 0` のように、「エラーが発生する条件を事前に予測し、完全に排除する(LBYL: Look Before You Leap)」のがプロフェッショナルのコードである。

結びにかえて

たかが「オブジェクトの未選択判定」、されど「オブジェクトの未選択判定」。
この細部に対するこだわりこそが、アマチュアの書くお遊びマクロと、現場を支える堅牢なエンタープライズVBAシステムを分ける境界線である。

あなたが設計するコードベースが、いかなる過酷な現場、いかなる不慣れなユーザーの手によって実行されようとも、決して揺るがぬ要塞であらんことを。

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