COM Interop, Marshal.ReleaseComObject
「じゃんぬねっと日誌」より.
コメント欄でちょこっと書きましたが,C# から COM の参照カウントを扱うのが面倒なら,自動コード生成を行ったり,参照カウント向きの言語*1を併用したりするのがむしろ正攻法かと思います.餅は餅屋.いっそのこと言語自作もありかなと.
以下はコメントで書いたサンプルの再掲.
// 参照設定 // "Microsoft Excel 11.0 Object Library" // "Microsoft Script Control 1.0" using System; using System.Collections; using MSScriptControl; using Excel = Microsoft.Office.Interop.Excel; using System.Threading; using System.Runtime.InteropServices; class Program { static void Main(string[] args) { ArrayList rcws = new ArrayList(); try { ScriptControl scl = new ScriptControl(); rcws.Add(scl); Excel.Application excel = new Excel.Application(); rcws.Add(excel); scl.Language = "VBScript"; scl.AddObject("objExcel", excel, false); scl.ExecuteStatement("objExcel.Visible = True"); scl.ExecuteStatement("Set objWorkBook = objExcel.WorkBooks.Add"); scl.ExecuteStatement("objExcel.ActiveCell.Value = \"hoge\""); scl.ExecuteStatement("objWorkBook.Saved = True"); scl.ExecuteStatement("objWorkBook.Close"); scl.ExecuteStatement("objExcel.UserControl = False"); Thread.Sleep(1000); } finally { foreach (object obj in rcws) { try { Marshal.ReleaseComObject(obj); } catch { } } } } }
VisualBasic 6 で ActiveX コンポーネントを作って,Reg-Free COM ってのもありでしょうね.
参照カウントベースのライブラリとのミスマッチは,Managed DirectX でもしばしば問題になります.
多いのがこのパターン.
Texture texture = ...;
int width = texture.GetSurfaceLevel(0).Description.Width;
GetSurfaceLevel が返す Surface クラスは実は IDisposable で,さらに Dispose を呼ぶことで内部 COM オブジェクトの Release が呼ばれるという仕組みです.そのため,このコードでは Surface オブジェクト (の内部の IDirect3DSurface9* が指すオブジェクト) の解放が次回の GC まで遅れます.元々 C++ で DirectX を使っていた人は,Unmanaged オブジェクトとの対応から危なそうな場所が予想できるでしょうが,そうでない人にとっては難しい問題に見えるかも知れません.