VB.NET から Excel の COM オブジェクトを操作する際に COM オブジェクトを適切に解放しないと,Excel のプロセスが正しく終了しない。
Excel のプロセスが正しく終了するための Excel COM オブジェクトの取り扱い方法を解説する。
まずは次のサンプルコードを Windows Form のボタンクリックイベントに記述して実行してみましょう。
※ Excel COM オブジェクトをコントロールするためには,Microsoft Excel 11.0 Object Library の参照設定が必要です(Excel 2003 の場合)。
Dim myExcel As New Microsoft.Office.Interop.Excel.Application
Dim books As Microsoft.Office.Interop.Excel.Workbooks
Dim book As Microsoft.Office.Interop.Excel.Workbook
Try
books = myExcel.Workbooks
book = books.Add
book.Close()
myExcel.Quit()
Finally
' COM オブジェクトの解放
System.Runtime.InteropServices.Marshal.ReleaseComObject(book)
System.Runtime.InteropServices.Marshal.ReleaseComObject(books)
' 次の行を実行すると Excel のプロセスが終了するはず
System.Runtime.InteropServices.Marshal.ReleaseComObject(myExcel)
End Try
このソースコードでは,Excel のプロセスを起動して,新規ブックを作成しています。その後ブックを閉じて,Excel を終了しています。
このソースコードをステップ実行しながら,[タスクマネージャ] の [プロセス] タブを確認してください。
[myExcel] のインスタンスが作成された時点で Excel のプロセスが [プロセス] にエントリされます。
その後,「myExcel.Quit」メソッドを実行しても,Excel のプロセスは終了されません。
Excel のプロセスが終了されるのは,次の行で[myExcel] のインスタンスが解放された時点です。
Excel の Application インスタンスを保持している変数が正しく解放されると,Excel のプロセスが終了されます。
今度は,次のソースコードを実行して,Excel のプロセスを監視します。
Dim myExcel As New Microsoft.Office.Interop.Excel.Application
Dim book As Microsoft.Office.Interop.Excel.Workbook
Try
book = myExcel.Workbooks.Add
book.Close()
myExcel.Quit()
Finally
' COM オブジェクトの解放
System.Runtime.InteropServices.Marshal.ReleaseComObject(book)
' 次の行を実行すると Excel のプロセスが終了するはず
System.Runtime.InteropServices.Marshal.ReleaseComObject(myExcel)
End Try
このソースコードでは,赤字で示した部分が前のソースコードと異なります。機能的にはまったく同じことを実装していますが,「myExcel.Workbooks.Add」という風にプロパティを直接使用してコードを記述しています。
このソースコードでは,Excel のプロセスは正しく終了されません
前のソースコードとは異なり,[myExcel] のインスタンスを解放しても,Excel のプロセスが残ったままになってしまいます。このソースコードを何度も実行すると Excel のプロセスが次々に増えていきます。
この残ってしまった Excel のプロセスが解放されるのは,Excel を呼び出したプログラムが終了する時点か,ガベージコレクタがオブジェクトを整理する時点のどちらかです。
Excel COM オブジェクトを使用する際には,以下のことに注意しなければいけません。
使用した COM オブジェクトは必ず解放しなければいけない。
COM オブジェクトの解放は,「System.Runtime.InteropServices.Marshal.ReleaseComObject」を使用します。
System.Runtime.InteropServices.Marshal.ReleaseComObject( [COM オブジェクト] )
ループ内で使用する,変数を共有するオブジェクトは都度解放する。
[ReleaseComObject] は,インスタンス化したオブジェクトの数だけ行わなければいけません。
ループ内で [Worksheet] オブジェクトを複数回使用する場合は,変数に格納した数だけ解放する必要があります。
For counter As Int32 = 0 To 10
myWorksheet = myWorkbooks.Item(counter)
System.Runtime.InteropServices.Marshal.ReleaseComObject(myWorksheet)
Next
「.(ドット)」を使用して,プロパティやメソッドを経由して呼び出してはいけない。
[Application] クラスの [Workbooks] プロパティなどがこれにあたります。
これらのプロパティは適切な変数に経由して,経由したオブジェクトも [ReleaseComObject] を呼び出さなければいけません。
このルールは見落としがちです。
Dim myExcel As New Microsoft.Office.Interop.Excel.Application
Dim books As Microsoft.Office.Interop.Excel.Workbooks
books = myExcel.Workbooks
book = books.Add
Cells プロパティではなく,Range プロパティを使用する。
セルの値を書き換える場合などに,[Worksheet] クラスの [Cells] プロパティを使用して,行と列を指定した(R1C1形式)セルを取得することができます。
この [Cells] プロパティは [Range] クラスに DirectCast することによって値の操作が可能になります。
しかし,[Cells] プロパティから [Range] への変換を使用していると,Excel プロセスが正しく解放されません。
セルの名称(A1形式)を指定する [Range] プロパティを使用します。
Dim targetRange As Range
' Cells では解放されない
targetRange = DirectCast(myWorksheet.Cells(3, 1), Range)
' Range は正しく動作する
targetRange = myWorksheet.Range("A3")