Word VBAの深淵:Findオブジェクトの非同期処理的発想と、ハイライトの「一括適用」術
業務自動化の世界において、Word VBAの`Find`オブジェクトは諸刃の剣だ。多くの初学者は`Selection.Find.Execute`を繰り返し、画面描画を伴う低速なループに陥る。だが、真のエンジニアは知っている。Wordの`Range`オブジェクトが持つ「ポインタ」としての性質を理解すれば、処理速度は劇的に向上する。
今回は、検索結果をメモリ上の配列に一時退避させ、置換後に「一括で書式を適用する」という、プロフェッショナルが用いる最適化手法を伝授する。
—
なぜ「逐次処理」は悪なのか
`Find.Execute`をループ内で走らせ、見つかるたびに`Replacement.Format = True`を実行する。これは、Wordの描画エンジンである`WinWord.exe`に対して、都度「再描画」と「レイアウト計算」を強要する行為だ。
大規模なドキュメントでこれをやれば、処理時間は指数関数的に増大する。我々が目指すべきは、「検索(論理抽出)」と「書式適用(描画)」の完全分離である。
実装の極意:Range配列へのキャッシュ戦略
まず、検索で見つかった`Range`オブジェクトを、`Collection`や`Array`に格納する。このとき、重要なのはRangeの「開始位置」と「終了位置」だけを保持するのではなく、Rangeオブジェクトそのものを参照として保持することだ。
プロフェッショナルな実装コード
Option Explicit
‘ 検索結果をメモリにキャッシュし、最後に一括で書式を適用するプロシージャ
Public Sub OptimizedHighlightReplacement()
Dim rngSearch As Range
Dim colResults As Collection
Set colResults = New Collection
‘ 検索対象の範囲を設定(ドキュメント全体)
Set rngSearch = ActiveDocument.Content
‘ 画面更新を停止し、レイアウト計算をスキップ(パフォーマンスの要)
Application.ScreenUpdating = False
With rngSearch.Find
.ClearFormatting
.Text = “検索キーワード”
.Forward = True
.Wrap = wdFindStop
‘ 検索ループ:見つかったRangeをメモリ上のコレクションに蓄積
Do While .Execute
‘ 重複参照を避けるため、Rangeを複製して保存
colResults.Add rngSearch.Duplicate
Loop
End With
‘ ここで置換処理を実行(あるいは先に置換して、後からハイライトを当てる)
‘ 今回はハイライト一括適用にフォーカス
Dim rngItem As Range
For Each rngItem In colResults
‘ 一括処理の利点:書式変更を個別にWinWordへ通知するのではなく、
‘ 保持しているRangeオブジェクトの属性を直接書き換える
rngItem.HighlightColorIndex = wdYellow
‘ 念のため、明示的にオブジェクトを開放(VBAのGCを過信しない)
Set rngItem = Nothing
Next rngItem
‘ 画面更新を再開
Application.ScreenUpdating = True
‘ オブジェクトの明示的解放
Set colResults = Nothing
Set rngSearch = Nothing
End Sub
伝説的アーキテクトからの知見
1. Rangeの「Duplicate」の重要性
`rngSearch`をそのままコレクションに突っ込んではならない。`Find`が次に進むたびに、同じインスタンスを指しているRangeの中身も書き換わってしまうからだ。必ず`.Duplicate`メソッドを使い、メモリ上に独立したコピーを作成せよ。これが「検索の連鎖」を破壊せずに結果を保持する唯一の手段だ。
2. メモリの断片化と解放
VBAのガベージコレクションは非常に緩やかだ。特に`Collection`オブジェクトを用いた場合、ループ終了後に`Set = Nothing`を明示しなければ、巨大なドキュメントではメモリリークの原因となる。Windows APIの`GlobalMemoryStatusEx`などを利用するまでもなく、VBAレベルでの「寿命管理」を徹底するだけで、レガシーシステムの安定性は段違いに向上する。
3. システム間連携の要:Rangeの「Start/End」への置換
外部システム(DBやAPI)から受け取った置換ルールを適用する場合、Rangeオブジェクトを直接渡すのは危険だ。その場合、一度`Long`型の`Start`と`End`のインデックスに変換し、ドキュメントの構造変化(行挿入など)に追従できる設計にする必要がある。
結論
Word VBAにおいて、「処理が遅い」と感じるなら、それはコードのせいではなく、「Wordの描画パイプライン」を意識していない設計のせいだ。
- 検索と適用を分離せよ。
- 描画更新をオフにせよ。
- Rangeの寿命を厳格に管理せよ。
この3点を守るだけで、あなたの書く自動化ツールは、ただのスクリプトから「堅牢なエンジン」へと昇華するはずだ。技術は裏切らない。コードの背後にあるアーキテクチャの真実を見極めよ。
