PowerShellExcel VBA

PowerShellからVBAを実行すると
Excelが切断される原因

複数のExcelマクロを順番に実行していたところ、終了処理でExcelとの接続が切れました。Workbook.CloseとApplication.Quitを誰が実行するのか、役割を整理します。

PowerShellからExcelを操作し、複数のマクロブックと月別ファイルを順番に処理していたとき、次のエラーが発生しました。

ERROR0x80010108
The object invoked has disconnected from its clients.

HRESULT: 0x80010108
RPC_E_DISCONNECTED

日本語では「呼び出されたオブジェクトがクライアントから切断されました」と表示されます。変数は残っていても、その変数が参照していたExcelはすでに終了しており、PowerShellから操作できない状態です。

連続実行そのものが原因とは限らない

今回の中心は、VBAとPowerShellの両方にブックとExcelの終了処理があったことです。処理回数よりも、Excelを閉じる担当が重複していた点を確認します。

AUTOMATION FLOW

複数の月別ファイルを順番に処理していた

当時のPowerShellは、計画系のファイルでマクロを実行した後、別フォルダにある同名・別内容の月別ファイルを順番に開く構成でした。それぞれについて3つのマクロを実行し、変更を保存してから次のファイルへ進みます。

  1. 01
    Excelを起動PowerShellがExcel.Applicationを作成する。
  2. 02
    対象ブックを開くフォルダごとに対応する月別ファイルを開く。
  3. 03
    マクロを連続実行Application.Runで3つの処理を順番に呼び出す。
  4. 04
    保存して閉じる各ブックの処理後、次の対象へ移動する。

ところが、VBA側にもWorkbook.CloseApplication.Quitがあり、PowerShell側にもClose()Quit()がありました。

DISCONNECTED OBJECT

Application.Quitの後もPowerShellは処理を続ける

Workbook.Closeは指定したブックを閉じます。一方、Application.QuitはExcelアプリケーションを終了します。

PowerShellが作成したExcelに対して、実行中のVBAがApplication.Quitを呼ぶと、PowerShellが保持している$excel$workbookは有効な接続先を失います。その後で保存や終了を実行すると、切断エラーになります。

VBA / 問題のある終了処理実行注意
Sub ExecuteMain()

    On Error GoTo ErrorHandler

    '集計や転記などの処理

    ThisWorkbook.Close SaveChanges:=True
    Application.Quit
    Exit Sub

ErrorHandler:
    Application.Quit

End Sub

正常終了でもエラー終了でも、VBAがExcel全体を閉じています。この後にPowerShellが$workbook.Save()$excel.Quit()を呼ぶと、すでに終了したCOMオブジェクトへ再びアクセスすることになります。

Excelを閉じる処理は、VBAとPowerShellのどちらか一方だけが担当する。

ONE OWNER

PowerShellが全体を管理する場合の役割分担

複数ブックをPowerShellから順番に処理するなら、Excelの開始から終了までをPowerShellへ統一すると流れが分かりやすくなります。

VBAブック内の処理を担当

集計・転記・書式変更を行い、完了したらPowerShellへ制御を戻します。

PowerShellExcelの寿命を管理

ブックを開く、マクロを呼ぶ、保存する、閉じる、最後にExcelを終了します。

  • VBAからApplication.Quitを呼ばない
  • VBAとPowerShellで同じブックを二重にCloseしない
  • エラー時も、Excelの終了はPowerShellのfinallyで行う
  • Quit()はすべてのブック処理が終わった後に一度だけ実行する

MINIMUM SAMPLE

VBAは処理だけ、PowerShellが閉じる

VBAでは、ブックやExcelを閉じず、処理に失敗した場合はエラーを呼び出し元へ返します。

VBA
Option Explicit

Public Sub ExecuteMain()

    Dim errorNumber As Long
    Dim errorDescription As String

    On Error GoTo ErrorHandler

    '集計や転記など、ブック内の処理だけを行う

    Exit Sub

ErrorHandler:
    errorNumber = Err.Number
    errorDescription = Err.Description

    On Error GoTo 0
    Err.Raise errorNumber, "ExecuteMain", errorDescription

End Sub

PowerShell側は、マクロが正常終了した場合に保存し、finallyでブックとExcelを閉じます。次は役割分担を示すための最小構成です。

PowerShell / 構成例
$excel = $null
$workbook = $null

try {
    $excel = New-Object -ComObject Excel.Application
    $excel.Visible = $false
    $excel.DisplayAlerts = $false

    $workbook = $excel.Workbooks.Open($bookPath)

    $macroName = "'$($workbook.Name)'!ExecuteMain"
    $excel.Run($macroName)

    $workbook.Save()
}
finally {
    if ($null -ne $workbook) {
        $workbook.Close($false)
        $workbook = $null
    }

    if ($null -ne $excel) {
        $excel.Quit()
        $excel = $null
    }
}
i

複数ブックを処理するときは、ブックのOpen・Run・Save・Closeをループ内で行い、ExcelのQuit()だけをループの外へ置きます。実際のパスやマクロ名は環境に合わせて変更してください。

SUMMARY

終了処理を重ねず、Excelの管理者を決める

  • Workbook.Closeはブック、Application.QuitはExcel全体を終了する
  • VBAがExcelを終了すると、PowerShell側のCOM参照は使えなくなる
  • 終了済みのオブジェクトへ再度アクセスすると、RPC_E_DISCONNECTEDが発生する
  • PowerShellが連続実行を管理するなら、VBAはブック内の処理だけにする
  • 保存・Close・Quitの担当をPowerShellへまとめ、Quitは最後に一度だけ実行する

今回のように、最初のVBAエラーをきっかけにエラー処理がApplication.Quitを実行すると、PowerShell側では別の切断エラーとして見えることがあります。最初のエラーと終了時のエラーを分けて考えることが、原因の特定につながります。

MicroSoft公式

Microsoft Learn:Application.Quit method
Microsoft Learn:Workbook.Close method
Microsoft Learn:PowerShellでCOMオブジェクトを操作する

VBA・Excel自動化の記事