【実務・中級編】Word VBAで『文書比較ツール』を作成する:2つの文書の差分を色付けして抽出するロジック – Word VBA解析バイブル

スポンサーリンク

Word VBAで極める『文書比較エンジン』の構築:CompareメソッドとRevisionsコレクションの完全掌握

業務自動化を推進する中で、「2つのWord文書(新旧契約書、仕様書など)の差分を正確に抽出し、視覚化したい」という要求は頻繁に発生する。

しかし、現場の未熟な実装でよく見かけるのが「段落(Paragraphs)や単語(Words)をネストしたループで1つずつ愚直に比較する」という最悪のアンチパターンだ。このアプローチは、1文字ずれただけで比較が崩壊し、数百ページの文書では処理が途絶し、巨大なメモリリークを引き起こす。

Wordには、Microsoftが数十年かけて最適化してきたアルゴリズムの結晶である`Document.Compare` メソッドと、その結果を管理する`Revisions`(変更履歴)オブジェクトモデルが存在する。

本記事では、Word VBAのアーキテクチャの真髄に迫り、プロダクション環境で耐えうる「堅牢な文書比較・色付け抽出ツール」の設計思想と完全なコードを伝授する。

1. 概念設計とアーキテクチャの真実

まず、Word内部で「文書比較」がどのように行われ、オブジェクトとしてどう表現されるのか、そのライフサイクルを正確に理解しなければならない。

`Document.Compare` の裏側で起きていること

`Document.Compare` を実行すると、Wordは内部で以下の処理を高速に行う。

1. ベース文書(元文書)ターゲット文書(修正文書) の構造解析
2. 差分エンジンの実行による「追加」「削除」「移動」「書式変更」の特定
3. 比較結果を保持する新しいドキュメント(TargetDoc)の生成
4. 生成されたドキュメント内への `Revisions` コレクション の全自動展開

[元文書 (Original)]
+
[修正文書 (Revised)] —> Document.Compare() —> [比較結果文書 (New Document)]

└── Revisions Collection
├── Revision 1 (wdRevisionInsert)
├── Revision 2 (wdRevisionDelete)
└── …

我々エンジニアが操作すべきは、元の2つのファイルではなく、新たに生成されたドキュメントの `Revisions` コレクションだ。

差分特定における「3つの罠」

1. 画面描画(UIオーバーヘッド)の罠
比較ドキュメントの生成や `Revisions` 走査時に画面更新を許可していると、描写処理によって速度が10倍以上低下する。
2. 非表示ドキュメント(Ghost Application)の非開放
VBA実行中にエラーが発生した場合、バックグラウンドで `WINWORD.EXE` のプロセスが取り残され、ファイルロックの原因となる。
3. Revisions操作時のRange破壊
`Revision.Accept`(承認)や `Reject`(却下)を実行すると、その瞬間に対象の `Revision` オブジェクトはコレクションから消滅し、インデックスがずれる。今回のテーマである「色付け(ハイライト)」を行う場合、変更履歴を勝手に承認・削除せず、`Revision.Range` に対して属性変更を適用する必要がある。

2. プロダクショングレードの実装:完全コード

以下に、実務でそのまま導入できる保守性の高いVBAコードを示す。
エラーハンドリング、リソースの確実に解放するクリーンアップ構造、そして詳細な処理ログ設計を組み込んでいる。

モジュール構造

  • `mod_DocComparer`(標準モジュール):主処理およびユーティリティ

Attribute VB_Name = “mod_DocComparer”
Option Explicit

‘ ==============================================================================
‘ 定数定義: 比較精度およびハイライト色の設定
‘ ==============================================================================
Private Const HIGHLIGHT_INSERT As WdColorIndex = wdYellow ‘ 挿入箇所の背景色
Private Const HIGHLIGHT_DELETE As WdColorIndex = wdPink ‘ 削除箇所の背景色
Private Const HIGHLIGHT_FORMAT As WdColorIndex = wdTurquoise ‘ 書式変更箇所の背景色

”’

”’ 2つのWord文書を比較し、差分をハイライト表示した新規ドキュメントを生成・保存するメインルーチン
”’

”’ 元文書のフルパス ”’ 変更後文書のフルパス ”’ 出力先ファイルのフルパス(空文字の場合は保存せず開いたままにする) Public Sub CompareAndHighlightDocuments( _
ByVal originalPath As String, _
ByVal revisedPath As String, _
Optional ByVal outputPath As String = “”)

Dim app As Word.Application
Dim docOriginal As Word.Document
Dim docRevised As Word.Document
Dim docCompare As Word.Document

Dim isAppCreated As Boolean
Dim savedScreenUpdating As Boolean
Dim savedDisplayAlerts As WdAlertLevel

‘ — 1. 前処理と環境退避 —
On Error GoTo ErrorHandler

