COM オブジェクトの参照カウントを解放する
- C#
- VB.NET
スポンサーリンク
COM オブジェクトを扱う場合は、参照カウントの解放を意識しなければなりません。使用した COM オブジェクトの参照カウントが、正しくデクリメントされていないと意図したタイミングでプロセスが解放されません。
参照カウントのデクリメントは、System.Runtime.InteropServices.Marshal クラスにある ReleaseComObject メソッドを使用します。COM ラッパ オブジェクト (COM そのものではない) の参照が未到達になれば、ガベージ コレクションを実行して解放することができますが、こちらはあくまでも保険です。
参照カウントのデクリメント解放自体は、ReleaseComObject メソッドを呼び出すだけですので難しくはありません。面倒なのは、ReleaseComObject メソッドを実行するために、参照したものをすべて変数へ格納しておく必要があるという点です。この点、COM 専用の言語である Visual Basic 6 以前とは、全く勝手が違いますので注意する必要があります。
また、処理の途中で例外が発生した場合でも参照カウントをデクリメントするために、try ~ finally を多用することになります。一連の流れを 1 つのメソッドで表現すると、どうしても可読性が悪くなってしまいます。
たとえば、下記は技術系掲示板でよく話題にあがる、Office PIA / Excel Interop 系のコード例です。こんなにネストが深くなってしまった時点で、Coding Horror にあたると思いますが、説明のためにそのままにしています。
個人的には COM を扱う場合は COM 専用の言語で実装することをお勧めします。VBA 側のマクロを .NET アプリケーションから呼び出す方法、VB または VBScript に処理を実装して呼び出す方法、あるいは ScriptControl を利用する方法があります。これらの方法であれば参照カウントの面倒はそれほど面倒ではありません。
とくに ASP.NET (Web アプリケーション) では使用しないことをお勧めします。サーバーサイドのオートメーションは技術的にも多くの問題をかかえており (障害が発生した場合 Web サーバの再立ち上げが必要な場合が多い) Microsoft も動作保証をしていません。
このあたりは @IT 会議室などを中心に私が何度か書いておりましたが、サイトでの説明が不十分なままでした。
サンプルコード
以下にサンプルコードを示します。
VB.NET 全般
' 必要な変数は Try の外で宣言する
Dim xlApplication As Excel.Application
' COM オブジェクトの解放を保証するために Try ~ Finally を使用する
Try
xlApplication = New Excel.Application()
' 警告メッセージなどを表示しないようにする
xlApplication.DisplayAlerts = False
Dim xlBooks As Excel.Workbooks = xlApplication.Workbooks
Try
Dim xlBook As Excel.Workbook = xlBooks.Add()
Try
Dim xlSheets As Excel.Sheets = xlBook.Worksheets
Try
Dim xlSheet As Excel.Worksheet = DirectCast(xlSheets(1), Excel.Worksheet)
Try
Dim xlCells As Excel.Range = xlSheet.Cells
Try
Dim xlRange As Excel.Range = DirectCast(xlCells(6, 4), Excel.Range)
Try
' Microsoft Excel を表示する
xlApplication.Visible = True
' 1000 ミリ秒 (1秒) 待機する
System.Threading.Thread.Sleep(1000)
' Row=6, Column=4 の位置に文字をセットする
xlRange.Value2 = "あと 1 秒で終了します"
' 1000 ミリ秒 (1秒) 待機する
System.Threading.Thread.Sleep(1000)
Finally
If Not xlRange Is Nothing Then
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlRange)
End If
End Try
Finally
If Not xlCells Is Nothing Then
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlCells)
End If
End Try
Finally
If Not xlSheet Is Nothing Then
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlSheet)
End If
End Try
Finally
If Not xlSheets Is Nothing Then
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlSheets)
End If
End Try
Finally
If Not xlBook Is Nothing Then
Try
xlBook.Close()
Finally
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlBook)
End Try
End If
End Try
Finally
If Not xlBooks Is Nothing Then
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlBooks)
End If
End Try
Finally
If Not xlApplication Is Nothing Then
Try
xlApplication.Quit()
Finally
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApplication)
End Try
End If
End Try
関連するリファレンス
準備中です。
