【テクニカル・上級編】【上級者向け】「変更履歴」を維持したまま置換を行い、差分を自動生成する – Word VBA解析バイブル

スポンサーリンク

変更履歴を「武器」にする:Word VBAによる差分追跡エンジニアリングの極致

Word VBAにおける`Find`および`Replacement`オブジェクトは、多くの初学者が「単なる文字列置換ツール」と誤解している。しかし、我々アーキテクトにとって、それはWordのドキュメントモデル(DOM)を意図的に破壊し、再構築するための強力なインターフェースに他ならない。

特に「変更履歴(TrackRevisions)」を維持したままの置換は、単に`ActiveDocument.TrackRevisions = True`を叩いて終わりではない。メモリリーク、Undoスタックの肥大化、そしてCOMインターフェースの不安定さを制御しつつ、いかにして「監査可能な差分」を生成するか。

今回は、この極限の課題に対する解法を提示する。

—

1. 破壊的置換を回避するアーキテクチャ

標準の`Range.Find.Execute Replace:=wdReplaceAll`は、高速だが「変更履歴」の記録において制御不能な挙動を示すことが多い。特に、複雑なネスト構造を持つフィールドコードや、インラインシェイプが混在するドキュメントでは、このメソッドはしばしばメタデータを破壊する。

シニアエンジニアが採用すべき戦略は、「Rangeオブジェクトの逐次走査」と「Undoユニットの論理的グループ化」である。

実装のコアロジック

‘ 変更履歴を維持した置換エンジン
Public Sub PreciseReplace(ByRef targetRange As Range, ByVal findText As String, ByVal replaceText As String)
Dim rng As Range
Set rng = targetRange.Duplicate

‘ Wordの変更履歴エンジンを明示的に有効化
ActiveDocument.TrackRevisions = True

‘ Undoスタックを汚染しないためのグループ化
Application.UndoRecord.StartCustomRecord “PreciseReplace_Action”

With rng.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = findText
.Replacement.Text = replaceText
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchCase = False

‘ 一括置換ではなく、ループによる逐次置換でイベントを制御する
Do While .Execute
‘ ここで必要に応じてWindows APIを叩き、メモリの状態を確認することも可能
‘ 巨大なドキュメントではDoEventsを適切に配置し、プロセッサを解放する
DoEvents
rng.Collapse wdCollapseEnd
Loop
End With

Application.UndoRecord.EndCustomRecord

‘ オブジェクトの明示的解放(VBAではSet Nothingが重要)
Set rng = Nothing
End Sub

—

2. メモリ最適化とCOMの死角

Word VBAにおいて`Set rng = Nothing`を怠ることは、単なる行儀の問題ではない。`Range`オブジェクトは内部的にWordのDOMと深く結びついており、ループ内で生成されたオブジェクトが解放されないと、ガベージコレクションが機能せず、数千箇所の置換処理でアプリケーションのメモリ使用量が数GBに達する。

伝説的アーキテクトの戒め:

  • Duplicateメソッドの活用: `Selection`を直接操作するな。必ず`Range.Duplicate`でコピーを作成し、メインのDOMと切り離せ。
  • イベントの無効化: `Application.ScreenUpdating = False` は必須だが、さらに`Application.DisplayAlerts = wdAlertsNone`を組み合わせることで、置換過程で発生する予期せぬダイアログを抑制せよ。

—

3. レガシー環境でのAPI活用:Windows APIの介入

大規模なドキュメントを処理する際、Wordの内部クロックよりも、OS側のスレッド優先度を制御する方が安定する場合がある。例えば、`Sleep`関数を用いてプロセッサの負荷を平準化し、システム全体のレスポンスを維持する手法だ。

If VBA7 Then
Private Declare PtrSafe Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
Else
Private Declare Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
End If

‘ ループ内に配置することで、CPU占有率を抑えつつバックグラウンド処理を安定させる
‘ 10msのSleepは、UIの描画更新と論理処理のバランスを取る黄金比である
Sleep 10

—

4. 結び:なぜ「変更履歴」にこだわるのか

自動化の真の目的は、作業の高速化ではない。「再現性と証跡の確保」である。

変更履歴を維持した置換は、単に「修正しました」という結果を出すだけではない。「いつ、誰が、どのシステムによって、どの文字列がどう変わったか」というメタデータを、ドキュメントそのものに刻み込む行為だ。

この手法をマスターしたエンジニアは、単なるコード書きから、「ドキュメントのライフサイクルを設計するアーキテクト」へと進化する。レガシーな技術スタックの中にこそ、システムを根底から支える本質が隠されていることを忘れてはならない。

諸君、コードを汚すな。システムを設計せよ。

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