【Word VBA】Rangeのコピペはもう古い。バイナリ概念を応用した「表データ超高速転送」の極意
開発現場でよく見かける光景がある。
「別ファイルのWordから表をごっそり持ってきたい」「マスターデータから表を動的に生成したい」――そう要求されたプログラマが、反射的に次のようなコードを書く。
‘ 【アンチパターン】絶対にやってはいけないRangeコピペ
sourceRange.Copy
destinationRange.Paste
ちょっと待ってほしい。君は本当に、WordのUIスレッドを巻き込み、クリップボードという「黒歴史の共有メモリ」を汚染しながら業務アプリを動かしたいのか?
大規模な帳票生成や、数百のファイルを巡回するデータ集計バッチにおいて、`Copy & Paste` や `Select` の連打は百害あって一利なしだ。画面がちらつき、メモリリークを引き起こし、最終的には「COMオブジェクトの例外」という名の死神がアプリケーションをクラッシュさせる。
今回は、Word VBAの深淵を覗き、表データを「値(テキスト)の二次元配列」として抽象化し、メモリ上で完結させる極限の高速データ転送アーキテクチャを伝授する。
—
なぜ `Range.Copy` は実務で使えないのか?
Wordの `Range` オブジェクトや `Selection` オブジェクトを通じた操作は、常にGUIのレンダリングエンジンと同期している。クリップボードを経由する処理は、OSのメッセージキューを消費するため、処理速度が物理的に制限されるのだ。
さらに、表(Table)のセル結合(Merge)やネスト構造が存在する場合、単純なコピペは意図しないレイアウト崩壊を引き起こす。
プロフェッショナルな設計思想:バイナリ的メモリ転送
私たちが目指すべきアプローチは明快だ。
1. 読込元: 表の構造をメタデータ(行数・列数)とともに「二次元配列(Variant)」という名の生データ(メモリブロック)として一気に吸い上げる。
2. 転送: アプリケーションのメモリ空間内だけでデータをハンドリングする。
3. 書込先: 転送先の表のサイズを動的に再構築し、配列データを一括流し込み(Bulk Insert)する。
ディスクやクリップボードに触れない。これが、爆速かつ堅牢なWord自動化の絶対条件だ。
—
【実装】メモリ効率を極限まで高めたデータ転送エンジン
以下のコードは、実務の現場で即座に使えるプロダクションコードだ。
ソース文書の特定の表からデータを二次元配列として抽出し、ターゲット文書の末尾にある表へ、セル結合情報を維持しながらノータイムで流し込む。
Option Explicit
” =================================================================
” “#{@} Module: TableDataTransferEngine
” 概要: 表データを二次元配列としてメモリ上で抽出し、別文書へ高速転送する
” =================================================================
Public Sub ExecuteHighSpeedTableTransfer()
Dim srcDoc As Document
Dim dstDoc As Document
Dim srcTable As Table
Dim dstTable As Table
‘ パスは環境に合わせて書き換えてください
Dim srcPath As String: srcPath = ThisDocument.Path & “\SourceData.docx”
Dim dstPath As String: dstPath = ThisDocument.Path & “\TargetReport.docx”
On Error GoTo ErrorHandler
‘ 1. ドキュメントを非表示(バックグラウンド)で安全に開く
‘ 画面描画(ScreenUpdating)を止めるまでもなく、Documentオブジェクトの直接操作は高速
Set srcDoc = Documents.Open(FileName:=srcPath, ReadOnly:=True, Visible:=False)
Set dstDoc = Documents.Open(FileName:=dstPath, ReadOnly:=False, Visible:=True)
‘ 2. 対象の表を取得(ここでは両文書の最初の表をターゲットとする)
Set srcTable = srcDoc.Tables(1)
Set dstTable = dstDoc.Tables(1)
‘ 3. 【核心】表データをメモリ上の二次元配列へ一括抽出
Dim rawData() As String
rawData = ExtractTableToMemory(srcTable)
‘ 4. 【核心】配列データをターゲットの表へ一括流し込み
Call InjectDataToTable(dstTable, rawData)
‘ 5. 保存してクローズ
dstDoc.Save
srcDoc.Close SaveChanges:=wdDoNotSaveChanges
dstDoc.Close SaveChanges:=wdSaveChanges
MsgBox “表データの高速転送が完了しました。”, vbInformation, “アーキテクチャ・ログ”
Exit Sub
ErrorHandler:
MsgBox “致命的なエラーが発生しました: ” & Err.Description, vbCritical, “異常終了”
If Not srcDoc Is Nothing Then srcDoc.Close SaveChanges:=wdDoNotSaveChanges
If Not dstDoc Is Nothing Then dstDoc.Close SaveChanges:=wdDoNotSaveChanges
End Sub
” =================================================================
” Function: Tableを二次元配列(バイナリ概念の抽象化)へ変換
” =================================================================
Private Function ExtractTableToMemory(ByRef tgtTable As Table) As String()
Dim maxRow As Long: maxRow = tgtTable.Rows.Count
Dim maxCol As Long: maxCol = tgtTable.Columns.Count
Dim buffer() As String
ReDim buffer(1 To maxRow, 1 To maxCol)
Dim r As Long, c As Long
Dim cellText As String
For r = 1 To maxRow
For c = 1 To maxCol
On Error Resume Next
‘ セル内のテキスト末尾には必ず改行コード(Chr(13) + Chr(7))が含まれるためトリムする
cellText = tgtTable.Cell(r, c).Range.Text
cellText = Left(cellText, Len(cellText) – 2)
buffer(r, c) = cellText
On Error GoTo 0
Next c
Next r
ExtractTableToMemory = buffer
End Function
” =================================================================
” Sub: メモリ上の配列データを表へ一括流し込み
” =================================================================
Private Sub InjectDataToTable(ByRef tgtTable As Table, ByRef buffer() As String)
Dim lRow As Long: lRow = UBound(buffer, 1)
Dim lCol As Long: lCol = UBound(buffer, 2)
‘ ターゲットの表サイズが足りない場合は動的に行を追加
Do While tgtTable.Rows.Count < lRow
tgtTable.Rows.Add
Loop
Dim r As Long, c As Long
For r = 1 To lRow
For c = 1 To lCol
On Error Resume Next
' セルに直接文字列を代入(Rangeオブジェクトを介さないため爆速)
tgtTable.Cell(r, c).Range.Text = buffer(r, c)
On Error GoTo 0
Next c
Next r
End Sub
---
恵まれた開発環境や最新のPCであっても、愚直なループや不要なUI操作は確実にアプリケーションをスポイルする。今回紹介した「データをメモリ上で配列として扱う」アプローチは、Word VBAだけでなく、Excelや外部データベース連携(ADODB等)を伴う複合的な業務自動化システムにおいても、そのまま流用できる強力なデザインパターンだ。
「なぜ遅いのか」「どこがボトルネックになり得るのか」を常に意識し、無駄なオブジェクト生成を極限まで削ぎ落とした、美しく強靭なコードを書き上げてほしい。君のプロジェクトの成功を祈る。