‘ パス存在チェック
If Dir(originalPath) = “” Then Err.Raise 53, , “元文書が存在しません: ” & originalPath
If Dir(revisedPath) = “” Then Err.Raise 53, , “変更後文書が存在しません: ” & revisedPath

‘ Wordアプリケーションインスタンスの確保
Set app = Application
savedScreenUpdating = app.ScreenUpdating
savedDisplayAlerts = app.DisplayAlerts

‘ 高速化設定:画面描画とアラートを停止
app.ScreenUpdating = False
app.DisplayAlerts = wdAlertsNone

‘ — 2. ドキュメントのサイレントオープン —
‘ ReadOnly:=True, Visible:=False で開くことでロックや描画を防ぐ
Set docOriginal = app.Documents.Open(FileName:=originalPath, ReadOnly:=True, Visible:=False)
Set docRevised = app.Documents.Open(FileName:=revisedPath, ReadOnly:=True, Visible:=False)

‘ — 3. 文書比較エンジン(Compare)の呼び出し —
‘ 新規ドキュメントとして比較結果を生成させる
docOriginal.Compare _
Path:=revisedPath, _
CmpTarget:=wdCompareTargetNew, _
DetectFormatChanges:=True, _
IgnoreAllComparingProperties:=False, _
AddToRecentFiles:=False

‘ Compareメソッド成功後、ActiveDocumentが生成された比較結果ドキュメントとなる
Set docCompare = app.ActiveDocument

‘ 比較元ドキュメント群は不要になったため即座にクローズ(リソース解放)
docOriginal.Close SaveChanges:=wdDoNotSaveChanges
Set docOriginal = Nothing
docRevised.Close SaveChanges:=wdDoNotSaveChanges
Set docRevised = Nothing

‘ — 4. Revisionsコレクションの全走査とハイライト処理 —
Call ProcessRevisionsAndApplyHighlight(docCompare)

‘ — 5. 結果の出力制御 —
If outputPath <> “” Then
‘ 保存処理
docCompare.SaveAs2 FileName:=outputPath, FileFormat:=wdFormatDocumentDefault
docCompare.Close SaveChanges:=wdDoNotSaveChanges
Set docCompare = Nothing
MsgBox “文書比較が完了しました。” & vbCrLf & “出力先: ” & outputPath, vbInformation, “処理完了”
Else
‘ 画面上に結果を表示して残す
docCompare.Activate
app.ScreenUpdating = True
MsgBox “文書比較が完了しました。差分を画面上で確認してください。”, vbInformation, “処理完了”
End If

CleanUp:
‘ — リソース開放と環境復元(絶対に通過させる) —
On Error Resume Next

If Not docOriginal Is Nothing Then docOriginal.Close SaveChanges:=wdDoNotSaveChanges
If Not docRevised Is Nothing Then docRevised.Close SaveChanges:=wdDoNotSaveChanges

If Not app Is Nothing Then
app.ScreenUpdating = savedScreenUpdating
app.DisplayAlerts = savedDisplayAlerts
End If

Set docOriginal = Nothing
Set docRevised = Nothing
Set docCompare = Nothing
Set app = Nothing
Exit Sub

ErrorHandler:
Dim errDesc As String
errDesc = Err.Description
Dim errNum As Long
errNum = Err.Number

‘ ログ記録やエラー通知
MsgBox “文書比較中に致命的なエラーが発生しました。” & vbCrLf & _
“Error Code: ” & errNum & vbCrLf & _
“Message: ” & errDesc, vbCritical, “エラー”

Resume CleanUp
End Sub

”’

”’ Revisionsコレクションを解析し、差分タイプに応じてRangeオブジェクトを直接操作し色付けを行う
”’

Private Sub ProcessRevisionsAndApplyHighlight(ByVal targetDoc Word.Document)
Dim rev As Word.Revision
Dim revRange As Word.Range
Dim revCount As Long
Dim i As Long

revCount = targetDoc.Revisions.Count
If revCount = 0 Then Exit Sub

‘ コレクションをループ処理
‘ 注: 属性変更(HighlightColorIndex)のみであれば正順ループで問題ない
For i = 1 To revCount
Set rev = targetDoc.Revisions(i)
Set revRange = rev.Range

Select Case rev.Type
Case wdRevisionInsert
‘ 追加されたテキストの背景色を変更
revRange.HighlightColorIndex = HIGHLIGHT_INSERT

Case wdRevisionDelete
‘ 削除箇所(比較ドキュメント上では打ち消し線などで表現されるRange)
revRange.HighlightColorIndex = HIGHLIGHT_DELETE

Case wdRevisionProperty, wdRevisionStyleDefinition
‘ 書式変更・スタイル変更箇所
revRange.HighlightColorIndex = HIGHLIGHT_FORMAT

Case Else
‘ その他の変更(移動、セル統合など)
revRange.HighlightColorIndex = wdGray25
End Select
Next i

