【SolidWorks VBAを掌握する極限の知見】
堅牢な例外処理の実装:Errオブジェクトと独自トランザクションログを活用したクラッシュ防止エラーハンドリング
開発現場でこんな悪夢を見たことはないだろうか。
「数千部品ある巨大アセンブリのバッチ処理中、終盤で突如SolidWorksがフリーズ。エラーメッセージもなくデスクトップに放り出され、半日分の作業が水の泡になった」――。
VBAのデフォルトのエラー処理(`On Error Resume Next`の乱用や、無策な `On Error GoTo 0`)は、実務の現場においては時限爆弾に等しい。
SolidWorks APIは、COMオブジェクトの塊であり、メモリリーク、不正なポインタ参照、未保存ドキュメントの競合など、予測不能なクラッシュ要因に満ちている。
今回は、プロのエンジニアとして生き残るための「クラッシュゼロ・完全追跡型エラーハンドリング設計」を伝授する。
—
1. なぜ初心者のエラー処理は現場で破綻するのか?
多くの解説書やネット記事にあるコードは、次のような甘い実装になっている。
‘ 【アンチパターン】絶対に真似してはいけないコード
Sub BadExample()
Dim swApp As SldWorks.SldWorks
Set swApp = Application.SldWorks
‘ エラーを隠蔽する最悪の書き方
On Error Resume Next
Dim swModel As SldWorks.ModelDoc2
Set swModel = swApp.ActiveDoc
‘ ここでファイルが存在しない等のエラーが起きても無視され、次の行でヌルポインタ例外へ直行
swModel.Extension.SaveAs…
On Error GoTo 0
End Sub
この書き方が非効率・危険である理由
1. エラーの握りつぶし(`Resume Next`): 本当に起きてはいけない致命的なバグまで無視され、意図しないドキュメント上書きやデータ破損を引き起こす。
2. 状態の不整合: マクロ途中で失敗した際、ドキュメントが「編集モード」のまま放置されたり、ファイルが排他ロックされたりする。
3. 原因特定不能: ユーザーから「なんかマクロが止まりました」と言われても、どのファイルの何行目で、どんなAPI戻り値のときに起きたのかが全くログに残らない。
プロの現場で求められるのは、「異常を即座に検知し、安全に状態をロールバック(復旧)し、原因をミリ秒単位のタイムスタンプ付きでログに書き出す」仕組みである。
—
2. プロダクションコード設計の核心
堅牢なSolidWorksマクロを構築するため、以下の3要素を実装する。
1. トランザクション的思考: 処理開始前の状態(アクティブドキュメント、表示設定など)を記憶し、異常時は安全な状態へ復元する。
2. 構造化されたエラーハンドリング: サブルーチンごとに明確な出口(`ErrorHandler:`)を設け、`Err`オブジェクトの情報を確実にキャプチャする。
3. 独自トランザクションログ: テキストファイル(UTF-8)へ、誰が・いつ・どの関数で・何のエラーコード(`Err.Number` / `Err.Description`)で落ちたかを自動記録する。
—
3. 【コピペ即実践】堅牢性・保守性を極めたプロダクションコード
以下のコードは、実務のファイル処理バッチなどでそのまま流用できるテンプレートである。
モジュール全体に `Option Explicit` を必ず付与し、APIの型安全性を担保している。
Option Explicit
‘ ==============================================================================
‘ 模块名: 堅牢なバッチ処理コントローラー
‘ 概要 : Errオブジェクトと独自ログ出力、状態復元を備えたプロフェッショナル実装
‘ ==============================================================================
Private Const LOG_FILE_NAME As String = “SolidWorks_Macro_Execution.log”
Public Sub ExecuteRobustProcess()
Dim swApp As SldWorks.SldWorks
Dim startTime As Double
startTime = Timer
‘ 1. 初期化とログ開始宣言
WriteTransactionLog “INFO”, “=== マクロ処理開始 ===”
On Error GoTo Global_ErrorHandler
Set swApp = Application.SldWorks
If swApp Is Nothing Then
Err.Raise vbObjectError + 1000, “ExecuteRobustProcess”, “SolidWorksのセッションが取得できません。”
End If
‘ 2. メイン処理の呼び出し(トランザクション境界)
ProcessMainBatch swApp
‘ 3. 正常終了
WriteTransactionLog “INFO”, “=== マクロ処理が正常終了しました。実行時間: ” & Format(Timer – startTime, “0.00”) & ” 秒 ===”
MsgBox “すべての処理が正常に完了しました。”, vbInformation, “完了”
Exit Sub
Global_ErrorHandler:
‘ グローバルレベルでの最終防衛線
Dim errDesc As String
errDesc = “Fatal Error [” & Err.Number & “]: ” & Err.Description
WriteTransactionLog “FATAL”, errDesc
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“詳細はログファイルを確認してください。” & vbCrLf & _
“ログパス: ” & GetLogFilePath(), vbCritical, “システムエラー”
End Sub
Private Sub ProcessMainBatch(ByVal swApp As SldWorks.SldWorks)
‘ このプロシージャ固有のエラーハンドラ
On Error GoTo Local_ErrorHandler
WriteTransactionLog “DEBUG”, “ProcessMainBatch: メイン処理シーケンスに入りました。”
Dim swModel As SldWorks.ModelDoc2
Set swModel = swApp.ActiveDoc
‘ ドキュメントが開かれていない場合のガード節
If swModel Is Nothing Then
Err.Raise vbObjectError + 1001, “ProcessMainBatch”, “アクティブなドキュメントが存在しません。モデルを開いてから実行してください。”
End If
WriteTransactionLog “INFO”, “処理対象ドキュメント: ” & swModel.GetPathName()
‘ — 【ここに実際の重いAPI処理を記述】 —
‘ 例として、カスタムプロパティの書き込みや強制リビルドなどを想定
Call PerformComplexOperation(swModel)
‘ ——————————————
Exit Sub
Local_ErrorHandler:
‘ ローカルでの例外捕捉:必要に応じたロールバック処理をここに記述
WriteTransactionLog “ERROR”, “ProcessMainBatch内でエラー発生 -> 処理を中断します. Err: ” & Err.Description
‘ 例:変更破棄の処理や、ビューポートの復元など(必要に応じて実装)
‘ swModel.EditRebuild3 等のリカバリ
‘ エラーを上位へ再スプロウ(Bubble up)させる
Err.Raise Err.Number, “ProcessMainBatch -> ” & Err.Source, Err.Description
End Sub
Private Sub PerformComplexOperation(ByVal swModel As SldWorks.ModelDoc2)
‘ さらに下位の処理レイヤー
On Error GoTo Deep_ErrorHandler
‘ わざとエラーを誘発するテスト(コメントアウトを外して動作確認可能)
‘ Err.Raise 11, “PerformComplexOperation”, “ゼロ除算シミュレーションエラー”
‘ 強制リビルドの実行
Dim boolstatus As Boolean
boolstatus = swModel.ForceRebuild3(False)
If Not boolstatus Then
WriteTransactionLog “WARN”, “PerformComplexOperation: リビルドが完全には成功しませんでした。”
End If
Exit Sub
Deep_ErrorHandler:
WriteTransactionLog “ERROR”, “PerformComplexOperation内部で例外: ” & Err.Description
Err.Raise Err.Number, “PerformComplexOperation”, Err.Description
End Sub
‘ ==============================================================================
‘ 独自トランザクションログ出力エンジン
‘ ==============================================================================
Private Sub WriteTransactionLog(ByVal logLevel As String, ByVal message As String)
On Error Resume Next ‘ ログ書き込み自体のエラーでマクロを止めないための安全策
Dim fso As Object
Dim ts As Object
Dim logPath As String
logPath = GetLogFilePath()
Set fso = CreateObject(“Scripting.FileSystemObject”)
‘ ログフォルダが存在しない場合は作成
Dim parentDir As String
parentDir = fso.GetParentFolderName(logPath)
If Not fso.FolderExists(parentDir) Then
fso.CreateFolder parentDir
End If
‘ 追記モードでオープン (Unicode: True)
Set ts = fso.OpenTextFile(logPath, 8, True, -1)
Dim logLine As String
logLine = “[” & Format(Now, “yyyy-mm-dd hh:nn:ss”) & “] [” & UCase(logLevel) & “] ” & message
ts.WriteLine logLine
ts.Close
Set ts = Nothing
Set fso = Nothing
End Sub
Private Function GetLogFilePath() As String
‘ ExcelやSolidWorksのマクロ実行パス、またはユーザーのテンポラリにログを吐く
Dim fso As Object
Set fso = CreateObject(“Scripting.FileSystemObject”)
‘ デスクトップ、またはアドインの実行フォルダに出力
GetLogFilePath = CreateObject(“WScript.Shell”).SpecialFolders(“Desktop”) & “\SolidWorks_Macro_Logs\” & LOG_FILE_NAME
Set fso = Nothing
End Function
—
4. チーフアーキテクトからの実践アドバイス:保守性の勘所
1. エラーの再スロー(Bubble up)の哲学
下位プロシージャで起きたエラーを単に隠蔽するのではなく、`Err.Raise` を用いて上位へ伝播させることが重要である。これにより、どのレイヤーで問題が起きたのかがログのトレースで一目瞭然となる。
2. ログファイルの出力先選定
ネットワークドライブ(共有サーバー)上のパスを直接指定すると、社内LANの瞬間的な切断などでVBA側が固まる原因になる。基本はローカル環境(デスクトップや一時フォルダ)に書き出し、処理完了後にサーバーへ転送する設計が最も安全である。
3. COMオブジェクトの解放徹底
コード内で `CreateObject` や手動で参照を持ったオブジェクトは、プロシージャの抜け際(あるいはエラー発生時)に必ず `Set xxx = Nothing` でメモリを解放する。これを怠ると、SolidWorksプロセスがバックグラウンドに残り続け(ゾンビプロセス)、次回のファイルオープン時にファイル競合エラーを引き起こす。
結び
マクロの良し悪しは、「正常系が動く時」ではなく、「異常系に直面した時にどう振る舞うか」で決まる。
今回解説したトランザクションログと堅牢な例外処理のフレームワークをあなたの開発スタンダードに組み込めば、大規模な設計変更バッチであっても、夜間に安心してマシンを離れられる「真の自動化環境」が手に入るはずだ。
