【実務・中級編】【保守性向上】Option Explicitの徹底と独自エラーログ出力クラスの作成によるCorelDRAWマクロの保守・運用術 – CorelDRAW VBA解析バイブル

スポンサーリンク

【CorelDRAW VBA保守性向上】Option Explicitの徹底と独自エラーログ出力クラスによる堅牢なマクロ設計

開発プロジェクトのリーダーである私たちが、現場の自動化ツール開発において最も直面し、そして頭を悩ませる問題は何か。それは「作った本人しか直せない、属人化したスパゲッティコード」と、「現場の運用フェーズで突如発生し、原因特定に数時間を要するブラックボックスなエラー」である。

特にCorelDRAW VBAの世界では、UIの利便性からマクロの記録やその場の継ぎ接ぎコードが横行しやすい。しかし、DTPの現場や製造業の現場において、マクロの沈黙はそのまま業務停止、ひいては数百万単位の刷り直しミスに直結する。

今回は、CorelDRAW VBAを「趣味のスクリプト」から「ミッションクリティカルなプロダクションコード」へと昇華させるための極限の知見を授けよう。

—

1. なぜ `Option Explicit` なしではプログラミングと言えないのか

VBAの初期設定のままコードを書き始めていないか?
`Option Explicit`(変数の宣言を強制する)を記述しない、あるいはVBAエディタのオプションで「変数の宣言を強制する」にチェックを入れていない開発者は、目隠しをして時速100キロで高速道路を逆走しているようなものだ。

暗黙の型宣言が生む「沈黙のバグ」

VBAでは、`Option Explicit`がない状態で `docWidth = 100` と書くと、勝手に `Variant` 型の変数が新規作成される。
もしあなたが後続のコードで `docWdith = 100` (スペルミス)と打った場合、VBAはエラーを出さず、「値が0の新しい変数 `docWdith` が勝手に誕生した」と解釈する。

CorelDRAWのオブジェクトモデルにおいて、意図しない変数の混入や型の不一致は、`Type mismatch` や `Object required` といったクラッシュを引き起こすだけでなく、「エラーは出ないが、意図したサイズでドキュメントが作成されない・オブジェクトが消える」という最もタチの悪いサイレント・バグを誘発する。

すべてのモジュールの最上部に、必ずこれを記述することから私たちの仕事は始まる。

Option Explicit

—

2. CorelDRAW VBAにおけるエラーハンドリングの限界

VBA標準の `On Error Resume Next` や `On Error GoTo ErrorHandler` は、その場しのぎの対症療法に過ぎない。
現場でエラーが起きたとき、ユーザー画面に突然 `エラー 9: インデックスが有効範囲にありません` と表示されても、オペレーターは何が起きたか理解できないし、開発者である私たちの元には「なんか止まりました」という情報量のゼロに近い報告しか上がってこない。

真にプロフェッショナルなマクロには、以下の要件を満たす「エラーログ自動出力機構」が組み込まれている必要がある。

1. ユーザーをパニックにさせない: エラー内容は隠し、分かりやすいメッセージと「処理を中断しました」をスマートに伝える。
2. 開発者を迷子にさせない: エラーが発生した「日時」「モジュール名」「プロシージャ名」「エラー番号」「エラー詳細」を、自動的にテキストファイルやネットワーク上の共有フォルダに吐き出す。
3. CorelDRAWの内部状態を汚さない: エラー発生時に画面描画のロック(`Optimization = True`)やアンドゥのトランザクションが開きっぱなしになるのを防ぐ。

—

3. 実装:独自エラーログ出力クラス(`Logger.cls`)の構築

VBAにはJavaやC#のような洗練されたロギングフレームワークはない。しかし、クラスモジュール(Class Module)を活用すれば、極めてクリーンで再利用性の高いログ出力基盤を自作できる。

以下のコードを、VBAエディタで 「クラスモジュール」 を新規追加し、名前を `Logger` に変更して貼り付けてほしい。

クラスモジュール: `Logger`

VERSION 1.0 CLASS
BEGIN
MultiUse = -1 ‘True
END
Attribute VB_Name = “Logger”
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit

Private FSO As Object
Private LogFilePath As String