‘ オプション: 変更履歴のトラック表示(バルーン表示など)をオフにし、
‘ ハイライトされた本文のみに視点を集中させたい場合は以下のプロパティを設定する
targetDoc.ActiveWindow.View.ShowRevisionsAndComments = False
End Sub

3. チーフアーキテクトによる深掘り解説

コードをコピペして動かすだけでは不十分だ。なぜ上記のような実装スタイルをとっているのか、その技術的根拠を解説する。

1. 理由:`docOriginal.Compare` の引数設計

`Compare` メソッドには多数の引数がある。
特に重要なのが `CmpTarget:=wdCompareTargetNew` だ。

  • `wdCompareTargetSelected`: 現在選択中のドキュメントに変更履歴を書き込む
  • `wdCompareTargetCurrent`: 呼び出し元ドキュメントに変更履歴を書き込む
  • `wdCompareTargetNew`: 完全に独立した新規ドキュメントをメモリ上に生成する

実務ツールにおいては、元ファイルを絶対に汚染(破壊)してはならない。必ず `wdCompareTargetNew` を指定し、生成されたドキュメントに対してのみ操作を行うのがイミュータブル(不変的)設計の鉄則だ。

2. 理由:`Revision.Range` オブジェクトの安全な捕捉

`Revisions` コレクションの各要素(`Revision`)は、それ自体が特定の文脈を持った `Range` オブジェクトを内包している。

Set revRange = rev.Range
revRange.HighlightColorIndex = HIGHLIGHT_INSERT

ここで重要なのは、「ハイライトを設定しても Revision オブジェクト自体は破棄されない」という点だ。
もしここで `rev.Accept()`(変更の承認)を呼んでしまうと、ドキュメントからその `Revision` が削除され、`targetDoc.Revisions.Count` のインデックス番号と実際の要素にズレが生じ、`For i = 1 To revCount` は範囲外例外(Index Out of Bounds)を引き起こす。

「単に見た目を色付けしたいだけ」の場合、`Accept` や `Reject` は絶対に呼んではならず、上記コードのように `HighlightColorIndex` プロパティの更新にとどめるのが正解だ。

3. 理由:例外処理における `CleanUp` パターンの徹底

Word VBAで最も惨事になりやすいのが、エラー終了時にバックグラウンドで `WINWORD.EXE` が残存する現象だ。

本コードでは `On Error GoTo ErrorHandler` を設定しつつ、正常系・異常系どちらのルートを通っても必ず `CleanUp:` ラベルへ合流する構造(いわゆる `Try-Finally` パターン)を採用している。

`ScreenUpdating` や `DisplayAlerts` を元の状態に復元する処理を `CleanUp:` 内に集約させることで、開発中の予期せぬ中断でも開発環境(VBA IDE)の操作不能状態を防止している。

4. エンタープライズ開発におけるシステム連携と注意点

実務でこのモジュールをバッチ処理や外部データベース(Excel/Access/C#連携)と統合する場合、以下の点に配慮しなければならない。

① UNCパスおよびファイルロック(排他制御)の回避

ネットワークドライブ上の共有ファイル(`\\server\share\file.docx`)を直接比較する場合、他のユーザーがファイルを開いていると `Documents.Open` でダイアログが表示されて処理が止まるリスクがある。

対策:
`Documents.Open` の引数に `ReadOnly:=True` を明示指定することは必須だが、さらに安全を期すなら、比較処理を実行する直前に `FileSystemObject` を使い、対象ファイルを一時フォルダ(`Environ(“TEMP”)`)にローカルコピーしてから比較を実行する設計を推奨する。

② 大容量ドキュメントにおけるパフォーマンスの限界突破

100ページを超える巨大な仕様書同士を比較する場合、`Revisions` が数千件に及び、メモリ使用量とCPUクロックを消費する。

この場合、以下のチューニングを追加する。

‘ 比較処理の直前にビューの更新を完全に切る
app.ActiveWindow.View.Type = wdNormalView ‘ 下書きモードに移行してレイアウト計算をスキップ

印刷レイアウト表示(`wdPrintView`)のままだと、ハイライトを適用するたびにWord内部でページの再計算(リフロー)が発生する。「下書きモード(`wdNormalView`)」に強制変更してから走査を行うことで、全処理時間を最大50%削減できる。

5. まとめ

Wordにおける文書比較ツールの開発は、テキスト比較のアルゴリズムを自作することではない。
Wordの強力なオブジェクトモデル(Compare API & Revisions API)を正しく理解し、ライフサイクルと例外処理を制御することに他ならない。

1. 比較アルゴリズムは `Document.Compare` に全面的に委ねる
2. 生成されたドキュメントの `Revisions` コレクションから `Range` を抽出し、`HighlightColorIndex` で着色する
3. `CleanUp` ブロックを構築し、画面描画・リソース解放を厳格に管理する

この3つの設計原則を守ることで、バグがなく、高速で、保守性に優れたプロダクションレベルの業務自動化ツールが完成する。現場のシステム開発にぜひ役立ててほしい。

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