最近我做了一个涉及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 ++“Unit”对象被销毁时被调用?
答案 0 :(得分:2)
虽然我不熟悉C#和COM集成,但我知道C#中的析构函数非常与C ++中的析构函数不同。 C#对象是内存管理和垃圾收集。这意味着在对象停止被它所属的应用程序引用并且变为“无法访问”之后的某个时刻,垃圾收集器将销毁它。
所以第一个重要的事情是被抛弃的对象和垃圾收集器之间的延迟是不确定的......它将在“未来的某个时刻”发生,这可能是应用程序终止的时候
其次,垃圾收集器并不总是在运行。当你的应用程序分配大块内存并且可用的可用内存耗尽时,存在“内存压力”的概念......此时,垃圾收集器将触发以清除旧的,无法访问的对象。如果您的应用程序没有分配大量内存,它将不会受到任何内存压力,垃圾收集器也不需要运行。
如果要确定性地清理托管对象的某些资源,则需要使用类似IDisposable
接口的内容并明确调用Dispose
方法。
答案 1 :(得分:2)
这完全正常并且是垃圾收集器的副作用。当托管应用分配足够的内存来填充代并触发集合时,它只收集垃圾并运行终结器。如果你没有足够的分配,那么在应用程序终止之前不会发生这种情况。
这应该不是问题,请确保除了释放未处理的非托管资源之外,不要使用终结器执行任何操作。在99.9%的情况下编写终结器是错误的,终结器是.NET框架类的实现细节。就像SafeHandle类一样。否则,无法让客户端应用程序中的确定性破坏在托管代码中产生确定性处置。如果您确实需要处置成员,那么您需要公开客户端代码可以调用的方法。