【実務・中級編】エラーハンドリングと変数の初期化:異常終了時のメモリリークを防ぐクリーンアップ処理 – Excel VBA解析バイブル

スポンサーリンク

Excel VBAを掌握する極限の知見
第1回:エラーハンドリングと変数の初期化――異常終了時のメモリリークを防ぐクリーンアップ処理

開発現場でよく見かける「とりあえず `On Error Resume Next` を入れておけ」という場当たり的なコード。そして、エラー発生時にオブジェクトの参照が残ったまま宙ぶらりんになり、Excelを終了するまでプロセスが解放されないメモリリークの山。

業務自動化ツールを本番稼働させ、幾多の修羅場をくぐってきたプロフェッショナルであれば、この恐怖がお分かりだろう。
VBAは「簡易的な言語」と侮られがちだが、COMオブジェクトの参照管理や外部リソース(DB、ファイルシステム)のハンドリングにおいて、C++やC#と同等の厳密なライフサイクル管理が求められる。

今回は、異常終了時であっても確実にリソースを回収し、変数を初期状態に巻き戻す「堅牢なクリーンアップ・パターン」の極意を伝授する。

1. なぜ「スパゲッティ・エラー処理」は破滅を招くのか

多くの初心者が書くエラー処理のアンチパターンを見てみよう。

Sub BadExample()
Dim cn As Object
Set cn = CreateObject(“ADODB.Connection”)
cn.Open “…”

‘ 何らかの処理(ここでエラーが発生する可能性がある)
Dim ws As Worksheet
Set ws = Worksheets(“存在しないシート”) ‘ 💥ここでエラー!

cn.Close
Set cn = Nothing
Exit Sub

ErrorHandler:
‘ エラー処理のつもりだが、cn が閉じられていない!
MsgBox “エラーが発生しました。”
End Sub

このコードの致命的な欠陥は、`Worksheets(“存在しないシート”)` でエラーが発生した瞬間、処理が `ErrorHandler` ラベルにジャンプし、ADODB.Connection(外部リソース)が接続されたまま放置される点にある。
結果として、Excelのプロセスが背後でメモリを食いつぶし続け、ファイルロックやリソース枯渇を引き起こす。これがVBAにおける「静かなるメモリリーク」の正体だ。

2. 堅牢な設計:Single Exit & Cleanup パターン

プロフェッショナルなVBA開発者が守るべき鉄則は一つ。
「出口(Exit)は常に1つに絞り、すべての終了パスは必ずクリーンアップを通るようにせよ」

これいを具現化するのが、Single Exit & Cleanup パターンである。

プロダクションコードのテンプレート

以下のコードは、ファイル操作、DB接続、オブジェクト生成を伴う実務レベルの処理を想定した、コピペして即戦力となるテンプレートだ。

Option Explicit

Public Sub ProcessBusinessLogic_Production()
‘ — 1. 変数の宣言(スコープはプロシージャ先頭で一元管理) —
Dim fso As Object
Dim cn As Object
Dim rs As Object
Dim targetFile As String
Dim isSuccess As Boolean

‘ 初期化
isSuccess = False
targetFile = “C:\Data\TargetLog.txt”

‘ — 2. エラーハンドラーの有効化 —
On Error GoTo ErrorHandler

‘ — 3. リソースの確保とメイン処理 —
Set fso = CreateObject(“Scripting.FileSystemObject”)
Set cn = CreateObject(“ADODB.Connection”)

‘ DB接続シミュレーション
‘ cn.Open “Provider=…”

‘ ファイルが存在するかチェック
If Not fso.FileExists(targetFile) Then
Err.Raise Number:=vbObjectError + 1, Description:=”指定されたログファイルが存在しません。”
End If

‘ (ここにメインの業務ロジックが続く)

‘ 正常終了フラグの立て直し
isSuccess = True

CleanUp:
‘ — 4. 厳格なクリーンアップ処理(正常・異常にかかわらず必ず通過) —
‘ オブジェクトの開放は「生成した順とは逆の順序」が鉄則
On Error Resume Next ‘ 解放時の予期せぬエラーで処理が止まるのを防ぐ

If Not rs Is Nothing Then
rs.Close
Set rs = Nothing
End If

If Not cn Is Nothing Then
If cn.State = 1 Then cn.Close ‘ 接続が開いている場合のみ閉じる
Set cn = Nothing
End If

Set fso = Nothing
On Error GoTo 0 ‘ エラー捕捉を通常に戻す

‘ 終了通知
If isSuccess Then
‘ Debug.Print “正常終了しました。”
End If
Exit Sub

ErrorHandler:
‘ — 5. 異常系ハンドリング —
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“内容: ” & Err.Description, vbCritical, “システムエラー”

isSuccess = False
Resume CleanUp ‘ 必ずクリーンアップラベルへ強制誘導する

End Sub

3. コードの急所:プロが押さえるべき3つの技術的ポイント

① `Resume CleanUp` による処理の合流

エラーが発生した際、`MsgBox` でユーザーに通知した後、すぐにプロシージャを抜け出てはならない。必ず `Resume CleanUp` を用いて、正常系と同じクリーンアップのルートを通らせる。これにより、「リソース解放ロジックの重複(コピペコード)」を完全に排除できる。

② 解放時の `On Error Resume Next` の防壁

オブジェクトを解放するコード(`rs.Close` や `cn.Close` など)は、すでにオブジェクトが破棄されている場合や、接続が既に切断されている場合にエラーを吐くことがある。
クリーンアップの最中に新たなエラーで処理が中断するのを防ぐため、一時的に `On Error Resume Next` を挟み、最後に `On Error GoTo 0` で元に戻すのが定石である。

③ オブジェクトの逆順解放

複数のCOMオブジェクト(例: Connection -> Recordset)を扱う場合、生成した順番とは逆の順序で `Set xxx = Nothing` を実行すること。依存関係のあるオブジェクトを正しい順序で解放しないと、COMの参照カウントが正しくデクリメントされず、メモリリークの温床となる。

4. まとめ:保守性の高いコードとは「後始末が美しいコード」だ

動くだけのコードを書くのは素人でもできる。しかし、「異常系であってもリソースを完全にクリーンアップし、次に備えて初期状態を維持するコード」を書くことこそが、プロのエンジニアの仕事である。

業務自動化ツールが長期間安定して稼働するかどうかは、突き詰めればこの「エラーハンドリングとメモリ管理の作法」にかかっている。ぜひ、あなたのプロジェクトのコードベースにもこのパターンを導入し、堅牢で美しいシステム構築を実現してほしい。

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