‘ =================================================================
‘ クラス初期化時にログファイルの出力パスを決定する
‘ =================================================================
Private Sub Class_Initialize()
Set FSO = CreateObject(“Scripting.FileSystemObject”)

‘ デフォルトではCorelDRAWの実行ファイルと同じパス、
‘ またはマクロドキュメントのパス、あるいは特定の共有フォルダを指定する
‘ ここでは安全のため、環境変数 TEMP フォルダを使用する
Dim tempPath As String
tempPath = Environ$(“TEMP”)

‘ ログファイル名:CorelDRAW_ErrorLog.txt
LogFilePath = tempPath & “\CorelDRAW_Macro_Error.log”
‘ Debug.Print “Log Path: ” & LogFilePath ‘ デバッグ用
End Sub

‘ =================================================================
‘ クラス終了処理
‘ =================================================================
Private Sub Class_Terminate()
Set FSO = Nothing
End Sub

‘ =================================================================
‘ エラーログをファイルに追記するパブリックメソッド
‘ =================================================================
Public Sub WriteLog(ByVal moduleName As String, ByVal procedureName As String, ByVal errNumber As Long, ByVal errDescription As String)
On Error GoTo SafeExit ‘ ログ書き込み自体でエラーが起きた場合に無限ループしないための防衛

Dim fileStream As Object
Dim logMessage As String

‘ ログメッセージのフォーマット構築(ISO8601風)
logMessage = “[” & Format$(Now, “yyyy-mm-dd hh:nn:ss”) & “] ” & _
“Module: ” & moduleName & ” | ” & _
“Proc: ” & procedureName & ” | ” & _
“Err#: ” & errNumber & ” | ” & _
“Desc: ” & errDescription

‘ ファイルが存在しない場合は新規作成、存在する場合は追記 (ForAppending = 8, True = Create)
If Not FSO.FileExists(LogFilePath) Then
Set fileStream = FSO.CreateTextFile(LogFilePath, True)
fileStream.WriteLine “=== CorelDRAW Macro Error Log Started ===”
fileStream.Close
End If

Set fileStream = FSO.OpenTextFile(LogFilePath, 8, True)
fileStream.WriteLine logMessage
fileStream.Close

Set fileStream = Nothing
Exit Sub

SafeExit:
‘ ログ書き込み失敗時はイミディエイトウィンドウにフォールバック
Debug.Print “CRITICAL: Failed to write log. Original Error: ” & errDescription
End Sub

‘ =================================================================
‘ ログファイルのフルパスを取得するプロパティ
‘ =================================================================
Public Property Get GetLogPath() As String
GetLogPath = LogFilePath
End Property

—

4. 実践:プロダクションコードにおけるエラーハンドリングの全貌

では、先ほど作成した `Logger` クラスを実際のCorelDRAW自動化スクリプト(標準モジュール)でどのように組み込むべきか。

以下のコードは、選択中のシェイプに対して何らかの安全な処理を行い、パフォーマンス最適化(`Optimization`)と例外処理、そしてログ出力を完璧に網羅した模範的なプロダクションコードである。

標準モジュール: `Module_Main`

Option Explicit

‘ =================================================================
‘ メイン処理:選択オブジェクトの安全な一括処理サンプル
‘ =================================================================
Sub ProcessSelectedShapesSafely()
‘ 1. オブジェクトのライフサイクル管理とパフォーマンス最適化の宣言
Dim CdrApp As CorelDRAW.Application
Set CdrApp = Application

‘ 画面描画とイベントのロック(爆発的な高速化の要)
CdrApp.Optimization = True
CdrApp.EventsEnabled = False

Dim activeDoc As CorelDRAW.Document
Set activeDoc = CdrApp.ActiveDocument

‘ 2. 独自ロガーのインスタンス化
Dim log As Logger
Set log = New Logger

‘ エラーハンドラへのジャンプを有効化
On Error GoTo ErrorHandler

‘ ————————————————————-
‘ ビジネスロジックの記述領域
‘ ————————————————————-
If activeDoc Is Nothing Then
Err.Raise 9999, “ProcessSelectedShapesSafely”, “アクティブなドキュメントが存在しません。”
End If

