Word VBAを掌握する極限の知見:`Range.FormattedText`による書式付きテキスト高速転送術
開発現場でこんなコードを見たことはないだろうか。
‘ 【アンチパターン】絶対にやってはいけないクリップボード依存の処理
Selection.Copy
Selection.Paste
もし、あなたが数百ページに及ぶドキュメントの生成や、大量の定型句・条文を組み立てるエンタープライズ向けのWordマクロを開発しているなら、この「選択してコピー&ペースト」というアプローチは今すぐ捨てていただきたい。
遅い。不安定。そして何より、画面のチラつき(スクリーニング)が激しい。
Word VBAにおけるパフォーマンス低下の最大のガンは、「不要なUIオブジェクト(Selection)への依存」と「Windowsクリップボードの経由」にある。
今回は、Wordのオブジェクトモデルの深層を知る者だけが使う、`Range.FormattedText`プロパティを用いた極限の高速転送テクニックを伝授する。
—
なぜ `Selection.Copy / Paste` は悪なのか?
Word VBA初心者が最初に覚えるのが `Selection` オブジェクトを使った操作だ。しかし、`Selection` は「現在ユーザーが見ている画面上のカーソル位置」を模倣するものであり、UIの描画処理を伴うため圧倒的に重い。
さらに、`Copy` メソッドや `Paste` メソッドを実行すると、OSのクリップボード領域にデータが書き込まれる。
- 他のアプリケーションとの予期せぬ干渉(クリップボードエラー)
- クリップボードのクリア処理による処理落ちは、実務のバグ報告の定番だ。
救世主:`Range` オブジェクトと `FormattedText`
これに対し、`Range` オブジェクトは画面の描画(UI)とは切り離された、メモリ上の文書領域を指す。
`FormattedText` プロパティは、フォント、サイズ、段落書式、さらには表やインライン図形まで含めた「リッチテキストの塊」を、クリップボードを一切汚さずに、純粋なメモリ間(VBAの変数およびドキュメント間)でダイレクトに転送する。
速度差は歴然だ。数千回のループ処理を行った場合、`FormattedText` を使ったロジックは、クリップボード経由のものと比較して 最大で10倍以上のパフォーマンス を叩き出す。
—
プロダクションコード:堅牢な「条文・定型句アセンブラ」
実務でそのまま使える、堅牢なモジュール設計のコードを提示する。
このコードは、マスター文書(またはテンプレート)の特定の「セクションやブックマーク」から書式付きのテキストをごっそり取得し、別ドキュメントの指定位置へミリ秒単位の速度で流し込むものだ。
Option Explicit
”’
”’
Public Sub ExecuteHighSpeedTransfer()
Dim wsSource As Document
Dim wsTarget As Document
Dim rngSource As Range
Dim rngTarget As Range
‘ エラーハンドリングの標準装備
On Error GoTo ErrorHandler
‘ 画面描画と言い換えを停止し、実行速度を極限まで引き上げる
Application.ScreenUpdating = False
Application.DisplayAlerts = wdAlertsNone
‘ 【前提】転送元と転送先のドキュメントがすでに開いていると仮定
‘ 実務では必要に応じて Workbooks.Open 相当の処理を記述
Set wsSource = ActiveDocument ‘ サンプルとしてアクティブドキュメントを元にする
Set wsTarget = Documents.Add ‘ 新規ドキュメントを転送先とする
‘ 1. コピー元のレンジを設定(例:先頭の段落)
Set rngSource = wsSource.Paragraphs(1).Range
‘ 2. 転送先の挿入位置を決定(例:ターゲット文書の末尾)
Set rngTarget = wsTarget.Content
rngTarget.Collapse wdCollapseEnd
‘ =====================================================================
‘ 【核心】FormattedTextによるダイレクト転送
‘ =====================================================================
‘ クリップボードを経由せず、書式情報(Font, ParagraphFormat等)ごと一瞬でコピー
rngTarget.FormattedText = rngSource.FormattedText
‘ 処理成功のログ出力など
MsgBox “書式付きテキストの転送が完了しました。”, vbInformation, “高速転送エンジン”
CleanUp:
‘ 描画の復元(絶対に忘れてはならない)
Application.ScreenUpdating = True
Application.DisplayAlerts = wdAlertsAll
Exit Sub
ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “システムエラー”
Resume CleanUp
End Sub
—
現場で絶対にハマる「3つの罠」と設計上の注意点
この `FormattedText` は強力だが、オブジェクトモデルの挙動を理解していないと、実務で不可解なバグを生む。チーフアーキテクトとして、現場で陥りがちな罠と回避策を共有しておこう。
1. ターゲットの「レンジの伸び縮み(拡張)」に注意せよ
`rngTarget.FormattedText = rngSource.FormattedText` を実行すると、`rngTarget` 自体が「新しく挿入されたテキストの範囲」に自動的に拡張される。
もし連続して複数のブロックを転送したい場合は、代入の都度、レンジの末尾に縮小(Collapse)させる必要がある。
‘ 連続して転送する場合の正しいイディオム
rngTarget.FormattedText = rngSource1.FormattedText
rngTarget.Collapse wdCollapseEnd ‘ 常に末尾にカーソルを移動させる感覚
rngTarget.FormattedText = rngSource2.FormattedText
rngTarget.Collapse wdCollapseEnd
2. `ScreenUpdating = False` は例外処理とセットで実装せよ
高速化のために `Application.ScreenUpdating = False` を使うのは鉄則だが、コードの途中でエラー(ゼロ除算やオブジェクト不一致など)が発生して処理が中断すると、Wordの画面がフリーズしたような状態(描画停止のまま)でユーザーに放置されるという最悪のUXを招く。
必ず上記のサンプルコードのように、`On Error GoTo` を用いて、いかなる場合でも `ScreenUpdating = True` が実行されるクリーンアップ動線を確保すること。
3. データベースや外部ファイル連携時の文字コード・段落記号の扱い
データベース(SQL ServerやSQLiteなど)から取得したプレーンテキストをWordに流し込む際、改行コード(`vbCrLf`など)が含まれている場合がある。
単に `Range.Text` に代入すると、Wordの段落区切り(¶)が崩れたり、フォント書式がデフォルトにリセットされる。
外部データと連携する場合は、「ベースとなる定型スタイルが適用されたRange」に対して `FormattedText` ではなく通常のテキスト代入を行い、その後スタイルを適用するか、あらかじめテンプレート側に用意したプレースホルダー(ブックマーク)の `Range.FormattedText` ごと置き換える設計にすべきだ。
—
結論:プロのVBAコードは「美しく、速い」
業務自動化ツールを作るエンジニアの価値は、「ただ動くものを作る」ことではなく、「大規模データや長期間の運用でも耐えうる、堅牢で高速なアーキテクチャを構築する」ことにある。
`Selection` を廃し、`Range` と `FormattedText` を使いこなす。
たったこれだけの設計思想の転換が、あなたの書くWord VBAを「おもちゃのマクロ」から「エンタープライズグレードの自動化ソリューション」へと昇華させる。
次の開発案件から、ぜひこの知見を組み込んでほしい。圧倒的なパフォーマンスの差に驚くはずだ。
