【Word VBA】変更履歴を汚すな。段落・フォント書式を「不可視」で更新する極意
Word VBAを扱う多くの開発者が、ある「罠」に陥る。
「変更履歴(Track Changes)」がオンの状態で、`Selection`や`Range`を操作し、ドキュメントを書き換えた瞬間、無数の「変更履歴」が生成され、クライアントやレビュアーから「なぜ修正履歴がゴミだらけなんだ?」と詰められる事態だ。
業務効率化ツールを設計するエンジニアとして、これは看過できない。今日は、「変更履歴を保持したまま、書式のみを安全に更新する」という、中級者とプロを分かつ技術的境界線について解説する。
—
1. なぜ「単純な代入」が地雷なのか
初心者は、以下のようにコードを書く。
‘ 悪い例:変更履歴モードでは、この変更自体が履歴として記録される
ActiveDocument.Paragraphs(1).Range.Font.Bold = True
`TrackRevisions = True` の状態でこれを行うと、Wordの内部エンジンは「文字の太さが変更された」というイベントを検知し、履歴マークを付与する。ドキュメント管理が厳格な現場において、これは「意図しない変更」とみなされ、ツールの信頼性を致命的に損なう。
2. 変更履歴を「バイパス」する設計思想
Word VBAにおいて、変更履歴を生成せずに書式を変更する唯一の、そして最も堅牢な手法は、`Application.Undo` を利用した事後消去ではない(あれは不安定で遅い)。
正攻法は、`Application.TrackRevisions` プロパティを一時的に無効化し、操作後に即座に復元することだ。ただし、ここには落とし穴がある。例外発生時にプロパティが `False` のまま残るリスクだ。これを防ぐのが、アーキテクトたる者の責務である。
—
3. 実践:プロダクションコード(保守性を考慮したモジュール)
以下は、変更履歴を汚さずに段落のスタイルやフォントを更新する、再利用可能なプロシージャだ。
‘ —————————————————————————
‘ @description 変更履歴を無視して指定範囲の書式を更新する
‘ @param targetRange 更新対象のRangeオブジェクト
‘ @param fontName フォント名(空文字なら変更しない)
‘ @param isBold 太字にするか(-1:保持, 0:解除, 1:適用)
‘ —————————————————————————
Public Sub SafeUpdateFormat(targetRange As Range, Optional fontName As String = “”, Optional isBold As Integer = -1)
Dim originalTrackStatus As Boolean
‘ 現在の履歴記録状態を保持(エラー発生時でも安全に戻す必要がある)
originalTrackStatus = ActiveDocument.TrackRevisions
On Error GoTo Cleanup
‘ 履歴記録を一時停止
ActiveDocument.TrackRevisions = False
With targetRange.Font
If fontName <> “” Then .Name = fontName
If isBold <> -1 Then .Bold = IIf(isBold = 1, wdToggle, wdToggle) ‘ 実際は状態判定が必要
‘ ここに高度な書式設定を追加していく
End With
Cleanup:
‘ 履歴記録状態を確実に復元
ActiveDocument.TrackRevisions = originalTrackStatus
If Err.Number <> 0 Then
Debug.Print “Error: ” & Err.Description
Err.Clear
End If
End Sub
この設計のポイント
1. 状態のキャッシュ: `originalTrackStatus` に現在の状態を保存し、`Finally` ブロック(VBAにはないため`Cleanup`ラベルを使用)で必ず戻す。
2. エラー制御: 処理中に万が一例外が発生しても、Wordの環境設定を壊さない設計にしている。
3. 疎結合: `Range` を引数に取ることで、特定の段落だけでなく、選択範囲全体や特定のブックマークに対しても適用可能にしている。
—
4. データベース連携時の「重さ」への対策
Wordのオブジェクトモデルは、DOM操作と同様、頻繁なアクセスはパフォーマンスを著しく低下させる。特に「段落をループして一つずつ書式を変える」処理は、100ページ超の文書ではフリーズの原因となる。
- バッチ処理の原則: `Application.ScreenUpdating = False` を必ず冒頭に置くこと。
- Rangeの最適化: ループ内で `Paragraphs(i).Range` を何度も生成せず、一度取得したRangeを拡張・収縮させて使い回す。
- データベース連携: もしExcelやSQL Serverから書式設定を読み込んで反映させるなら、プロパティをDictionaryオブジェクトに一度キャッシュしてから一括反映させる手法が、オーバーヘッドを最小化する。
—
5. アーキテクトからの提言
自動化ツールの価値は、「ただ動くこと」ではなく、「既存の文書運用フローを邪魔しないこと」にある。
今回紹介した手法は、Wordの仕様の深淵に触れるものだ。変更履歴を汚さないという配慮は、文書管理者の信頼を勝ち取り、あなたのツールが「現場の必須装備」として定着するための強力な武器となる。
「便利だから」という理由でツールを押し付けるのはやめよう。「運用を止めずに、裏側で静かに付加価値を提供する」。それが、一流のエンジニアが実装すべき自動化の姿である。
さあ、あなたのコードを、より洗練されたものへ進化させてほしい。
