【実務・中級編】【中級者向け】置換処理中に発生する「検索対象が見つからない」エラーをスマートに回避する – Word VBA解析バイブル

スポンサーリンク

【Word VBA】Find.Executeの裏切りを見抜け!「検索対象なし」エラーと無限ループを完全封殺する堅牢な置換設計

Word VBAにおける文字・文字列の置換処理は、一見すると非常にシンプルだ。
`Selection.Find` や `Range.Find` を叩き、`Replacement.Execute` を回せばいい――そう思ってコードを書き、いざ大量のドキュメントを処理した瞬間にマクロが停止する、あるいは永遠に終わらない「フリーズ(無限ループ)」の悪夢に直面したことはないだろうか。

開発現場でよく見かける、こんな稚拙なコードを思い出してほしい。

‘ 【アンチパターン】絶対に書いてはならない危険なコード
Sub BadReplacement()
Selection.Find.ClearFormatting
Selection.Find.Text = “旧用語”
Selection.Find.Replacement.Text = “新用語”
‘ 戻り値を無視してループさせると、見つからない場合に無限ループの罠に嵌まる
Do
Selection.Find.Execute Replace:=wdReplaceAll
Loop Until Selection.Find.Found = False ‘ ←これでは不十分!
End Sub

チーフアーキテクトである私から言わせれば、「Find.Executeの戻り値(Boolean)を評価せず、`.Found` プロパティの挙動に全幅の信頼を置くこと」は、地雷原を裸足で歩くようなものだ。

今回は、Wordの検索・置換エンジン(COMオブジェクト)の裏側の挙動を完全に掌握し、どんな例外的なドキュメントが来ても絶対にクラッシュしない、プロダクション品質の堅牢な置換ロジックを伝授しよう。

1. なぜ `.Execute` は開発者を裏切るのか?

Wordの `Find` オブジェクトは、Excel(Range.Find)とは異なり、文書全体のカーソル位置やストーリー(本文、ヘッダー、フッター、脚注など)を跨いで複雑なステート(状態)を保持している。

`Find.Execute` メソッドは、検索文字列がヒットすれば `True` を、しなければ `False` を返す。しかし、実務でこのメソッドを使う際、以下の罠に注意しなければならない。

1. 「見つからない」はエラーではなく「False」を返す
対象が存在しない場合、VBAの実行時エラー(ランタイムエラー)にはならず、単に `False` が返る。これをハンドリングせず後続処理を進めると、意図しない範囲を巻き込んだり、ロジックが破綻する。
2. `wdReplaceAll` の幻影
一括置換(`Replace:=wdReplaceAll`)を使えば一発で終わるように思えるが、巨大な文書や複雑な表・セクションを跨ぐ場合、途中で処理がロストしたり、一部のストーリー(ヘッダー等)が取り残されたりする。厳密な制御が必要な業務ツールでは、ループによる個別置換が不可欠となる。
3. 無限ループの無限地獄
検索方向(`Forward`)やラップ(`Wrap`)の設定を誤ると、文書の末尾から先頭に戻り、永遠に同じ文字列を置換し続けるバグが完成する。

2. 堅牢な置換ロジックの設計思想

プロダクションコードとして通用する要件はたった一つ。「例外を想定し尽くし、どんな初期状態の文書であっても安全に完了すること」だ。

そのために、以下の設計原則を遵守する。

  • Selectionに依存しない(Rangeオブジェクトの局所化)

画面のチラツキ(ScreenUpdating)を防ぎ、処理速度を劇的に向上させるため、必ず `Range` オブジェクトに対して `Find` を実行する。

  • 戻り値(Boolean)を厳格に評価する

`Execute` メソッドの返り値を直接 `If` 文の条件式に組み込み、ヒットしなかった瞬間に即座にループを抜け出す。

  • 検索パラメーターの初期化(ClearFormatting / ClearAllParameters)

前回実行時のゴミ(書式設定など)が残っていると検索に失敗するため、必ず初期化を行う。

3. 【コピペOK】実務で使える究極の安全置換プロシージャ

以下に提供するコードは、私が実際の基幹システム連携ドキュメント自動生成ツールで採用している、エラー耐性を極限まで高めた堅牢な置換エンジンだ。

Option Explicit

Public Sub SafeReplaceEngine()
Dim targetDoc As Document
Set targetDoc = ActiveDocument ‘ 処理対象ドキュメント

Dim targetString As String
Dim replacementString As String

