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
関連するリファレンス
準備中です。