Объект C # не уничтожается при использовании интерфейса COM / ATL в переполнении стека

В последнее время я делал небольшой проект, который включал модуль DLL (который был создан с C #) и который мне нужно было использовать в моем приложении (которое было написано на неуправляемом C ++). Чтобы это работало, я использовал ATL / COM.

Я заметил, что хотя я использую _com_ptr_t в моем приложении C ++ для обработки моего основного 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» уничтожен?

2

Решение

Хотя я не знаком с интеграцией C # и COM, я знаю, что деструкторы в C # работают очень в отличие от деструкторов в C ++. Объект C # управляется памятью и собирается сборщиком мусора. Это означает, что в какой-то момент после того, как объект перестанет ссылаться на приложение, к которому он принадлежит, и станет «недоступным», сборщик мусора уничтожит его.

Итак, первая важная вещь заключается в том, что задержка между оставляемым объектом и уничтожающим его сборщиком мусора является недетерминированной … это произойдет «в какой-то момент в будущем», что вполне может произойти в момент завершения приложения.

Во-вторых, сборщик мусора не всегда работает. Существует понятие «нехватка памяти», когда ваше приложение выделяет большие куски памяти, а доступная ему свободная память заканчивается … в этот момент сборщик мусора сработает, чтобы избавиться от старых недоступных объектов. Если ваше приложение не выделяет много памяти, оно не будет страдать от какого-либо давления памяти, и сборщик мусора не будет нуждаться в запуске.

Если вы хотите детерминистически очистить некоторые ресурсы управляемого объекта, вам нужно будет использовать что-то вроде IDisposable интерфейс и вызвать Dispose метод явно.

2

Другие решения

Это совершенно нормально и является побочным эффектом сборщика мусора. Который собирает только мусор и запускает финализатор, когда управляемое приложение выделяет достаточно памяти для заполнения поколения и запуска коллекции. Если вы не выделите достаточно средств, этого не произойдет, пока приложение не завершится.

Это не должно быть проблемой, убедитесь, что вы не используете финализатор для каких-либо действий, кроме освобождения неуправляемых ресурсов, которые не были утилизированы. Написание финализатора неправильно в 99,9% всех случаев, финализаторы — это детали реализации классов платформы .NET. Как и классы SafeHandle. В противном случае невозможно позволить детерминированному уничтожению в клиентском приложении произвести детерминированное удаление в управляемом коде. Если вам нужно избавиться от членов, вам нужно предоставить метод, который может вызвать клиентский код.

2

По вопросам рекламы [email protected]