目次
- はじめに:VBAでの印刷自動化に伴うエラーの複雑な背景
- PrintOutメソッドに関する実行時エラー1004の原因と対処法
- OS・プリンター環境に起因する根本的なエラー
- 【最大の鬼門】ActivePrinterプロパティでの指定エラーとポート番号の罠
- ActivePrinterのエラーを回避し、安全にプリンターを切り替える高度な解決策
- 代替手段としてのPDF出力(ExportAsFixedFormat)におけるエラー要因
- 実務で使える安全な印刷処理のベストプラクティス(テンプレートコード)
- まとめ:印刷マクロは環境への配慮が不可欠
はじめに:VBAでの印刷自動化に伴うエラーの複雑な背景
Excel VBAを活用して、請求書、納品書、月次レポートなどの定型帳票を連続してプリンターから出力する「印刷マクロ」は、手作業を劇的に削減できるため非常に需要の高い処理です。しかし、VBAで印刷に関するコード(PrintOut メソッドや ActivePrinter プロパティ)を記述した際、「実行時エラー ‘1004’」等のエラーメッセージによって処理が強制終了してしまうトラブルが後を絶ちません。
セルへの値の代入や別シートへのコピーといったExcel内部で完結する処理とは異なり、「印刷」という行為は、Excelアプリケーションという枠を飛び越え、Windows OSの印刷機構(スプーラー)や、外部のハードウェア機器(プリンター)、ネットワーク環境(プリントサーバー)と密接に連携する必要があります。そのため、VBAのコード自体は文法的に完璧であっても、パソコンにプリンターが接続されていなかったり、他の社員のパソコンで実行した際のネットワークポートの割り当てが異なったりするだけで、途端にエラーが発生してしまいます。
手作業で「ファイル」タブから印刷ボタンを押す場合、Excelのユーザーインターフェース(UI)がこれらの環境の違いを裏側で吸収し、エラーがある場合は人間が目視でプリンターを選び直すことができます。しかしVBAは指定された命令を厳格に実行しようとするため、わずかな環境の不一致が致命的なエラーに直結するのです。
本記事では、VBAで印刷処理を実行する際に発生するエラーの原因を「PrintOutメソッドの指定ミス」「Windowsやプリンター環境の不備」「ActivePrinterプロパティのポート番号問題」という3つの観点から詳細に分解し、どのような環境で実行されてもエラーで止まらない、堅牢で安全な印刷マクロを構築するための具体的な対処法とコード実装パターンを網羅的に解説します。
PrintOutメソッドに関する実行時エラー1004の原因と対処法
最も基本的な印刷命令である Worksheets("Sheet1").PrintOut を実行した際にエラーとなる場合、まずは対象となるシート自体の状態を疑う必要があります。
原因1:非表示状態のシートを印刷しようとしている
VBAの印刷エラーにおいて非常に多いのがこの原因です。ユーザーに直接触れさせないために、ひな形(テンプレート)シートを「非表示(Visible = xlSheetHidden または xlSheetVeryHidden)」に設定している状態で、そのシートに対して PrintOut メソッドを実行すると、「実行時エラー ‘1004’: PrintOut メソッドは失敗しました」が発生します。Excelの仕様として、非表示のオブジェクトを直接印刷することはできません。
【対処法】
印刷を行う直前にシートを一時的に再表示(xlSheetVisible)し、印刷が完了した直後に再び非表示に戻す処理を組み込みます。この際、画面のちらつきを防ぐために Application.ScreenUpdating = False を併用するのがベストプラクティスです。
Sub PrintHiddenSheet()
Application.ScreenUpdating = False ' 画面の更新を停止
With ThisWorkbook.Worksheets("ひな形")
.Visible = xlSheetVisible ' 一時的に表示
.PrintOut ' 印刷実行
.Visible = xlSheetHidden ' 再び非表示に戻す
End With
Application.ScreenUpdating = True ' 画面の更新を再開
End Sub
原因2:シートに印刷するデータが存在しない(空白のシート)
対象のシートに文字やデータが1つも入力されていない、あるいは印刷範囲(PrintArea)の指定内にデータが存在しない場合、手動で印刷しようとすると「印刷するデータがありません」という警告メッセージが出ますが、VBAで PrintOut を実行した場合はエラー1004として処理が停止します。
【対処法】
シートが空白である可能性がある場合は、事前にデータが存在するか(または特定の必須セルに値が入っているか)を判定するIF文を追加し、空の場合は印刷処理をスキップするように制御します。
原因3:PrintOutの引数(ページ指定など)の論理的な矛盾
PrintOut メソッドには、開始ページ(From)、終了ページ(To)、部数(Copies)などの引数を指定できます。ここで、例えば総ページ数が3ページしかないシートに対して From:=1, To:=5 と指定したり、From:=3, To:=1 のように開始ページが終了ページを上回るような論理的に破綻した数値を渡したりすると、実行時エラーが発生します。
【対処法】
引数に変数を渡す場合は、その変数が正しい範囲の数値に収まっているかを事前にバリデーション(検証)する処理を記述してください。
OS・プリンター環境に起因する根本的なエラー
コードやシートの状態が正しくても、パソコン自体の設定が原因でエラーになるケースです。
原因4:「通常使うプリンター」が設定されていない・ドライバーの不具合
新しいパソコンをセットアップした直後や、仮想環境(VDI)などでよく発生します。Windowsの「設定」>「デバイス」>「プリンターとスキャナー」において、有効なプリンターが一つも登録されていない(あるいは「Microsoft Print to PDF」すら存在しない)場合、Excelは印刷の出力先を見つけることができず、PrintOut メソッドは失敗します。また、プリンタードライバーが破損している場合も同様のエラーが起きます。
原因5:WindowsのPrint Spoolerサービスが停止している
Windows OSにおいて、印刷データを管理してプリンターに転送する中核機能が「Print Spooler」サービスです。何らかのシステムエラーやセキュリティソフトの干渉により、このサービスが停止状態に陥っていると、Excelからの印刷要求はOSレベルで拒否され、VBAはエラーを返します。この場合は、Windowsの「サービス」管理画面から Print Spooler を再起動する必要があります。
【最大の鬼門】ActivePrinterプロパティでの指定エラーとポート番号の罠
VBAの印刷処理において、最も多くの開発者を絶望させるのが、出力先のプリンターをマクロ内で切り替えるために Application.ActivePrinter プロパティを使用した際に発生するエラーです。テスト環境(自分のPC)では動いたのに、他の人のPCにマクロファイルを渡した瞬間に「実行時エラー ‘1004’: ActivePrinter メソッドは失敗しました」が発生する事象です。
ActivePrinterの仕様と「Ne01:」などのポート番号について
VBAでプリンターを指定する場合、単純にプリンターの名称(例:「Canon LBP6030」)を代入すればよいと直感的に考えがちですが、Excelの仕様ではそれは許されません。ActivePrinter プロパティに代入する文字列は、「プリンター名 on ポート番号」という特殊なフォーマットである必要があります。
例:Application.ActivePrinter = "Canon LBP6030 on Ne01:"
ここで問題となるのが末尾の Ne01: や Ne02: といった「ポート番号(ネットワークポート)」です。USB接続などのローカルプリンターの場合は LPT1: や USB001 などで固定されることが多いですが、社内ネットワーク上にある共有プリンター(IPアドレス指定など)の場合、Windowsはこの「Neポート番号」をそのパソコンにプリンターがインストールされた順番や、ネットワークの認識順によって動的に割り当てます。
別のPCで実行するとエラーになる(環境依存の問題)
つまり、Aさんのパソコンでは「Canon LBP6030 on Ne01:」として認識されているプリンターが、Bさんのパソコンでは「Canon LBP6030 on Ne04:」として認識されている可能性が極めて高いということです。
そのため、マクロの中に ActivePrinter = "Canon LBP6030 on Ne01:" とハードコーディング(固定値で記述)してしまうと、Ne01にそのプリンターが存在しないBさんのパソコンで実行した瞬間に、文字列の不一致とみなされ実行時エラー1004が発生してマクロが停止します。
ActivePrinterのエラーを回避し、安全にプリンターを切り替える高度な解決策
この「Neポート番号がPCによって異なる問題」を解決するためには、固定の文字列を指定するのではなく、システムから動的に正しいポート番号を取得するか、ExcelのActivePrinterプロパティを使わずにOSレベルでプリンターを切り替えるという高度なアプローチが必要になります。
解決策1:WMIを使用してOSの「通常使うプリンター」ごと変更する(推奨)
最も確実で、コードがシンプルになる解決策です。VBAからWindowsのWMI(Windows Management Instrumentation)という機能を呼び出し、ExcelのActivePrinterを操作するのではなく、Windows OSの「通常使うプリンター」の設定自体を目的のプリンターに変更してしまう方法です。
この方法であれば、厄介な「on Ne01:」といったポート番号を一切気にする必要がなく、純粋なプリンター名(文字列)だけで切り替えが可能です。
' -----------------------------------------------------------
' 目的:ポート番号を気にせず指定したプリンターで印刷する
' -----------------------------------------------------------
Sub PrintWithSpecificPrinter_WMI()
Dim targetPrinterName As String
Dim objWMIService As Object
Dim colPrinters As Object
Dim objPrinter As Object
Dim printerFound As Boolean
' 切り替えたいプリンター名(コントロールパネル等で表示される正確な名前)
targetPrinterName = "Canon LBP6030"
printerFound = False
' WMIオブジェクトの取得
Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")
' 指定した名前のプリンターをシステムから検索
Set colPrinters = objWMIService.ExecQuery("Select * From Win32_Printer Where Name = '" & targetPrinterName & "'")
' プリンターが見つかった場合、それをWindowsの「通常使うプリンター」に設定
For Each objPrinter In colPrinters
objPrinter.SetDefaultPrinter
printerFound = True
Next
If printerFound = False Then
MsgBox "指定されたプリンター「" & targetPrinterName & "」が見つかりません。", vbCritical
Exit Sub
End If
' --- ここで印刷処理を実行 ---
' OSの通常使うプリンターが変更されたため、何も指定せずにPrintOutすればそのプリンターから出力される
ThisWorkbook.Worksheets("出力シート").PrintOut
' ※必要であれば、処理が終わった後に元のプリンターに戻す処理を追加する
Set colPrinters = Nothing
Set objWMIService = Nothing
End Sub
解決策2:レジストリからポート番号を自動取得して文字列を組み立てる
OSの通常使うプリンターは変更せず、あくまでExcelアプリケーション内だけの出力先(ActivePrinter)を切り替えたい場合は、Windowsのレジストリを読み込んで、対象プリンターに割り当てられている現在のNeポート番号を取得し、「プリンター名 on ポート番号」の文字列を動的に組み立てる関数を作成します。
' -----------------------------------------------------------
' 目的:レジストリからポート番号を取得してActivePrinterにセットする
' -----------------------------------------------------------
Sub ChangeActivePrinter_Registry()
Dim targetPrinter As String
Dim wshShell As Object
Dim regPath As String
Dim portName As String
Dim fullPrinterName As String
targetPrinter = "Canon LBP6030"
' プリンターのポート情報が格納されているレジストリのパス
regPath = "HKCU\Software\Microsoft\Windows NT\CurrentVersion\PrinterPorts\"
Set wshShell = CreateObject("WScript.Shell")
' エラーハンドリング(レジストリに該当プリンターが存在しない場合の回避)
On Error Resume Next
portName = wshShell.RegRead(regPath & targetPrinter)
On Error GoTo 0
If portName = "" Then
MsgBox "プリンターがインストールされていません。", vbCritical
Exit Sub
End If
' レジストリの値は "winspool,Ne01:,15,45" のような形式で返るため、Split関数で2番目の要素(Ne01:)を抽出
portName = Split(portName, ",")(1)
' "プリンター名 on ポート番号" の文字列を組み立てる
fullPrinterName = targetPrinter & " on " & portName
' ActivePrinterにセット
Application.ActivePrinter = fullPrinterName
' 印刷実行
ThisWorkbook.Worksheets("出力シート").PrintOut
Set wshShell = Nothing
End Sub
このアプローチであれば、誰のパソコンで実行しても、そのパソコンにおける正しいポート番号が自動で取得されるため、エラー1004は発生しなくなります。
代替手段としてのPDF出力(ExportAsFixedFormat)におけるエラー要因
近年では紙への印刷(PrintOut)ではなく、PDFファイルとして出力保存する ExportAsFixedFormat メソッドを利用する機会が増えています。このメソッドはプリンターの接続環境に依存しないためActivePrinterのエラーは起きませんが、特有の実行時エラーが存在します。
出力先のPDFファイルが別のアプリケーションで開かれている
PDF出力処理において最も頻発するエラーがこれです。マクロによって「請求書.pdf」というファイルを指定のフォルダに出力しようとした際、すでに前回の出力結果である「請求書.pdf」が Adobe Acrobat Reader などのPDF閲覧ソフトで開かれたままになっていると、OSによってファイルがロック(書き込み禁止状態)されているため、Excelはファイルを上書きできず、実行時エラー ‘1004’ となってマクロが停止します。
【対処法】
出力先のファイルパスを生成した後、PDF出力の直前で Dir 関数などを用いてファイルの存在を確認し、存在する場合はファイルにアクセス(上書き)可能かどうかをチェックするエラーハンドリングを実装するか、出力するファイル名に日付や時刻のスタンプ(例:請求書_20231015_143000.pdf)を付与して、常に新規ファイルとして出力させることでファイルロックによるエラーを完全に回避できます。
実務で使える安全な印刷処理のベストプラクティス(テンプレートコード)
これまでに解説した様々なエラー要因(非表示シート、空白データ、環境の違い)をすべて考慮し、途中で止まることのない堅牢な印刷マクロの基本構造(ベストプラクティス)は以下のようになります。
Sub SafePrintProcess()
Dim wsPrint As Worksheet
Dim defaultPrinter As String
Set wsPrint = ThisWorkbook.Worksheets("出力用")
' 1. 現在のActivePrinterを記憶しておく(後で戻すため)
defaultPrinter = Application.ActivePrinter
' 2. 印刷データが存在するかどうかの確認(例としてA1セルが空でないか)
If Trim(wsPrint.Range("A1").Value) = "" Then
MsgBox "印刷するデータが存在しません。処理を中止します。", vbExclamation
Exit Sub
End If
Application.ScreenUpdating = False
' 3. エラー発生時に処理を中断せず、エラー処理ブロックへ飛ぶ設定
On Error GoTo PrintErrorHandler
' 4. 非表示シート対策:一時的に表示状態にする
Dim originalVisibility As XlSheetVisibility
originalVisibility = wsPrint.Visible
wsPrint.Visible = xlSheetVisible
' 5. プリンターの指定が必要な場合は、前述のWMIかレジストリ関数で切り替える
' (ここでは省略し、既存のActivePrinterで印刷)
' 6. 印刷実行
wsPrint.PrintOut Copies:=1
' 7. 元の状態に戻す
wsPrint.Visible = originalVisibility
Application.ActivePrinter = defaultPrinter
Application.ScreenUpdating = True
MsgBox "印刷命令を送信しました。", vbInformation
Exit Sub
' --- エラー発生時の処理ブロック ---
PrintErrorHandler:
' 画面更新等の設定を確実に元に戻す
Application.ScreenUpdating = True
If Not wsPrint Is Nothing Then wsPrint.Visible = originalVisibility
' エラー内容の表示
MsgBox "印刷処理中にエラーが発生しました。" & vbCrLf & _
"エラー番号: " & Err.Number & vbCrLf & _
"エラー内容: " & Err.Description, vbCritical
End Sub
このように On Error GoTo を用いてエラーを補足し、エラーが起きてもExcelの設定(ScreenUpdatingやシートの表示状態)を確実に元の状態に戻すフェイルセーフの設計が、実務用マクロには不可欠です。
まとめ:印刷マクロは環境への配慮が不可欠
VBAで印刷操作を行う際に発生するエラーは、コード自体の単純な記述ミスよりも、「Excelの仕様上の制約」と「パソコンを取り巻く外部環境」の不一致によって引き起こされるケースが圧倒的多数を占めます。
特に ActivePrinter プロパティにおける「ポート番号(Ne01: など)」の仕様は、複数のユーザーでマクロを共有する環境において必ずと言っていいほど直面する罠です。この問題を解決するためには、ハードコーディングを避け、WMIによるOSレベルでの設定変更や、レジストリからの動的取得といった高度なテクニックを取り入れる必要があります。
印刷マクロを構築する際は、「指定したプリンターが繋がっていないかもしれない」「シートが非表示になっているかもしれない」「データが空かもしれない」といったあらゆるイレギュラーな状況を事前に想定し、本記事で紹介したエラー回避のロジックやエラーハンドリングを組み込むことで、現場の誰もが安心してボタン一つで利用できる信頼性の高い自動化ツールを完成させてください。