制御不能な「Wordの闇」を制する:Rangeオブジェクトによる段落最適化の極致
Word VBAを「単なるマクロ記録の延長」だと思っているなら、今すぐその認識を捨てろ。Wordのオブジェクトモデルは、Excelとは比較にならないほど複雑で、かつメモリの断片化(フラグメンテーション)を招きやすい。
今回は、特定の記号(★や◆)を検出し、段落全体を太字にするという一見単純なタスクを題材に、「なぜ多くのエンジニアのコードは遅いのか」「なぜドキュメントが破壊されるのか」という深淵に触れる。
—
1. 「Selection」という名の呪い
初心者が書くコードの9割は `Selection` オブジェクトを多用する。画面描画を伴う `Selection` は、処理速度を劇的に低下させるだけでなく、予期せぬスクロールやフォーカス移動を引き起こし、UIスレッドを占有する。
真の自動化エンジニアは `Range` オブジェクトのみを操る。`Range` はメモリ上に存在する論理的な範囲であり、描画を必要としない。
—
2. 極限のメモリ管理とパフォーマンス設計
WordのRange操作は、文書が肥大化するほど「再描画のオーバーヘッド」が累積する。これを回避するには、以下の原則を厳守せよ。
- ScreenUpdatingの完全制御: 処理中の描画を止め、メモリの書き換えを最小化する。
- オブジェクトの明示的解放: VBAはGC(ガベージコレクション)が優秀ではない。参照を明示的に `Nothing` にする習慣が、大規模文書でのクラッシュを防ぐ。
- Findオブジェクトの再利用: `Find` プロパティは強力だが、設定をリセットしないと次回の検索にゴミが残る。
—
3. 実装:プロフェッショナル・コード
以下は、パフォーマンスと堅牢性を重視した「記号検出&太字化」の最適解だ。
Option Explicit
”’
”’
Public Sub HighlightTargetParagraphs()
Dim doc As Document
Dim rng As Range
Dim targetSymbol As String
‘ メモリ最適化:描画停止
Application.ScreenUpdating = False
Set doc = ActiveDocument
targetSymbol = “★” ‘ ここに判定したい記号を指定
‘ 文書全体をRangeで取得
Set rng = doc.Content
With rng.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = targetSymbol
.Forward = True
.Wrap = wdFindStop
.Format = False
‘ 検索ループ
Do While .Execute
‘ 発見した箇所を段落単位に拡大(Expandメソッド)
‘ これにより、ピンポイントの記号ではなく「段落全体」を制御対象にする
rng.Expand Unit:=wdParagraph
‘ 書式設定(太字化)
rng.Bold = True
‘ 次の検索のためにRangeを終了位置へ移動
rng.Collapse Direction:=wdCollapseEnd
Loop
End With
‘ 後処理:オブジェクトの明示的解放
Set rng = Nothing
Set doc = Nothing
Application.ScreenUpdating = True
MsgBox “最適化処理が完了しました。”, vbInformation
End Sub
—
4. シニアエンジニアへ贈る「さらに先の知見」
上記のコードで満足してはならない。現場で直面するであろう「真の課題」に対する備えも記しておく。
- 大規模ドキュメントへの対応: 数万ページに及ぶ文書の場合、`rng.Expand` をループ内で多用すると、WordのUndoスタックが溢れ、メモリ不足に陥る可能性がある。その場合は、`ActiveDocument.UndoClear` を適宜呼び出し、スタックをフラッシュせよ。
- Windows APIとの連携: もし外部システムから送られてくるテキストデータに制御コードが含まれている場合、VBA単体では処理できない文字化けが発生する。その際は、`Declare PtrSafe Function` を用い、Windows APIの `MultiByteToWideChar` を直接叩いて、バイナリレベルでテキストを正規化するのがプロの作法だ。
- レガシー保守: 20年前の `.doc` 形式(Word 97-2003)と最新の `.docx` では、XML構造へのアクセス方法が異なる。`CompatibilityMode` を確認し、バージョンごとの挙動差を抽象化するレイヤーをコード内に設けるのが、保守性を高める唯一の道である。
最後に
コードは「動けば良い」という段階を卒業せよ。
メモリのライフサイクルを意識し、描画を制御し、Wordという巨大なエンジンを「Range」という針の先で操る。その感覚こそが、業務自動化エンジニアとしての真の境地である。
君たちの書くコードが、誰かの業務時間を数千時間単位で削減することを期待している。健闘を祈る。
