Ich habe ein verwaltetes COM-Objekt geschrieben in C# und einen nativen COM-Client und Senke in C++ geschrieben (MFC und ATL). Der Client erstellt das Objekt und informiert seine Ereignisschnittstelle beim Start, entlädt sich von seiner Ereignisschnittstelle und gibt das Objekt beim Herunterfahren frei. Das Problem ist, dass das COM-Objekt einen Verweis auf die Senke hat, die erst freigegeben wird, wenn die Garbage Collection ausgeführt wird. Zu diesem Zeitpunkt ist der Client bereits abgerissen und führt daher normalerweise zu einer Zugriffsverletzung. Es ist wahrscheinlich nicht so eine große Sache, da der Client sowieso heruntergefahren wird, aber ich würde das gerne nach Möglichkeit auflösen. Ich brauche mein COM-Objekt, um mein Sink-Objekt schneller freizugeben, und ich weiß nicht, wo ich anfangen soll, da mein COM-Objekt nicht explizit mit dem Sink-Objekt arbeitet.Wie wird die Lebensdauer von Objekten beim Arbeiten mit COM-Interop verwaltet?
Mein COM-Objekt:
public delegate void TestEventDelegate(int i);
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ITestObject
{
int TestMethod();
void InvokeTestEvent();
}
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ITestObjectEvents
{
void TestEvent(int i);
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(ITestObjectEvents))]
public class TestObject : ITestObject
{
public event TestEventDelegate TestEvent;
public TestObject() { }
public int TestMethod()
{
return 42;
}
public void InvokeTestEvent()
{
if (TestEvent != null)
{
TestEvent(42);
}
}
}
Der Client ist ein Standard-MFC-Dialog-Programm, mit zusätzlicher Unterstützung für ATL. Meine Senke-Klasse:
class CTestObjectEventsSink : public CComObjectRootEx<CComSingleThreadModel>, public ITestObjectEvents
{
public:
BEGIN_COM_MAP(CTestObjectEventsSink)
COM_INTERFACE_ENTRY_IID(__uuidof(ITestObjectEvents), ITestObjectEvents)
END_COM_MAP()
HRESULT __stdcall raw_TestEvent(long i)
{
return S_OK;
}
};
Ich habe folgende Mitglieder in meiner Dialogklasse:
ITestObjectPtr m_TestObject;
CComObject<CTestObjectEventsSink>* m_TestObjectEventsSink;
DWORD m_Cookie;
In OnInitDialog():
HRESULT hr = m_TestObject.CreateInstance(__uuidof(TestObject));
if(m_TestObject)
{
hr = CComObject<CTestObjectEventsSink>::CreateInstance(&m_TestObjectEventsSink);
if(SUCCEEDED(hr))
{
m_TestObjectEventsSink->AddRef(); // CComObject::CreateInstace() gives an object with a ref count of 0
hr = AtlAdvise(m_TestObject, m_TestObjectEventsSink, __uuidof(ITestObjectEvents), &m_Cookie);
}
}
In OnDestroy():
if(m_TestObject)
{
HRESULT hr = AtlUnadvise(m_TestObject, __uuidof(ITestObjectEvents), m_Cookie);
m_Cookie = 0;
m_TestObjectEventsSink->Release();
m_TestObjectEventsSink = NULL;
m_TestObject.Release();
}
Sieht mir aus, Sie vergessen m_TestObjectEventsSink-> Release(). Es ist nicht automatisch, da Sie einen Zeiger auf CComObject <> speichern, wahrscheinlich werden Sie es gerade verlieren. Nicht sicher, warum das notwendig wäre. –
Hoppla, tut mir leid. Habe das vergessen, aber der Effekt ist der gleiche wie CComObject :: CreateInstance() gibt dir ein Objekt mit einem ref count von 0. Ich werde die Frage trotzdem aktualisieren. – Luke
CComObject :: CreateInstance() gibt Ihnen ein Objekt mit einem ref count von 0; es liegt in Ihrer Verantwortung, AddRef() es. – Luke