1

最近、DLLモジュール(C#で作成されたもの)を含み、アプリケーションで使用する必要がある(アンマネージC ++で記述された)小さなプロジェクトを実行していました。これを機能させるには、ATL/COMを使用していました。

コアCOMインターフェイスを処理するためにC++アプリケーションで_com_ptr_tを使用しているにもかかわらず、C#オブジェクトのデストラクタは、アプリケーションが閉じられたときにのみ呼び出されることに気付きました。

物事をもう少し明確にするための情報源をいくつか紹介しましょう。

私のC#コードのいくつか:

[ComVisible(true)]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
public interface ITestCOM
{
    [DispId(1)]
    void Connect([In, MarshalAs(UnmanagedType.U2)] ushort value);
}

[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(ITestCOMEvents))]
public partial class TestCOM : ITestCOM
{
   ...
   ~TestCOM()
   {
      MessageBox.Show("DESTRUCTOR");
   }
   ...
   public void Connect(ushort value)
   {
      ...
   }
}

次のようなものを使用して.tlbファイルを作成しています: " RegAsm.exe TestCOM.dll /tlb:Test.tlb / codebase "

私のC++ヘッダーには次のものがあります。

#import "C:\...\mscorlib.tlb"
#import "......\TestCOM.tlb" named_guids exclude("ISupportErrorInfo")

#include <afxdisp.h>
#include <atlcom.h>

class Unit : public ::IDispEventSimpleImpl<0, Unit, &__uuidof(TestCOM::ITestCOMEvents)>
{
public:
   BEGIN_SINK_MAP(Unit)
      SINK_ENTRY_INFO(0, __uuidof(TestCOM::ITestCOMEvents), 0x1, OnEventCallback, &OnEventCallbackDef)
   END_SINK_MAP()

   ...

private

   TestCOM::ITestCOMPtr mTestCOM;
   // NOTE: This would be the same as "_com_ptr_t<_com_IIID<TestCOM::ITestCOM,  &__uuidof(TestCOM::ITestCOM)> > mTestCOM;"
}

そして、私のC ++ソースファイルは、次のように「mTestCOM」を作成します。

mTestCOM.CreateInstance(TestCOM::CLSID_TestCOM)

そして、基本的にはそれだけです。C#の「TestCOM」オブジェクトの関数は次のように使用できます。

mTestCOM->Connect(7);

問題は、C ++の「ユニット」オブジェクトが破棄されたときではなく、アプリケーションが閉じられたときにのみC#TestCOMオブジェクトのデストラクタが呼び出されるのはなぜですか。

4

2 に答える 2

2

私はC#とCOMの統合に精通していませんが、C#のデストラクタはC++のデストラクタとは非常に異なる動作をすることを知っています。AC#オブジェクトはメモリ管理され、ガベージコレクションされます。これは、オブジェクトが属するアプリケーションによる参照を停止し、「到達不能」になった後のある時点で、ガベージコレクターがオブジェクトを破棄することを意味します。

したがって、最初に重要なことは、オブジェクトが破棄されてからガベージコレクターがオブジェクトを破棄するまでの遅延は非決定的であるということです...これは「将来のある時点」で発生します。これは、アプリケーションが終了する時点である可能性があります。

第二に、ガベージコレクターは常に実行されているわけではありません。「メモリプレッシャー」の概念があります。アプリケーションが大量のメモリを割り当てていて、使用可能な空きメモリが不足している場合...その時点で、ガベージコレクタが起動して、古い到達不能なオブジェクトを取り除きます。アプリケーションが大量のメモリを割り当てない場合、メモリの負荷に悩まされることはなく、ガベージコレクタを実行する必要はありません。

管理対象オブジェクトのリソースの一部を決定論的にクリーンアップする場合は、インターフェイスなどを使用して、メソッドを明示的にIDisposable呼び出す必要があります。Dispose

于 2012-11-14T15:19:31.177 に答える
2

これは完全に正常であり、ガベージコレクタの副作用です。これは、管理対象アプリが世代をいっぱいにしてコレクションをトリガーするのに十分なメモリを割り当てたときに、ガベージを収集し、ファイナライザーを実行するだけです。十分に割り当てないと、アプリが終了するまでこれは発生しません。

これは問題にはならないはずです。ファイナライザーを使用して、破棄されなかったアンマネージリソースを解放する以外のことを行わないようにしてください。ファイナライザーを作成することは、すべてのケースの99.9%で行うのは間違っています。ファイナライザーは、.NETFrameworkクラスの実装の詳細です。SafeHandleクラスのように。それ以外の場合、クライアントアプリでの決定論的破棄によって、マネージコードで決定論的破棄が生成されるようにする方法はありません。メンバーを破棄する必要がある場合は、クライアントコードが呼び出すことができるメソッドを公開する必要があります。

于 2012-11-14T15:22:36.363 に答える