VB.NETでExcel編集を行った後でExcelプロセスが消えないトラブル

このエントリーをはてなブックマークに追加

先日VB.NETでExcelブックにデータを読み書きするプログラムを作っていたのですが、
その際にタスクマネージャにExcelのプロセスが残ったままになる現象が起きてはまって
しまったので備忘録を書いておこうと思います。

【環境】
・Windows 10
・Visual Studio 2013
・Microsoft Office 2013

結論から言うとExcelのオブジェクトを解放してあげればいいのですが、それにはコツがあるのです。

NG例

xlApplication.Workbooks.Item("Sheet1")

'■COMオブジェクトの解放
Call ReleaseCOMObject(xlsApp)

何がいけないかというと「xlsApp.Workbooks.Item(“Sheet1”)」の部分です。
ドット(.)が二つ以上続く操作を行う場合は、COMオブジェクトを解放してもExcelのプロセスが残ってしまいます。
この場合は一つずつ分解して変数に保持しなければいけません。

OK例(サンプルソース)

Imports Microsoft.Office.Interop

Public Shared Sub P_Sample()

'■Excel用
Dim xlApplication As Excel.Application = Nothing
Dim xlWkBooks As Excel.Workbooks = Nothing
Dim xlBook As Excel.Workbook = Nothing
Dim xlSheets As Excel.Sheets = Nothing
Dim xlWkSheet As Excel.Worksheet = Nothing
Dim xlActvSheet As Excel.Worksheet = Nothing
Dim xlRange As Excel.Range = Nothing

Try

'■Excel.Application の新しいインスタンスを生成する
xlApplication = New Excel.Application()

'■アプリケーションの非表示
xlApplication.Visible = False

'■test.xlsmを開く
xlWkBooks = xlApplication.Workbooks
xlBook = xlWkBooks.Open(“D:\test.xlsm”)

'■シートの指定
xlSheets = xlBook.Worksheets

'***********************
' [Sheet1]シート
'***********************
xlWkSheet = xlSheets.Item("Sheet1")
xlWkSheet.Activate()

'■指定エリアの値をクリア
xlRange = xlWkSheet.Range("B7:C59")
xlRange.Value = ""
'■COMオブジェクトの解放
Call ReleaseCOMObject(xlRange)

'■COMオブジェクトの解放
Call ReleaseCOMObject(xlWkSheet)

'***********************
' [Sheet2]シート
'***********************
xlWkSheet = xlSheets.Item("Sheet2")
xlWkSheet.Activate()

'■指定エリアの値をクリア
xlRange = xlWkSheet.Range("B7:C59")
xlRange.Value = ""
'■COMオブジェクトの解放
Call ReleaseCOMObject(xlRange)

'■COMオブジェクトの解放
Call ReleaseCOMObject(xlWkSheet)

'----------------------------------------
' Sheet1シートに社員番号、氏名をセット
'----------------------------------------

xlWkSheet = xlSheets.Item("Sheet1")
xlWkSheet.Activate()

xlRange = xlWkSheet.Range("C1”)
xlRange.Value = “001” ‘社員番号
'■COMオブジェクトの解放
Call ReleaseCOMObject(xlRange)

xlRange = xlWkSheet.Range("D1”)
xlRange.Value = “佐藤” ‘氏名
'■COMオブジェクトの解放
Call ReleaseCOMObject(xlRange)

'■COMオブジェクトの解放
Call ReleaseCOMObject(xlWkSheet)

'■Excelを上書保存して閉じる
xlBook.Close(True)

'■COM オブジェクトの参照カウントを解放する
Call ReleaseCOMObject(xlSheets)
Call ReleaseCOMObject(xlWkBooks)
Call ReleaseCOMObject(xlBook)

xlApplication.Quit()
Call ReleaseCOMObject(xlApplication)

Exit Sub
'**エラー処理 ******************************************
Catch ex As Exception

'■エラーメッセージとログ出力、強制終了
Messagebox.Show “処理に失敗しました。”

Finally

'■Excelを保存
If Not xlBook Is Nothing Then
xlBook.Close(True)
End If

'■COM オブジェクトの参照カウントを解放する
Call ReleaseCOMObject(xlRange)
Call ReleaseCOMObject(xlWkSheet)
Call ReleaseCOMObject(xlSheets)
Call ReleaseCOMObject(xlWkBooks)
Call ReleaseCOMObject(xlBook)

If Not xlApplication Is Nothing Then
xlApplication.Quit()
End If

Call ReleaseCOMObject(xlApplication)
End Try
End Sub

'■====================================================■
'■ COMオブジェクトの解放
'■====================================================■
Public Shared Sub ReleaseCOMObject(Of T As Class)(ByRef objCom As T)

'オブジェクトが空の場合処理を抜ける
If objCom Is Nothing Then
Return
End If

Try
'パラメタがCOMオブジェクトかチェック
If System.Runtime.InteropServices.Marshal.IsComObject(objCom) Then

'Runtime Callable Wrapperの解放を行う
Dim cntRCW As Integer = System.Runtime.InteropServices.Marshal.FinalReleaseComObject(objCom)

If cntRCW <> 0 Then
'解放しきれていない場合、メッセージを出力
MessageBox.Show("解放エラー")
End If

End If

Finally
objCom = Nothing
End Try

End Sub

一つの操作ごとにCOMオブジェクトを解放しています。
また、最後にCOMオブジェクトを解放だけではなく、「Excel.Application」の「Quit」と
Excel.Workbook」の「Close」を必ず行って下さい。

COMオブジェクトの解放が煩わしという人には「ClosedXML」や「EPPlus」といったライブラリを
使ったExcelファイルの読み書きを行う方法もありますよ。