TL;DR — Word にとって文書は 一続きの長い文字の並び であり、Word の
Rangeはその中の開始位置と終了位置にすぎません。セルを一つずつ訪ねるのではなく、テキストのひと区間を指定してそこに操作を加えます。Excel からの定番パターンは、レイアウトを{{Client}}のようなプレースホルダー付きの Word テンプレート に持たせ、そのコピーを開き、プレースホルダーをワークシートの値に置き換え、保存して閉じ、終了する、というものです。このパターンを壊すものが三つあります。Excel でDim r As Rangeと書くとそれは Excel の Range を意味するので、Set r = doc.Contentが型の不一致で失敗すること。修飾しないActiveDocumentやSelectionが Word への隠れたつながりを作り、二回目の実行で エラー 462 になること。そして検索と置換が 255 文字 を超える置換文字列を受け付けないことです。Word のオブジェクトをすべて自分の変数経由で修飾すれば、三つとも解消します。
Sub QuickLetter()
Dim wd As Object, doc As Object
Set wd = CreateObject("Word.Application") ' 新しい、非表示の Word
Set doc = wd.Documents.Add(ThisWorkbook.Path & "\Letter.dotx")
doc.Content.Find.Execute FindText:="{{Client}}", _
ReplaceWith:=Range("B2").Value, Replace:=2 ' 2 = wdReplaceAll
doc.SaveAs2 ThisWorkbook.Path & "\Letter_out.docx"
doc.Close
wd.Quit
End Sub
この記事は、Excel から他の Office アプリを操作する 三部構成シリーズの第二回です。PowerPoint のガイド では、スライド上の図形でできたプレゼンテーションを扱いました。ここで扱うのは Word、つまりテキストの流れです。参照設定のガイド では、VBA が相手のプログラムの語彙をどう覚えるかを説明しています。下で扱う Range の衝突は、まさにそこから生まれます。三つに共通するルールは、どのオブジェクトもいずれか一つのアプリケーションに属している。だから、どのアプリのものかを明示する です。
この記事で学べること
- 考え方の軸:文書は一続きのテキストで、Range は二つの位置の組
- Word を起動してテンプレートを開き、すべてをきれいに閉じる方法
- 255 文字を超えるテキストも含めて、プレースホルダーを置き換える方法
- Excel と Word の
Rangeの衝突と、その避け方 - 修飾しない
ActiveDocumentがエラー 462 と居残る WINWORD.EXE を生む理由 - Excel の表を貼り付け、DOCX で保存し、PDF に書き出す方法
考え方の軸:グリッドではなく、一続きのテキスト
Excel は値を行と列で指定します。Word には、表の中を除いて行も列もありません。Word の文書は段落記号まで含めた文字の並びであり、ほとんどすべてが Range、つまりその並びの中の開始位置と終了位置です。
| やりたいこと | Word での書き方 |
|---|---|
| 本文全体 | doc.Content |
| 一つの段落 | doc.Paragraphs(3).Range |
| テンプレート内の目印の位置 | doc.Bookmarks("Total").Range |
| 文書の末尾 | doc.Content を末尾に縮めたもの |
範囲に対しては、.Text を設定する、.Font で書式を付ける、.Find で検索する、といった操作をします。テキストを変えるとその後ろの位置がずれるので、文字の番号を覚えておくコードは壊れ、目印を検索するコードは壊れません。
ですから、仕事の分担は PowerPoint のときと同じで、データは Excel が持ち、レイアウトは Word が持つ です。送付状や契約書、レポートは、業務担当者が編集できるテンプレートとして Word でデザインし、マクロにはそこへ値を入れさせます。
Word を起動し、きれいに終わらせる
PowerPoint と違い、Word は CreateObject に対して 新しい、目に見えないコピー を起動します。これは CreateObject のガイド で説明したとおりです。そのコピーはあなたのものなので、終了しても安全です。危険はむしろ逆側にあります。wd.Quit の前にマクロがエラーで止まると、目に見えない Word が動き続け、次にテンプレートを開いたとき、Word はファイルがロックされていると文句を言います。後始末は、エラーが起きても到達できる場所に置きましょう。
Sub MakeLetter()
Const wdExportFormatPDF As Long = 17
Dim wd As Object, doc As Object
On Error GoTo CleanUp
Set wd = CreateObject("Word.Application")
Set doc = wd.Documents.Add(Template:=ThisWorkbook.Path & "\Letter.dotx")
ReplaceTag doc, "{{Client}}", Range("B2").Value
ReplaceTag doc, "{{Amount}}", Format(Range("B3").Value, "#,##0.00")
ReplaceTag doc, "{{Terms}}", Range("B4").Value ' 長い段落のこともある
doc.SaveAs2 ThisWorkbook.Path & "\Letter_" & Range("B2").Value & ".docx"
doc.ExportAsFixedFormat OutputFileName:=ThisWorkbook.Path & "\Letter.pdf", _
ExportFormat:=wdExportFormatPDF
CleanUp:
If Not doc Is Nothing Then doc.Close SaveChanges:=False
If Not wd Is Nothing Then wd.Quit
Set doc = Nothing
Set wd = Nothing
If Err.Number <> 0 Then MsgBox "Letter failed: " & Err.Description
End Sub
テンプレートのパスを渡した Documents.Add は、その .dotx を元にした無題の新規文書を作るので、テンプレート自体が変更されることはありません。CleanUp へのジャンプについては On Error のガイド で説明しています。開発中に Word の動きを見たいなら、作成直後に wd.Visible = True を加えてください。
プレースホルダーの置き換えと 255 文字の壁
Replace:=2(wdReplaceAll)付きの Find.Execute は、プレースホルダーを手早く置き換える方法で、名前、日付、金額ならこれで十分です。ところが契約条件の段落を渡すと、エラー 5854、String parameter too long(文字列パラメーターが長すぎます)で止まります。置換文字列は 255 文字までに制限されているのです。
堅牢な版では、プレースホルダーを一つずつ検索し、見つかった範囲にテキストを直接書き込みます。こちらには長さの制限がありません。
Sub ReplaceTag(doc As Object, ByVal tag As String, ByVal newText As String)
Const wdFindStop As Long = 0
Const wdCollapseEnd As Long = 0
Dim r As Object ' Word の範囲。Excel の Range ではない
Set r = doc.Content
With r.Find
.ClearFormatting
.Text = tag
.MatchCase = True
.Wrap = wdFindStop
Do While .Execute
r.Text = newText ' r は見つかったプレースホルダーを指している
r.Collapse wdCollapseEnd ' 書き込んだ内容の後ろから続ける
Loop
End With
End Sub
Execute が成功すると、範囲 r は見つかったテキストに移動するので、r.Text を設定すればちょうどそのプレースホルダーだけが置き換わります。末尾に縮めておけばループはそこから続くので、たまたまタグを含む値がもう一度置き換えられることはありません。波かっこのタグはテンプレートの作成者にとって見つけやすく、本文と取り違えることもありません。Word のコンテンツ コントロールやブックマークでも構いませんが、開発者でない人が保守できるのは素朴なタグのほうです。
Range の衝突:Excel の Range は Word の Range ではない
どちらのプログラムにも Range というオブジェクトがあり、Shape、Font、Border、Selection も共通しています。Excel の VBA の中では、修飾しない Range はつねに Excel のものを指します。優先順位の一覧で Excel のライブラリが Word のライブラリより上にあるからです。そのため、Word への参照設定をしていても、次のコードは失敗します。
Dim r As Range ' Excel.Range
Set r = doc.Content ' 実行時エラー 13: 型が一致しません
直し方は、どのアプリケーションのものかを明示することです。参照設定があれば Dim r As Word.Range、なければ上の ReplaceTag のように Dim r As Object とします。このエラーは Type mismatch(「型が一致しません。」)として表示されます。修飾しない名前が何を指すかを決める優先順位の一覧は、参照設定のガイド で示しています。習慣にすべきことは単純です。Excel のコードでは、Word のオブジェクトを入れる変数は Word.Something か Object のどちらかにし、裸の名前は決して使わないことです。
エラー 462:修飾しない ActiveDocument が作る隠れたつながり
二つ目の衝突はもっと静かです。Word ライブラリへの参照設定があると、Excel では Word のグローバルな名前を接頭辞なしで書けてしまいます。
Set wd = New Word.Application
wd.Documents.Add
ActiveDocument.Content.Text = "Hello" ' 修飾なし - 一回目は動く
wd.Quit
一回目の実行は動きます。二回目は実行時エラー 462、The remote server machine does not exist or is unavailable(リモート サーバー マシンが存在しないか、使用できません)で止まるか、タスク マネージャーにいつまでも消えない WINWORD.EXE が残ります。修飾しない ActiveDocument は、あなたの wd 変数を経由していませんでした。VBA がそのために独自の隠れたつながりを Word に張ったのです。wd.Quit は Word を閉じましたが、隠れたつながりは閉じたコピーを指したままで、VBA プロジェクトがリセットされるまで消えません。
ルールは、Word のオブジェクトにはすべて、自分で作った変数を経由してたどり着く ことです。wd.ActiveDocument と書くか、より良いのは文書を doc に持ち、ActiveDocument も Selection も一切使わないことです。ここでは遅延バインディングが助けになります。参照設定がなければ、修飾しない ActiveDocument は単なる未宣言の変数になり、Option Explicit がコンパイル時に止めてくれます。
Excel の表を文書に貼り付ける
Excel で書式を整えた表は、テンプレートの作成者が置いたブックマークの位置に、本物の Word の表として入れられます。
Sub PasteTable(doc As Object)
Dim target As Object
ThisWorkbook.Worksheets("Summary").Range("A1:D12").Copy
Set target = doc.Bookmarks("SalesTable").Range
target.PasteExcelTable LinkedToExcel:=False, WordFormatting:=False, RTF:=False
Application.CutCopyMode = False
End Sub
LinkedToExcel:=False は単純なコピーを作ります。送付する手紙やレポートにはこれが正解です。リンクされた表は、文書があなたの PC を離れた途端に切れてしまいます。WordFormatting:=False は Excel の見た目を保ちます。文書の表スタイルに合わせたいなら True にします。シートの見た目そのままで、編集されることのない範囲にしたいなら、PowerPoint のガイド のように CopyPicture で図として貼り付けるのが代替策です。
Word 自身の中では:同じオブジェクトモデル
Word の中で動くマクロを書きたくて Word VBA を検索したのなら、橋渡しの部分を除けば、ここまでの内容はすべてそのまま当てはまります。Word 自身の VBA エディターでは、ActiveDocument、Range、Selection は Word のオブジェクトを指し、修飾に使う wd 変数はありません。一続きのテキストという考え方、Find のループ、255 文字の制限は同じです。変わるのは向きです。Word から見ると Excel が外部のプログラムになり、Excel の Range に同じルールが当てはまります。
判断の分かれ目:レイアウトは Word、ループは Excel
手紙が一通なら、どちらのプログラムでも用は足ります。二百通となると、設計の判断が効いてきます。私のルールは、ループとデータは Excel に、レイアウトは Word のテンプレートに置き、テンプレートに任せられる書式をマクロで設定しない です。Word は一度だけ起動し、同じ .dotx から一行につき一つの文書を作り、保存したらその都度閉じ、最後に終了します。これはファイル名、PDF 出力、エラー処理を自分で決められる、自分で制御できる差し込み印刷です。Word の組み込みの差し込み印刷は、差し込み済みの長い文書を一つ印刷するだけなら十分です。一行ごとに一つのファイルが必要になった途端、マクロに軍配が上がります。
ExcelMaster の活用
Word 自動化のバグは、二つのプログラムの間の隙間に潜みます。正しく見える行で起きる型の不一致、いつまでも終了しない WINWORD.EXE、置き換えるには長すぎる契約条項。
ExcelMaster には、欲しい結果をそのまま伝えられます。たとえば「テンプレートから顧客ごとに一通ずつ手紙を作って、顧客名を付けて PDF で保存して」。すると、Word のオブジェクトをすべて修飾し、長さ制限のないプレースホルダーのループを使い、ある行が失敗しても必ず走る後始末を備えたマクロを書きます。
よくある質問
Excel VBA から Word 文書を開くには?
CreateObject("Word.Application") で Word を作り、ファイルパスを渡して wd.Documents.Open を呼ぶか、テンプレートのパスを渡して wd.Documents.Add を呼び、それを元にした新規文書を得ます。結果は変数に入れ、その変数だけを経由して作業します。
Excel から Word を自動化するとエラー 462 が出るのはなぜですか?
コードが Word のオブジェクトを修飾せずに使ったからです。多くは ActiveDocument か Selection で、そのために VBA が Word への隠れたつながりを作りました。Quit の後、そのつながりは閉じたプログラムを指しています。Word のオブジェクトにはすべて、自分の wd 変数か doc 変数を経由してたどり着くようにします。
Dim r As Range が Word で型の不一致になるのはなぜですか?
Excel の VBA では、Range は Excel の Range を意味します。Word の範囲は別の型なので、そこに doc.Content を代入すると失敗します。Word への参照設定があれば Word.Range として、なければ Object として宣言します。
VBA で Word の 255 文字を超えるテキストを置換するには?
Range.Find.Execute でプレースホルダーを見つけ、見つかった範囲の .Text に長い値を設定します。255 文字の制限がかかるのは検索と置換の ReplaceWith 引数だけで、Range.Text にはかかりません。
Excel VBA から Word 文書を PDF で保存するには?
出力ファイル名と、wdExportFormatPDF にあたる ExportFormat:=17 を指定して doc.ExportAsFixedFormat を呼びます。文書は開いたままなので、その後で閉じて Word を終了します。
検証環境
検証環境: Excel 365 と Word 365 (Windows 11)、VBA 7.1 — 最終確認 2026-10-02。
関連ガイド: VBA PowerPoint · VBA 参照設定 · VBA CreateObject · VBA Outlook · VBA On Error · VBA Option Explicit · VBA Replace · VBA PDF 保存
