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 会議室などを中心に私が何度か書いておりましたが、サイトでの説明が不十分なままでした。
サンプルコード
以下にサンプルコードを示します。
C# 全般
// 必要な変数は try の外で宣言する Excel.Application xlApplication = null; // COM オブジェクトの解放を保証するために try ~ finally を使用する try { xlApplication = new Excel.Application(); // 警告メッセージなどを表示しないようにする xlApplication.DisplayAlerts = false; Excel.Workbooks xlBooks = xlApplication.Workbooks; try { Excel.Workbook xlBook = xlBooks.Add(string.Empty); try { Excel.Sheets xlSheets = xlBook.Worksheets; try { Excel.Worksheet xlSheet = (Excel.Worksheet)xlSheets[1]; try { Excel.Range xlCells = xlSheet.Cells; try { Excel.Range xlRange = (Excel.Range)xlCells[6, 4]; 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 (xlRange != null) { System.Runtime.InteropServices.Marshal.ReleaseComObject(xlRange); } } } finally { if (xlCells != null) { System.Runtime.InteropServices.Marshal.ReleaseComObject(xlCells); } } } finally { if (xlSheet != null) { System.Runtime.InteropServices.Marshal.ReleaseComObject(xlSheet); } } } finally { if (xlSheets != null) { System.Runtime.InteropServices.Marshal.ReleaseComObject(xlSheets); } } } finally { if (xlBook != null) { try { xlBook.Close(); } finally { System.Runtime.InteropServices.Marshal.ReleaseComObject(xlBook); } } } } finally { if (xlBooks != null) { System.Runtime.InteropServices.Marshal.ReleaseComObject(xlBooks); } } } finally { if (xlApplication != null) { try { xlApplication.Quit(); } finally { System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApplication); } } }
関連するリファレンス
準備中です。