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

我注意到,即使我在C++应用程序中使用_com_ptr_t处理我的核心COM接口(interface),也只有在我的应用程序关闭时才调用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++ header 中,我有:
#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++“Unit”对象时不调用析构函数?

最佳答案

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

因此,首先重要的是,对象被遗弃与垃圾收集器破坏该对象之间的延迟是不确定的……它将在“将来的某个时刻”发生,这很可能在应用程序终止时发生。

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

如果要确定性地清理某些托管对象的资源,则需要使用诸如IDisposable接口(interface)之类的方法,并显式调用Dispose方法。

07-24 09:37
查看更多