TL;DR — クイックフィックス
簡単に言うと、コードが既に存在しないExcelオブジェクト(Workbook、Worksheet、Range)への参照を保持しています。ワークブックが閉じられたか、COMサーバーがクラッシュしたか、マクロの実行中にExcelが強制終了されたかのいずれかです。使用前に毎回参照を新たに取得し、まだ有効かどうかを確認するガードを追加してください。
ほとんどのケースを解決するクイックパターン:
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet1") ' 毎回新たに取得する
' 使用前にガードをかける
If Not ws Is Nothing Then
ws.Range("A1").Value = "Hello"
End If
このエラーの原因
内部的には、0x80010108はCOM/DCOMエラー、具体的にはRPC_E_DISCONNECTEDです。VBAが既にシャットダウンしたCOMサーバー上のオブジェクトのメソッドを呼び出したり、プロパティを読み取ろうとした瞬間にWindowsがこのエラーを発生させます。
十中八九、以下のいずれかのシナリオが原因です:
- コードが
WorkbookまたはWorksheet変数でワークブックを参照したまま、そのワークブックが閉じられた。 - 別のOfficeアプリ(Access、Word、Outlook)から
CreateObject("Excel.Application")またはGetObject()でExcelを開いたが、実行中にそのExcelインスタンスがシャットダウンした。 - 長時間実行されるループで、ユーザーが手動でワークブックを閉じた後も古いオブジェクト参照が残った。
- モジュールレベルまたはグローバル変数が、次のマクロ実行前に閉じられたオブジェクトへの参照を保持していた。
- ExcelのCOMサーバーがクラッシュまたは強制終了された(タスクマネージャーでの強制終了、メモリ不足、ハードクラッシュなど)ため、オートメーションの途中で停止した。
修正1 — オブジェクト参照を再取得する(最も一般的な修正)
プロシージャ呼び出しをまたいでオブジェクト参照をキャッシュするのをやめましょう。必要なたびにワークブックとワークシートを新たに参照してください。1行追加するだけで、このエラーを完全に防ぐことができます。
問題のあるパターン(古い参照):
' モジュールレベルの変数 — 危険
Dim gSheet As Worksheet
Sub Init()
Set gSheet = Workbooks("Data.xlsx").Worksheets("Report")
End Sub
Sub ProcessData()
' Init() 実行後に Data.xlsx が閉じられていた場合、ここでクラッシュする
gSheet.Range("A1").Value = "Done" ' ここで Error -2147417848 が発生
End Sub
修正済みパターン(毎回新鮮な参照を取得):
Sub ProcessData()
Dim wb As Workbook
Dim ws As Worksheet
' アクセス前にワークブックが開いているか確認する
On Error Resume Next
Set wb = Workbooks("Data.xlsx")
On Error GoTo 0
If wb Is Nothing Then
MsgBox "Data.xlsx が開かれていません。開いてから再試行してください。"
Exit Sub
End If
Set ws = wb.Worksheets("Report")
ws.Range("A1").Value = "Done"
End Sub
修正2 — 長いループ内でオブジェクトを検証する
長時間実行されるループは罠です。1万行の処理途中でユーザーがData.xlsxを閉じると、マクロは次のws.Cells()呼び出しで容赦なくクラッシュします。100回のイテレーションごとにワークブックがまだ有効かどうかを確認しましょう。
Function IsWorkbookOpen(wbName As String) As Boolean
Dim wb As Workbook
On Error Resume Next
Set wb = Workbooks(wbName)
On Error GoTo 0
IsWorkbookOpen = Not (wb Is Nothing)
End Function
Sub ProcessRows()
Dim i As Long
Dim ws As Worksheet
For i = 1 To 10000
' 100行ごとに再確認する
If i Mod 100 = 0 Then
If Not IsWorkbookOpen("Data.xlsx") Then
MsgBox "ワークブックが閉じられました。行 " & i & " で停止します。"
Exit For
End If
' 確認後に参照を再取得する
Set ws = Workbooks("Data.xlsx").Worksheets("Sheet1")
End If
ws.Cells(i, 1).Value = i
Next i
End Sub
修正3 — クロスアプリケーションオートメーション(Access/Word → Excel)
AccessやWordからExcelを操作すると、新たな障害モードが加わります。Excelがクラッシュしたり、OSに強制終了されたり、タイムアウトしたりすることがあり、xlApp変数が幽霊を指し示したままになります。インラインのOn Error Resume Nextではなく、本物のGoToエラーハンドラが、これを確実にクリーンアップする唯一の方法です。
' Access VBA または Word VBA から実行
Sub ExportToExcel()
Dim xlApp As Object
Dim xlWb As Object
Dim xlWs As Object
On Error GoTo Cleanup
' 新しいExcelインスタンスを作成する
Set xlApp = CreateObject("Excel.Application")
xlApp.Visible = True
Set xlWb = xlApp.Workbooks.Add
Set xlWs = xlWb.Worksheets(1)
xlWs.Range("A1").Value = "Exported Data"
xlWb.SaveAs "C:\Reports\output.xlsx"
xlWb.Close False
xlApp.Quit
Set xlWs = Nothing
Set xlWb = Nothing
Set xlApp = Nothing
Exit Sub
Cleanup:
MsgBox "Excelオートメーションが失敗しました: " & Err.Description
' エラー時も必ずクリーンアップする
If Not xlApp Is Nothing Then
On Error Resume Next
xlApp.Quit
Set xlApp = Nothing
End If
End Sub
覚えておくべき3つのルール:
- クリーンアップ処理では必ず
xlApp.Quitを呼び出すこと。リークしたExcelインスタンスはタスクマネージャーに積み重なり、その後のすべてのオートメーション実行が失敗する原因になります。 - オブジェクト変数を
Nothingに設定する順序は逆順で:Worksheet → Workbook → Application。 - メイン処理には
On Error Resume NextではなくGoToエラーハンドラを使用すること。サイレントエラーは何がどこで壊れたかを完全に隠してしまいます。
修正4 — エラーをハンドルしてリトライする
無人スクリプトは深夜3時にメッセージボックスを表示できません。エラー-2147417848を明示的にトラップし、2秒待機してから諦める前にリトライしましょう:
Sub RobustWrite(targetCell As String, value As Variant)
Const MAX_RETRIES As Integer = 3
Dim attempt As Integer
Dim ws As Worksheet
For attempt = 1 To MAX_RETRIES
On Error GoTo RetryHandler
Set ws = ThisWorkbook.Worksheets("Output")
ws.Range(targetCell).Value = value
On Error GoTo 0
Exit For ' 成功 — ループを抜ける
RetryHandler:
If Err.Number = -2147417848 Then
' COM切断 — 待機してリトライ
Application.Wait Now + TimeValue("00:00:02")
Err.Clear
Resume Next
Else
' 別のエラー — 握りつぶさない
Err.Raise Err.Number, Err.Source, Err.Description
End If
Next attempt
End Sub
修正5 — エラーを引き起こすワークブックレベルのイベント
ワークブックやワークシートのイベントは思わぬ落とし穴になることがあります。Workbook_BeforeCloseやWorksheet_Deactivateが発火する時点では、ExcelはすでにオブジェクトのWinding Downを開始しています。Range参照は技術的にはまだ存在していますが、信頼性は保証されません。視覚的な更新処理は、より早い安全なイベントに移動してください。
' 誤り — シートが非アクティブ化中で、Range参照が切断される可能性がある
Private Sub Worksheet_Deactivate()
Me.Range("A1").Interior.Color = RGB(255, 0, 0) ' 80010108 が発生する可能性あり
End Sub
' 正しい — フラグを使用して、より安全なイベントでハンドルする
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
' シートがまだ完全にアクティブな間に視覚的な更新を行う
Me.Range("A1").Interior.Color = RGB(255, 0, 0)
End Sub
確認 — 修正が機能したことを確認する
- VBAエディタ(
Alt+F11)を開き、以前クラッシュしていた行にブレークポイントを設定します。 F5でマクロを実行します。ブレークポイントで一時停止したら、イミディエイトウィンドウ(Ctrl+G)を開いて? ws Is Nothingと入力します(実際の変数名に置き換えてください)。Falseと表示されるはずです。- ブレークポイントを削除して、マクロを全体実行します。エラーダイアログが表示されなければ修正は成功です。
- クロスアプリオートメーションの場合:マクロ終了後にタスクマネージャー → 詳細タブを開きます。孤立した
EXCEL.EXEプロセスがゼロであれば、クリーンアップコードが正しく機能しています。
まとめ
- エラー
-2147417848 (80010108)= コードがすでに切断されたCOMオブジェクトを呼び出しています。 - ほとんどの修正方法:オブジェクト参照をキャッシュせず毎回新たに取得し、使用前に
Is Nothingで確認する。 - クロスアプリオートメーションの場合:クリーンアップハンドラで必ず
Quit+Set Nothingを実行する。 - イベントハンドラの場合:
DeactivateやBeforeCloseイベント内でシートオブジェクトへのアクセスを避ける。

