【実務・中級編】【上級者向け】「検索と置換」の実行時エラーをキャッチする堅牢なラッパー関数の設計 – Word VBA解析バイブル

スポンサーリンク

【Word VBA】「検索と置換」の実行時エラーをキャッチする堅牢なラッパー関数の設計

開発現場でWord VBAを用いたドキュメント自動化を推進する時、多くのエンジニアが最初に直面する壁が、`Range.Find` や `Selection.Find` の気まぐれな挙動と、それに伴う突然の実行時エラーだ。

「なぜ、前日まで動いていたマクロが今日は止まるのか?」
「保護されたセクション、変更履歴の競合、表のセル結合構造の歪み……」

実務の現場で扱うWordファイルは、ユーザーが自由勝手に編集した「地雷原」のようなものだ。素朴な `Find.Execute` をそのままコードに書くのは、目隠しをして地雷原を歩くようなものに等しい。

今回は、予期せぬ文書構造の破壊や保護領域によるクラッシュを完全に無力化し、エラーを美しくハンドリングして処理を継続する「プロ仕様のラッパー関数」の設計思想と実装コードを伝授する。

—

1. なぜ標準の `Find` は実務で通用しないのか?

Word VBAの `Find` オブジェクトは、Excelの `Range.Find` とは異なり、Wordの「UI(ユーザーインターフェース)の状態」と深く結びついている。

実務で頻発する3大クラッシュ要因

1. 保護された文書・領域(Content Protection / Forms Protection)
編集が制限された領域に対して置換を試みた瞬間、容赦なく「実行時エラー」が吐き出される。
2. 特殊なストーリー(ヘッダー、フッター、脚注、テキストボックス)へのアクセス制約
メインストーリー以外を走査する際、特定の条件下でポインターが無効化し、メモリリークや強制終了を引き起こす。
3. 未初期化のオブジェクト参照
`.ClearFormatting` を怠った状態で前回の検索条件が残留し、意図しない無限ループやヒット漏れが発生する。

これらを個別の `On Error Resume Next` でその場しのぎに網羅しようとすると、コードはスパゲッティ化し、本当のバグを見逃す最悪のアーキテクチャが完成する。必要なのは、「例外を局所化し、トランザクション的に安全に実行するラッパー」である。

—

2. 堅牢なラッパー関数の設計思想

プロダクションコードとして耐えうるラッパー関数には、以下の要件が求められる。

  • 状態の完全なカプセル化: 検索前後のオプション(フォント、段落書式など)を汚染しない。
  • 明確なエラーの分類: 「文書保護による拒否」「ヒットゼロ(正常系)」「予期せぬシステムエラー」を厳密に区別する。
  • 監査ログの出力: どの段落、どのセクションで例外が発生したかを特定できる情報を残す。

—

3. 【実装】プロダクション・ラッパー関数

以下のコードは、実務の現場でそのまま組み込める堅牢な設計を施したラッパー関数だ。コピペしてモジュールに貼り付け、その構造の美しさと強靭さを体感してほしい。

Option Explicit

‘ =================================================================================
‘ módulo名: Mdl_RobustFind
‘ 概要 : 実行時エラーを完璧に制御するWord検索・置換ラッパー関数
‘ =================================================================================

Public Enum FindErrorPolicy
cepFailFast = 1 ‘ エラー時に即座に処理を中断する
cepLogAndContinue = 2 ‘ エラーをログに記録して次の処理へ進む
End Enum

/

  • 指定されたRangeに対して安全に検索・置換を実行するプロ仕様のラッパー
  • @param TargetRange 検索対象のRangeオブジェクト
  • @param FindText 検索する文字列
  • @param ReplaceText 置換後の文字列(空文字の場合は検索のみ)
  • @param MatchCase 大文字・小文字を区別するか
  • @param MatchWholeWord 単語全体に一致するか
  • @param ErrorPolicy エラー発生時のポリシー
  • @return Long 置換(またはヒット)した回数

/
Public Function SafeExecuteFindAndReplace( _
ByVal TargetRange As Range, _
ByVal FindText As String, _
Optional ByVal ReplaceText As String = “”, _
Optional ByVal MatchCase As Boolean = False, _
Optional ByVal MatchWholeWord As Boolean = False, _
Optional ByVal ErrorPolicy As FindErrorPolicy = cepLogAndContinue) As Long

Dim hitCount As Long
hitCount = 0

‘ 引数のバリデーション
If TargetRange Is Nothing Then
Debug.Print “[WARN] SafeExecuteFindAndReplace: TargetRangeがNothingです。”
SafeExecuteFindAndReplace = 0
Exit Function
End If

If Len(FindText) = 0 Then
Debug.Print “[WARN] SafeExecuteFindAndReplace: 検索文字列が空です。”
SafeExecuteFindAndReplace = 0
Exit Function
End If

‘ エラーハンドリングのスコープ設定
On Error GoTo ErrorHandler

With TargetRange.Find
‘ 1. 状態のクリア(前回の検索ゴミを完全にパージする)
.ClearFormatting
.Replacement.ClearFormatting

