.Net アセンブリで作成された後、アンマネージ コードに戻される COM オブジェクトの有効期間を管理するアイデアを探しています。
背景: 当社のソフトウェア エンジンはアンマネージ C++ でコーディングされています。当社のソフトウェアの機能は、エンジンの COM オブジェクト コンポーネントを使用して拡張できます。ほとんどの場合、エンジン自体は何年も変更する必要がありません。ただし、ソフトウェアに新しい機能を追加するために、より多くのコンポーネントを構築し続けています。これらのコンポーネントは以前はアンマネージ C++ でもビルドされていましたが、ここ数年は C# でこれらのプラグインをコーディングしてきました。
最近まで、C# コンポーネントで使用されるアンマネージ COM オブジェクトに対して Marshal.ReleaseComObject を呼び出すべきではなく、代わりに RCW とガベージ コレクションがそれらの有効期間を管理できるようにするという考えに同意してきました。
この理論は良さそうに思えますが、実際には、GC がこれらの COM オブジェクトのクリーンアップを怠り、場合によってはソフトウェアが利用可能なメモリをすべて使い果たして失敗することが原因であると思われる、ソフトウェアでメモリの問題が発生し始めています。
GC と RCW がこのオブジェクトに作用するのを妨げている可能性のある間違いはありますか? 必要なときにメモリが解放されていることを確認するために必要なことを行うというガベージ コレクションのアイデアではありませんか?
より良いアイデアがないため、しぶしぶ Marshal.ReleaseComObject (場合によっては FinalReleaseComObject) の使用を開始しました。一般的に、これはうまくいくようです。
ただし、Marshal.ReleaseComObject を使用するための一貫したパターンを開発しようとすると、それを使用できるかどうか確信が持てないケースが 1 つ見つかりました。一部のコンポーネントは、COM オブジェクトの戻り値をアンマネージ エンジンに戻す必要があります。コンポーネントは戻り値を再び見ることはなく、(現在) 使用されなくなったときに通知を受ける方法がありません。例えば:
// Unmanaged C++ code
Engine::Engine() : m_ipDoodadCreator(CLSID_FancyComponent)
{
}
void Engine::ProcessDoodad()
{
try
{
// Smart pointer
IDoodadPtr ipDoodad = m_ipDoodadCreator->CreateDoodad();
...
ipDoodad = NULL; // Calls Release...
}
catch (...) { ... }
// At this point the doodad lives on because the RCW is holding a
// reference to it.
}
// Managed C# Component
public class FancyComponent : IDoodadCreator
{
public IDoodad IDoodadCreator.CreateDoodad()
{
// IDoodad is implmented in unmanaged c++
IDoodad doodad = new IDoodad();
try
{
...
return doodad;
}
finally
{
// Can't do this here because engine doesn't yet have
// a reference to doodad.
// Marshal.ReleaseComObject(doodad);
}
}
}
これまでのところ、この状況で決定論的に RCW にその参照を解放させるために思いつくことができる唯一のアイデアは、エンジンを作り直して、すべてのインターフェースが時々呼び出される Cleanup() 呼び出しを持つようにすることです。しかし、それには時間がかかり、場合によっては実装が非常に面倒です。
tl;dr管理されていない環境に返される COM オブジェクトへの参照を RCW に強制的に解放させる方法はありますか?