【実務中級】CorelDRAW VBAで要塞を築く:`On Error Resume Next`の正しい使い所と堅牢なエラーロギング機構
業務自動化の現場において、VBAコードが予期せぬエラーで突然停止し、作業中のドキュメントが中途半端な状態で放置される――これほど開発者を絶望させる瞬間はない。特にCorelDRAWのオブジェクトモデルは、選択状態の不在、存在しないレイヤー名、エクスポート時のファイル競合など、ランタイムエラーの地雷原と化している。
世の初学者の多くは、エラーを恐れるあまり、思考停止でコードの先頭に `On Error Resume Next` を貼り付ける。そして、バグの所在を永遠に闇に葬り去る。
しかし、これはプログラミングの自殺行為だ。エラーを無視するのではなく、「制御し、記録し、安全に復旧させる」。プロフェッショナルなエンジニアが実践する、エラーハンドリングの極意を伝授しよう。
—
1. 愚かなエラー処理 vs 誇り高きエンジニアリング
まず、多くの現場で見かける「最悪のアンチパターン」を確認する。
‘ 【やってはいけないアンチパターン】
Sub BadExample()
On Error Resume Next ‘ すべてを握りつぶす魔の呪文
Dim s As Shape
Set s = ActiveLayer.CreateRectangle(0, 0, 100, 100)
s.Fill.UniformColor.CmykAssign 0, 100, 100, 0 ‘ もしFillが取得できなくても無視される
ActiveDocument.Export “C:\Output\test.pdf”, cdrPDF ‘ 失敗しても知らぬ存ぜぬ
MsgBox “処理が完了しました!” ‘ エラー起きてるのに完了って嘘をつくな
End Sub
このコードの問題点は明白だ。`CreateRectangle` が失敗しようが、ファイル出力がアクセス権限エラーで弾かれようが、プログラムは何食わぬ顔で走り続け、最後には「完了しました」とユーザーに大嘘をつく。結果、生成されたはずのファイルは影も形もない。
我々が目指すべきは、「ピンポイントで失敗を予測し、失敗した場合は即座にErrオブジェクトを捕縛してログに叩き落とし、安全なルートへフォールバックする」堅牢なアーキテクチャだ。
—
2. CorelDRAW VBAにおける `On Error Resume Next` の正しい使い所
`On Error Resume Next` は悪ではない。使う範囲(スコープ)を極限まで狭め、「エラーが発生することが仕様上あり得る、かつ、その失敗が致命的ではない単一のステートメント」に対してのみ適用するのが鉄則である。
例えば、「特定の名前を持つレイヤーが存在するかどうかを確認し、なければ新規作成する」という処理を考えてみよう。
Dim targetLayer As Layer
On Error Resume Next
Set targetLayer = ActivePage.Layers(“MyCustomLayer”)
On Error GoTo 0 ‘ すぐにデフォルトのエラー監視に戻す!
If targetLayer Is Nothing Then
Set targetLayer = ActivePage.CreateLayer(“MyCustomLayer”)
End If
このイディオムでは、`Layers(“…”)` が存在しないレイヤー名を指定して実行時エラー(Runtime Error)を吐くことを逆手に取っている。`On Error Resume Next` をわずか1行挟むことで、「エラーが起きたら `Nothing` が返る」という安全な挙動にすり替えているのだ。そして、次の行ですぐに `On Error GoTo 0` で保護を解除している。これが正しいスコープ管理である。
—
3. 実践:Errオブジェクトを活用した堅牢なエラーロギング機構
実務の現場では、単にエラーを回避するだけでは不十分だ。「いつ、どのモジュールで、どのようなエラーコードと説明が発生したか」をテキストログとして永続化する必要がある。
ここからは、実務でそのまま使える、構造化されたエラーロギングクラス(またはモジュール)の設計を見ていこう。
ログ出力用標準モジュール:`ErrorLogger.bas`
Option Explicit
‘ エラーログをテキストファイルに書き出すプロシージャ
Public Sub WriteErrorLog(ByVal moduleName As String, ByVal procName As String, ByVal errNumber As Long, ByVal errDesc As String)
Dim fso As Object
Dim ts As Object
Dim logPath As String
‘ ログファイルの保存先(ドキュメントフォルダやアドインフォルダ等)
logPath = Environ(“USERPROFILE”) & “\Desktop\CorelDRAW_ErrorLog.txt”
Set fso = CreateObject(“Scripting.FileSystemObject”)
‘ ファイルが存在しない場合は新規作成、存在する場合は追記 (ForAppending = 8)
On Error GoTo LogError_Failed
Set ts = fso.OpenTextFile(logPath, 8, True)
ts.WriteLine “==================================================”
ts.WriteLine “Timestamp : ” & Now
ts.WriteLine “Module : ” & moduleName
ts.WriteLine “Procedure : ” & procName
ts.WriteLine “Err Number: ” & errNumber
ts.WriteLine “Description: ” & errDesc
ts.WriteLine “==================================================”
ts.Close
Set ts = Nothing
Set fso = Nothing
Exit Sub
LogError_Failed:
‘ ログ書き込み自体が失敗した場合はイミディエイトウィンドウに逃がす(無限ループ防止)
Debug.Print “CRITICAL: ログの書き込みに失敗しました – ” & Err.Description
On Error GoTo 0
End Sub
—
4. プロダクションコード:安全なドキュメント処理のテンプレート
上記で作成したロガーを組み込み、CorelDRAWのオブジェクト操作を安全にラップしたプロダクションコードの模範解答を示す。
Option Explicit
‘ メインエントリーポイント
Sub ProductionProcessMain()
Const MODULE_NAME As String = “Module1”
Const PROC_NAME As String = “ProductionProcessMain”
‘ 1. プロシージャレベルのエラーハンドラを宣言
On Error GoTo ErrorHandler
‘ CorelDRAWの描画を一旦停止し、パフォーマンスを爆発的に向上させる
ActiveDocument.BeginCommandGroup “Batch Process”
EventsEnabled = False
ScreenRefresh = False
‘ — メイン処理のシミュレーション —
Dim doc As Document
Set doc = ActiveDocument
If doc Is Nothing Then
Err.Raise 9999, “Validation”, “アクティブなドキュメントが存在しません。”
End If
‘ 意図的なテスト用エラー(例:存在しないレイヤーの操作)
ProcessLayers doc
‘ 正常終了処理
ScreenRefresh = True
EventsEnabled = True
doc.EndCommandGroup
MsgBox “すべての処理が正常に完了しました。”, vbInformation, “成功”
Exit Sub
ErrorHandler:
‘ — 異常終了時のフォールバックとロギング —
‘ 描画ロックが外れないままフリーズするのを防ぐため、必ず復旧させる
ScreenRefresh = True
EventsEnabled = True
On Error Resume Next
If Not ActiveDocument Is Nothing Then ActiveDocument.EndCommandGroup
On Error GoTo 0
‘ ログ機構の呼び出し
WriteErrorLog MODULE_NAME, PROC_NAME, Err.Number, Err.Description
‘ ユーザーへの優しくかつ正確な通知
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“エラーコード: ” & Err.Number & vbCrLf & _
“詳細: ” & Err.Description & vbCrLf & _
“※デスクトップの ErrorLog.txt を確認してください。”, vbCritical, “致命的なエラー”
End Sub
Private Sub ProcessLayers(ByRef doc As Document)
‘ このサブプロシージャ内でのエラーも呼び出し元(ErrorHandler)に伝播する
Dim targetLayer As Layer
‘ 局所的なエラーハンドリング(存在しない場合の安全なフォールバック)
On Error Resume Next
Set targetLayer = doc.Pages(1).Layers(“NonExistentLayer”)
On Error GoTo 0
If targetLayer Is Nothing Then
‘ ログに残して安全に新規作成へ逃げる
Set targetLayer = doc.Pages(1).Layers.Add(“NonExistentLayer”)
End If
‘ さらに何らかの処理…
targetLayer.Activate
End Sub
—
5. チーフアーキテクトからの提言
ここで示したコードの構造には、実務で生き残るための重要な哲学が詰まっている。
1. 環境のクリーンアップ(フェイルセーフ)
`ScreenRefresh = False` や `EventsEnabled = False` を用いてCorelDRAWの描画を停止している場合、途中でエラーが起きるとUIが完全にフリーズしたままゾンビ化する。ErrorHandler内で確実にフラグを戻す処理(ファイナライザ的役割)を入れることは、コードの美しさではなく、OSを守るための必須要件である。
2. Err.Raiseによるカスタム例外の送出
CorelDRAWが吐くエラーだけでなく、自前のビジネスロジック(「ドキュメントが開かれていない」「選択図形が多すぎる」など)の違反も、`Err.Raise` を使うことで統一されたエラーハンドリングパイプラインに合流させることができる。
「エラーが起きないコード」を書くことは不可能だ。しかし、「エラーが起きたときにシステムを破壊せず、原因を雄弁に語るログを残して美しく退場するコード」を書くことは、プロのエンジニアであれば常に義務付けられている。
明日からのあなたのVBAコードから、無謀な `On Error Resume Next` を排除し、気高いエラー設計を実装してほしい。
