1

最近我正在做一个涉及 DLL 模块(用 C# 创建)并且我需要在我的应用程序中使用(用非托管 C++ 编写)的小项目。为此,我使用了 ATL/COM。

我注意到,即使我在 C++ 应用程序中使用_com_ptr_t来处理我的核心 COM 接口,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# TestCOM 对象的析构函数仅在我的应用程序关闭时调用,而不是在我的 C++“单元”对象被销毁时调用?

4

2 回答 2

2

虽然我不熟悉 C# 和 COM 集成,但我知道 C# 中的析构函数与 C++ 中的析构函数的工作方式非常不同。AC# 对象是内存管理和垃圾收集的。这意味着在对象停止被它所属的应用程序引用并变得“不可访问”之后的某个时刻,垃圾收集器将销毁它。

所以第一件重要的事情是,一个对象被放弃和垃圾收集器销毁它之间的延迟是不确定的……它会“在未来的某个时候”发生,这很可能是在应用程序终止的时候。

其次,垃圾收集器并不总是在运行。有“内存压力”的概念,当您的应用程序正在分配大块内存并且可用的内存快用完时......此时,垃圾收集器将启动以摆脱旧的、无法访问的对象。如果您的应用程序没有分配大量内存,它将不会受到任何内存压力,垃圾收集器也不需要运行。

如果您想确定性地清理一些托管对象的资源,您将需要使用IDisposable接口之类的东西并显式调用该Dispose方法。

于 2012-11-14T15:19:31.177 回答
2

This is entirely normal and a side-effect of the garbage collector. Which only collects garbage, and runs the finalizer, when a managed app allocates enough memory to fill up a generation and trigger a collection. If you don't allocate enough then this won't happen until the app terminates.

This should not be a problem, make sure that you don't use the finalizer to do anything other than release unmanaged resources that were not disposed. Writing a finalizer is the wrong thing to do in 99.9% of all cases, finalizers are implementation details of .NET framework classes. Like the SafeHandle classes. There is otherwise no way to let deterministic destruction in the client app produce deterministic disposal in the managed code. If you do need to dispose members then you'll need to expose a method that the client code can call.

于 2012-11-14T15:22:36.363 回答