‘ 2. 条件の設定
.Text = FindText
.Replacement.Text = ReplaceText
.Forward = True
.Wrap = wdFindStop ‘ 範囲外への自動継続を禁止し、制御下に置く
.Format = False
.MatchCase = MatchCase
.MatchWholeWord = MatchWholeWord
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False

‘ 3. 実行とループ処理
Do While .Execute
‘ 保護された領域や読み取り専用セルにヒットした場合、ここでエラーがトラップされる
hitCount = hitCount + 1

If Len(ReplaceText) > 0 Then
‘ 置換モードの場合
‘ 必要に応じてログ記録やイベント処理をここに記述
.Replacement.Execute Replace:=wdReplaceOne
Else
‘ 検索のみの場合(必要ならRangeの操作をここで行う)
‘ 例: TargetRange.HighlightColorIndex = wdYellow
End If

‘ 無限ループ防止と、次の一致箇所へレンジをシフト
‘ ※正確なポインタ移動はWordの仕様上複雑なため、範囲指定検索では
‘ wdFindStopと組み合わせて安全にインクリメントする
TargetRange.Collapse wdCollapseEnd
Loop
End With

SafeExecuteFindAndReplace = hitCount
Exit Function

ErrorHandler:
‘ —————————————————————————–
‘ 異常系ハンドリング
‘ —————————————————————————–
Dim errDesc As String
errDesc = “Error ” & Err.Number & “: ” & Err.Description & _
” (検索文字: ” & FindText & “, 位置: Paragraphs/Range)”

Debug.Print “[ERROR] ” & errDesc

‘ ログファイルや外部DBへの書き込み処理をここに連携可能
‘ Call WriteLogToDatabase(Err.Number, errDesc)

Select Case ErrorPolicy
Case cepFailFast
‘ 処理を中断して上位プロシージャへエラーを伝播
On Error GoTo 0
Err.Raise Err.Number, “SafeExecuteFindAndReplace”, errDesc

Case cepLogAndContinue
‘ エラーを飲み込んで次の処理を継続
Resume Next

Case Else
Resume Next
End Select

End Function

—

4. プロダクションコードとしての解説と実務的注意点

上記のコードには、シニアエンジニアの知見が凝縮されている。実務で導入する際のポイントを解説する。

① `.Wrap = wdFindStop` の採用理由

UI上の「検索と置換」ダイアログボックスでは、文書の最後まで検索すると「先頭から続けますか?」と聞いてくる。これをマクロ内でやられると、予期せぬ無限ループや文書全体の意図しない書き換えの温床になる。`wdFindStop` を指定することで、「指定したRange内だけで完結させる」という厳密なスコープ管理が可能になる。

② エラーポリシーの抽象化 (`FindErrorPolicy`)

すべてのエラーでマクロが停止してしまうと、1,000ページある仕様書のバッチ処理などが「998ページ目のたった1箇所の保護セル」のために止まり、作業全体が台無しになる。
一方で、金融や法務系のドキュメントでは「置換漏れ」が致命傷になるため、ポリシーを引数(`cepFailFast` / `cepLogAndContinue`)で切り替えられる設計にしている点が、プロ仕様たる所以だ。

③ データベースやファイル連携への拡張

`ErrorHandler` ブロック内を見てほしい。ここではイミディエイトウィンドウへの出力(`Debug.Print`)にとどめているが、この部分を社内のログ収集API、あるいはローカルのCSV/SQLiteデータベースへの書き込み処理に差し替えるだけで、「どの文書のどの箇所で自動化が阻害されたか」の完全な監査証跡(ダッシュボード)を構築できる。

—

5. 呼び出し側の実装例

このラッパー関数を実際にどのように業務ツールから呼び出すか、そのクリーンな使用例を示す。

Public Sub RunBatchReplacement()
Dim doc As Document
Set doc = ActiveDocument

Dim replacedTotal As Long

‘ メインボディ全体に対して安全な置換を実行
‘ (例: 旧製品名「Product-A」を「Product-X」に置換)
replacedTotal = SafeExecuteFindAndReplace( _
TargetRange:=doc.Content, _
FindText:=”Product-A”, _
ReplaceText:=”Product-X”, _
MatchCase:=True, _
ErrorPolicy:=cepLogAndContinue)

MsgBox “置換処理が完了しました。合計 ” & replacedTotal & ” 箇所を更新しました。”, vbInformation
End Sub

—

結びにかえて:自動化エンジニアの誇り

「動けばいいや」で作られたVBAコードは、いつか必ず現場を裏切る。特にWordはその複雑なオブジェクトモデルゆえに、ちょっとした文書の構造変化で簡単に牙をむく。

今回解説したラッパー関数のように、「起こり得るエラーを予測し、境界を定義し、システムを優雅に継続させる」ことこそが、プロの業務自動化エンジニアの仕事だ。
あなたの書くコードが、明日の現場のストレスを消し去る強靭なインフラとなることを期待している。

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