If activeDoc.Selection.Shapes.Count = 0 Then
MsgBox “処理対象のオブジェクトが選択されていません。”, vbExclamation, “警告”
GoTo CleanUp
End If

‘ トランザクションの開始(CorelDRAWでは必須ではないがUndoの単位をまとめる)
activeDoc.BeginUndoGroup “安全なシェイプ一括処理”

Dim sh As CorelDRAW.Shape
For Each sh in activeDoc.Selection.Shapes
‘ 例:もし特定のレイヤー以外ならエラーを発生させるテスト
‘ if sh.Layer.Name = “LockedLayer” then err.raise…

‘ サンプルとしてアウトラインカラーを赤に変更する処理
sh.Outline.Color.RGBAssign 255, 0, 0
Next sh

activeDoc.EndUndoGroup
‘ ————————————————————-

CleanUp:
‘ 3. リソースの解放とパフォーマンスの復元(極めて重要)
CdrApp.Optimization = False
CdrApp.EventsEnabled = True
CdrApp.Refresh

Set log = Nothing
Set activeDoc = Nothing
Set CdrApp = Nothing

MsgBox “処理が正常に完了しました。”, vbInformation, “完了”
Exit Sub

ErrorHandler:
‘ 4. エラー発生時の安全な回収とログ書き込み
‘ エラー詳細をロガーに渡す
log.WriteLog “Module_Main”, “ProcessSelectedShapesSafely”, Err.Number, Err.Description

‘ ユーザーへの丁寧なフィードバック
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“処理を中断します。” & vbCrLf & _
“詳細は以下のログを確認してください:” & vbCrLf & _
log.GetLogPath, vbCritical, “致命的なエラー”

‘ エラー時も確実に環境を復元する
On Error Resume Next
If Not activeDoc Is Nothing Then
activeDoc.EndUndoGroup
End If
CdrApp.Optimization = False
CdrApp.EventsEnabled = True
CdrApp.Refresh

‘ オブジェクトの破棄
Set log = Nothing
Set activeDoc = Nothing
Set CdrApp = Nothing
End Sub

—

5. チーフアーキテクトからの実践的アドバイス

上記のコードを見れば、なぜこれが「プロ仕様」なのかが理解できるはずだ。

1. `Optimization = True` とエラーハンドリングの不可分な関係

CorelDRAW VBAで大量のオブジェクトを操作する際、`Application.Optimization = True` を使うことで描画更新を止め、処理速度を数十倍から数百倍に跳ね上げることができる。
しかし、このモード中にエラーでコードが強制終了すると、CorelDRAWの画面がフリーズしたような状態(描画が更新されないまま)で固まるという最悪のUXを引き起こす。
私たちのコードでは、`CleanUp:` ラベルと `ErrorHandler:` ラベルの双方向で必ず `Optimization = False` を保証する構造にしている。これがプロの守りだ。

2. ログファイルの保存先戦略

今回のコードでは環境変数 `TEMP` (`C:\Users\…\AppData\Local\Temp`) を使用したが、実務の現場(社内LAN等)に合わせて、以下のように設計を変更するのも有効である。

  • ネットワーク上の共有ドライブ(例: `\\Server\Logs\CorelDRAW\`)にチーム全員のログを集約する。
  • ただし、ネットワークが切断されている環境でもマクロがクラッシュしないよう、ロガー内部で `On Error Resume Next` を使い、ファイル書き込み失敗時はローカルへフォールバックする耐障害性を持たせている(クラス内の `SafeExit` ラベル参照)。

—

総括

属人化の排除とは、個人のプログラミング作法に頼ることではない。
「誰が書いても、誰がメンテナンスしても、同じように動き、エラーが起きたら一目で原因が特定できる『型』をシステムに強制すること」である。

`Option Explicit` で変数の海図を描き、独自の `Logger` クラスで航海の安全記録を残す。
この基盤を手に入れた瞬間から、あなたのCorelDRAW VBA開発は「動けばいいおもちゃ」から「ビジネスを支える堅牢なインフラ」へと進化する。

現場のエンジニア諸君、今すぐ既存のマクロにこの設計を導入したまえ。結果はコードの美しさと、トラブルシューティングの劇的な効率化となってすぐに現れるはずだ。

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