「動けばいい」マクロはゴミ箱へ。CorelDRAW VBAにおける、破綻を許さない堅牢な例外処理設計論
現場のエンジニア諸君、よく聞け。
「マクロが動かない」「レイヤーが見つからないというエラーで止まった」「座標がズレてデザインが崩れた」……。もし君が、クライアントや現場のユーザーからこれらを報告されているとしたら、君が作っているのは「自動化ツール」ではない。ただの「不安定なスクリプト」だ。
プロフェッショナルが作るべきは、「予期せぬ事態(例外)を前提とした、自己修復的かつ説明責任を果たすソフトウェア」である。
CorelDRAWのオブジェクトモデルは強力だが、非常に状態依存的だ。ユーザーが作業中にレイヤーを消すかもしれない、ドキュメントの単位(Unit)が勝手に変更されているかもしれない、あるいは外部データベースから読み込んだ座標がドキュメントの描画可能範囲を超えているかもしれない。
本稿では、単なる `On Error Resume Next` という初心者向けの逃げ道を徹底的に排除し、実務レベルで耐えうる「堅牢なエラーハンドリング設計」の極意を伝授する。
1. 「Happy Path」の幻想を捨てろ
多くの開発者は、「レイヤーが存在し、ページが存在し、座標が正しい」という、いわゆる Happy Path(理想的な実行経路) だけを想定してコードを書く。しかし、実務の現場は地獄だ。
エラーハンドリングを設計する際、以下の3つの「非決定的な要素」を常に意識せよ。
1. オブジェクトの不在: `Layers(“LayerName”)` を参照したとき、その名前のレイヤーが本当に存在するか?
2. コンテキストの不一致: 現在アクティブなドキュメント、あるいはページが、操作対象の意図したものと一致しているか?
3. 物理的境界の逸脱: 指定したガイド線の座標 $(x, y)$ は、現在のドキュメントの `Page.Size` の範囲内に収まっているか?
これらをチェックせずに処理を進めることは、時限爆弾を抱えて走るのと同じである。
2. 堅牢なアーキテクチャ:3層のエラー管理
プロフェッショナルな設計では、エラーを「握りつぶす」のではなく、「分類し、適切に報告する」必要がある。
① 検証層 (Validation Layer)
処理を実行する前に、オブジェクトの存在とパラメータの妥当性をチェックする。ここでは例外を「発生」させるのではなく、事前に「拒絶」する。
② 実行層 (Execution Layer)
実際のCorelDRAW APIを叩く層。ここでは `On Error GoTo` を用い、予期せぬランタイムエラーをキャッチする。
③ 回復・報告層 (Recovery & Reporting Layer)
エラーが発生した際、ユーザーに「何が原因で、どうすれば直るか」を論理的に提示し、プログラムを安全な状態(例えば、処理を中断してドキュメントを保存せずに閉じる等)へ導く。
3. 【実践】プロダクション・グレードのコード例
以下に、レイヤーの追加とガイド線の自動配置を行う、極めて堅牢な実装例を示す。このコードは、そのまま業務ツールの一部として組み込めるレベルの設計思想に基づいている。
Option Explicit
' ==============================================================================
' 業務自動化エンジニア向け:堅牢なレイヤー・ガイド操作モジュール
' 設計思想:事前検証(Validation) + 構造化エラー処理(Structured Error Handling)
' ==============================================================================
''' <summary>
''' 指定した名前のレイヤーを作成し、指定座標にガイド線を配置する。
''' </summary>
''' <param name="layerName">作成するレイヤー名</param>
''' <param name="posX">配置するX座標</param>
''' <param name="posY">配置するY座標</param>
Public Sub CreateLayerAndGuideRobustly(ByVal layerName As String, ByVal posX As Double, ByVal posY As Double)
On Error GoTo ErrorHandler
Dim doc As Document
Dim targetLayer As Layer
Dim pg As Page
' --- 1. コンテキストの検証 (Validation Phase) ---
Set doc = ActiveDocument
If doc Is Nothing Then
Err.Raise vbObjectError + 513, "CreateLayerAndGuide", "アクティブなドキュメントが見つかりません。"
End If
Set pg = doc.ActivePage
If pg Is Nothing Then
Err.Raise vbObjectError + 514, "CreateLayerAndGuide", "アクティブなページが見つかりません。"
End If
' 座標の境界チェック (Boundary Validation)
' ドキュメントの単位が何であれ、現在の単位に基づいた境界チェックを行う
If posX < 0 Or posX > pg.Size.Width Or posY < 0 Or posY > pg.Size.Height Then
Err.Raise vbObjectError + 515, "CreateLayerAndGuide", _
"指定された座標 (" & posX & ", " & posY & ") がページ範囲外です。" & vbCrLf & _
"ページサイズ: " & pg.Size.Width & " x " & pg.Size.Height
End If
' --- 2. 実行フェーズ (Execution Phase) ---
' レイヤーの存在確認と作成
Set targetLayer = GetOrCreateLayer(doc, layerName)
' ガイド線の配置
' ガイド線はレイヤーに属させる必要があるため、レイヤーをアクティブにする
targetLayer.Activate
pg.Guides.Add CreateGuide(doc, posX, posY)
MsgBox "処理が正常に完了しました。", vbInformation, "Success"
ExitSub:
Exit Sub
ErrorHandler:
' --- 3. 回復・報告フェーズ (Recovery & Reporting Phase) ---
' ここでエラーの種類に応じたロジックを分岐させる
Select Case Err.Number
Case vbObjectError + 513, vbObjectError + 514
' 重大な環境エラー
MsgBox "致命的なエラー: " & Err.Description, vbCritical, "System Error"
Case vbObjectError + 515
' ユーザー入力・データエラー
MsgBox "入力パラメータエラー: " & Err.Description, vbExclamation, "Parameter Error"
Case Else
' 未知のランタイムエラー
MsgBox "予期せぬエラーが発生しました。" & vbCrLf & _
"Error No: " & Err.Number & vbCrLf & _
"Description: " & Err.Description, vbCritical, "Runtime Error"
End Select
' ログ出力などの処理をここに記述(実務ではファイルやDBへ)
Debug.Print "Error Log: " & Now & " | " & Err.Description
Resume ExitSub
End Sub
''' <summary>
''' レイヤーが存在すればそれを返し、存在しなければ新規作成する。
''' </summary>
Private Function GetOrCreateLayer(ByRef doc As Document, ByVal name As String) As Layer
Dim lyr As Layer
On Error Resume Next ' レイヤー検索における一時的なエラー回避
Set lyr = doc.Layers(name)
On Error GoTo 0 ' 即座に通常の挙動に戻す
If lyr Is Nothing Then
' レイヤーが存在しない場合のみ作成
Set lyr = doc.AddLayer(name)
End If
Set GetOrCreateLayer = lyr
End Function
''' <summary>
''' ガイド線オブジェクトを生成するヘルパー関数
''' </summary>
Private Function CreateGuide(ByRef doc As Document, ByVal x As Double, ByVal y As Double) As Guide
' 実際には、X座標かY座標のどちらか一方を指定して垂直/水平ガイドを作るロジックが必要
' ここでは簡略化のため、X座標に基づいた垂直ガイドを返すと想定
Set CreateGuide = doc.ActivePage.Guides.Add(x, True) ' True = Vertical
End Function
4. 外部連携(DB/ファイル)における致命的な落とし穴
実務で最も恐ろしいのは、「CorelDRAW側の操作は失敗したが、外部DBの更新は成功してしまった」という状態だ。これを「不整合(Inconsistency)」と呼ぶ。
例えば、ExcelやSQL Serverから座標リストを読み込み、それに基づいてガイド線を引くツールを想定しよう。
1. トランザクションの概念を持て:
DBの値を更新する前に、CorelDRAW側の操作が成功するかどうかを「Dry Run(シミュレーション)」で検証するか、あるいはCorelDRAWの操作をすべて完了させた後に、一括でDBへ書き込む設計にせよ。
2. アトミックな設計:
「1行のデータ処理」をひとつの塊(アトミック)として扱え。1つの座標が不正だったからといって、全プロセスを止めるのか、あるいはその行だけスキップしてログを残すのか。この仕様を事前に定義しておくことが、設計者の責務である。
5. まとめ:エンジニアとしての矜持
「とりあえず動くコード」を書くのは誰にでもできる。しかし、「壊れないコード」を書くには、常に最悪のシナリオを想像する力が必要だ。
- `On Error Resume Next` を乱用するな。 それはエラーを解決するのではなく、隠蔽しているだけだ。
- オブジェクトの存在を疑え。 `Nothing` チェックは、プロフェッショルの作法である。
- ユーザーに「理由」を教えろ。 「エラーです」ではなく、「座標が範囲外です」と伝える。それがツールの信頼性を決める。
君が書くコードの一行一行が、現場の生産性を支えていることを忘れるな。以上だ。