targetString = “旧システム名”
replacementString = “新クラウド基盤”

Dim successCount As Long
successCount = 0

‘ 画面描画を停止し、処理速度を限界まで引き上げる
Application.ScreenUpdating = False
Application.Calculation = wdCalculationManual

On Error GoTo ErrorHandler

‘ 文書のメインストーリー(本文)を表すRangeを取得
Dim rngSearch As Range
Set rngSearch = targetDoc.Content

With rngSearch.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = targetString
.Replacement.Text = replacementString
.Forward = True
.Wrap = wdFindStop ‘ 文書末尾に達したら検索を停止する(重要:無限ループ防止)
.Format = False
.MatchCase = True
.MatchWholeWord = False
.MatchWildcards = False
End With

‘ Executeメソッドの戻り値でループを制御する
‘ wdReplaceOneを指定し、1件ずつ確実に置換とカウントを行う
Do While rngSearch.Find.Execute(Replace:=wdReplaceOne) = True
successCount = successCount + 1

‘ 【重要】置換直後のRangeは「置換された文字列の末尾」に移動しているため、
‘ 次の検索のためにRangeの末尾をドキュメントの終端まで再設定し、
‘ かつ無限ループを防ぐためにポインタを1文字進める
rngSearch.Collapse wdCollapseEnd
rngSearch.End = targetDoc.Content.End

‘ 安全装置:無限ループ検知(保険)
If successCount > 10000 Then
Err.Raise vbObjectError + 1000, “SafeReplace”, “置換回数が上限(10,000件)を超えました。無限ループの可能性があります。”
End If
Loop

‘ 正常終了処理
Application.ScreenUpdating = True
Application.Calculation = wdCalculationAutomatic

MsgBox “置換処理が完了しました。” & vbCrLf & _
“置換成功件数: ” & successCount & ” 件”, vbInformation, “処理成功”

Exit Sub

ErrorHandler:
‘ 異常終了時も必ず画面描画と計算方法を復元する(これを怠るとWordがフリーズ状態になる)
Application.ScreenUpdating = True
Application.Calculation = wdCalculationAutomatic

MsgBox “エラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“詳細: ” & Err.Description, vbCritical, “致命的エラー”
End Sub

4. チーフアーキテクトからの実装上の急所アドバイス

上記のコードにおいて、プログラミング中級者が最もハマりやすいポイントを解説しておこう。

① `.Wrap = wdFindStop` の絶対性

`wdFindContinue` や `wdFindAsk` を設定していると、文書の末尾まで検索した後に先頭に戻ってしまい、運悪く条件が噛み合わなくなると無限ループの渦に飲み込まれる。プログラム制御による置換を行う場合は、必ず `wdFindStop` を指定し、検索範囲の終端に達したらスパッと処理を終わらせる設計にすべきだ。

② `rngSearch.Collapse wdCollapseEnd` の意味

`Find.Execute` が成功すると、`rngSearch` 自体が「見つかった文字列の範囲」に縮小(再定義)される。そのまま次のループを回すと、同じ文字列を延々と再検索してしまう
そのため、ヒットした直後に `.Collapse wdCollapseEnd` でレンジを「ヒットした文字列の末尾の点」に変形させ、さらに `.End = targetDoc.Content.End` で検索範囲の終端をドキュメントの最後まで再拡張するという、「ポインタの前進操作」が不可欠なのだ。

③ アプリケーション状態の確実な復元(トランザクション的思考)

VBAでマクロを書く際、`ScreenUpdating = False` や `Calculation = wdCalculationManual` を使うのは常道だが、途中でエラー(Err.Raiseや予期せぬ例外)が発生した際にこれらを戻し忘れると、ユーザーのWordがバグったような挙動を示し、最悪の場合は未保存データを巻き込んで強制終了する。
必ず `On Error GoTo ErrorHandler` を経由させ、いかなる結末を迎えても環境をクリーンアップして返す設計を徹底してほしい。

総括

「検索対象が見つからない」という日常的な事象は、VBAにおいては「制御を誤ればシステムを崩壊させるトリガー」になり得る。
単に動くコードを書くフェーズは卒業し、「失敗することを前提にし、失敗した時あるいはヒットしなかった時にどう美しく安全に着地するか」をデザインすること。それこそが、現場で信頼される真の業務自動化エンジニアの仕事である。

この知見をあなたのツール開発に組み込み、バグとは無縁の強靭なWordオートメーションを実現してほしい。

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