最近,我正在做一个涉及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
方法。