こんにちは! Word VBAの世界へようこそ。
マクロの記録ボタンを押してコードを眺めてみたものの、「なんだか複雑で、狙った通りに動かないな…」と感じていませんか?
特に、法律の契約書やマニュアルなど、実務でよく使われる「変更履歴の記録」がオンの状態での文字置換は、多くのWord VBAエンジニアが一度はハマる「魔の領域」です。
今回は、変更履歴のトラップを華麗にかわし、履歴を残しながら安全に置換をやり切るための極意を、優しく丁寧にお伝えします。ここをクリアすれば、あなたのWord VBAスキルは間違いなく中級者の領域に到達しますよ!
—
1. なぜ「変更履歴オンでの置換」は罠だらけなのか?
Wordの「変更履歴の記録」がオンのとき、人間が手作業で文字を打ち直すと、古い文字には「取り消し線」、新しい文字には「下線」がついて記録されますよね。
これをVBAの `.Execute Replace:=wdReplaceAll`(一括置換)でやろうとすると、Wordの内部エンジンは大パニックを起こします。
一瞬で数千箇所の置換処理走ると、変更履歴のマーキング処理が追いつかなくなったり、「置換したはずの文字列が、次の置換対象に巻き込まれて無限ループする(あるいは意図しないゴミが残る)」という恐ろしい現象が発生します。
現場でこれをやると、文書がぐちゃぐちゃになり、クライアントから青い顔で連絡が来る原因になります。では、プロはどうするか?
答えは簡単です。「一括置換(ReplaceAll)の魔法を捨て、1つずつ丁寧に『変更履歴モードをまとわせた手動置換』をループで回す」のです。
—
2. 変更履歴を守りながら安全に置換する実装パターン
それでは、実際に現場でそのまま使える実用コードを見てみましょう。
今回は、文書全体の「旧商品名」を「新商品名」に、変更履歴を残したまま安全に置き換えるマクロです。
Sub SafeReplaceWithTrackChanges()
Dim rngTarget As Range
Dim targetText As String
Dim replaceText As String
‘ 検索する言葉と、置き換える言葉を定義
targetText = “旧商品A”
replaceText = “新商品B”
‘ 1. 文書全体の保護(変更履歴がオンか確認・強制設定)
If ActiveDocument.TrackRevisions = False Then
ActiveDocument.TrackRevisions = True
End If
‘ 2. 検索範囲(Rangeオブジェクト)を文書の先頭に設定
Set rngTarget = ActiveDocument.Content
‘ 3. 検索条件の構築(Findオブジェクトの作法)
With rngTarget.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = targetText
.Replacement.Text = replaceText
.Forward = True
.Wrap = wdFindStop ‘ 文書の最後まで来たらストップする(重要!)
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcard = False
End With
‘ 4. ループを回して「1つずつ」確実に置換していく
‘ ※一括置換(wdReplaceAll)は使わず、.Execute単体で判定する
Do While rngTarget.Find.Execute
‘ 【重要】すでに置換された範囲を再検索して無限ループに陥るのを防ぐため、
‘ 範囲の末尾にカーソル(Range)を移動させてから次の検索へ進む
rngTarget.Collapse wdCollapseEnd
Loop
‘ 後片付け
Set rngTarget = Nothing
MsgBox “変更履歴を残したまま、置換が安全に完了しました!”, vbInformation, “処理完了”
End Sub
—
3. コードのポイントと「知っておくべき極意」
上記のコードには、Word VBAのアーキテクチャを知り尽くしたエンジニアならではの「安全装置」が組み込まれています。初心者の方がつまずきやすいポイントを紐解いていきましょう。
① `.Wrap = wdFindStop` と `Collapse` のコンボ
一括置換ではなくループ処理(`Do While`)を使う場合、検索範囲の終端管理が命です。
`.Wrap = wdFindStop` を指定することで、文書の最後まで検索が終わった時にピタッと止まってくれます。さらに、見つけた瞬間に `rngTarget.Collapse wdCollapseEnd` で検索開始位置を「見つかった単語のすぐ後ろ」に移動させます。これを怠ると、同じ場所を永遠にループし続けるバグ(無限地獄)が完成してしまうので注意してください。
② なぜ `.Execute Replace:=wdReplaceAll` を使わないのか?
「コードを短くしたいから一括置換したい!」という誘惑に駆られますが、変更履歴がオンのときの `wdReplaceAll` は、Wordの内部でイベントや履歴の描画競合を起こし、最悪の場合Wordが強制終了(フリーズ)します。
「1つずつ見つけて、変えて、進む」という地道なループこそが、変更履歴を正確に残すための唯一にして最良の道なのです。
—
4. 陥りやすいエラーと対処法
Q. 実行すると「実行時エラー 4605: このメソッドまたはプロパティは、変更履歴の記録中には使用できません。」が出ます
A. 変更履歴がオンのときに、直接 `.Text` プロパティを書き換えようとしたり、特定の保護がかかった領域を操作しようとするとこのエラーが出ます。
今回のコードのように `Find` オブジェクトと `.Execute` を正しい手順で使っていれば回避できますが、もし文書に「編集制限(パスワード保護)」がかかっている場合は、マクロの冒頭でパスワードを解除する処理(`ActiveDocument.Unprotect “password”`)が必要になります。
—
まとめ:ここをクリアすればWord VBAは怖くない!
お疲れ様でした! 変更履歴が絡む置換処理は、Word VBAにおける最初の大きな壁です。
しかし、「一括置換の幻想を捨て、Rangeをコントロールしながら1つずつループさせる」というアプローチを理解できたあなたなら、もうマクロの記録の奴隷ではありません。
現場で「変更履歴を残したまま一括置換して!」と無茶振りをされたら、ぜひこのコードを思い出してくださいね。
ここをクリアしたあなたなら、どんな複雑な文書自動化もバッチリこなせます。次のステップへ向けて、一緒に楽しくコードを書いていきましょう!
