Mam zarządzany obiekt COM napisany w języku C# oraz natywny klient COM i zlew napisany w C++ (MFC i ATL). Klient tworzy obiekt i doradza jego interfejsowi zdarzenia podczas uruchamiania, i nie radzi sobie ze swoim interfejsem zdarzeń i zwalnia obiekt podczas zamykania. Problem polega na tym, że obiekt COM odwołuje się do zlewu, który nie jest zwalniany, dopóki nie zostanie uruchomiony proces zbierania śmieci, w którym to momencie klient jest już zerwany, co zwykle skutkuje naruszeniem dostępu. Prawdopodobnie nie jest to wielka sprawa, ponieważ klient i tak się zamyka, ale chciałbym rozwiązać to z wdziękiem, jeśli to możliwe. Potrzebuję mój obiekt COM, aby zwolnić mój obiekt sink w bardziej punktualny sposób, i nie wiem, od czego zacząć, ponieważ mój obiekt COM nie działa jawnie z obiektem sink.Jak zarządzać długością obiektu podczas pracy z interfejsem COM?
Mój obiekt COM:
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);
}
}
}
Klient jest standardowy program oparty na dialog MFC, z dodaną obsługą ATL. Moja klasa umywalką:
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;
}
};
Mam następujący członkowie w mojej klasie dialogowym:
ITestObjectPtr m_TestObject;
CComObject<CTestObjectEventsSink>* m_TestObjectEventsSink;
DWORD m_Cookie;
W 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);
}
}
W 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();
}
Wygląda na to, że zapomniałeś m_TestObjectEventsSink-> Release(). To nie jest automatyczne, ponieważ przechowujesz wskaźnik do CComObject <>, prawdopodobnie po prostu go przeciekasz. Nie wiem, dlaczego to by było konieczne. –
Ups, przepraszam. Zapomniałem o nich, ale efekt jest taki sam jak CComObject :: CreateInstance() daje ci obiekt z liczbą ref 0. Będę aktualizować pytanie niezależnie. – Luke
CComObject :: CreateInstance() daje obiekt o wartości ref 0; Twoim obowiązkiem jest AddRef() to. – Luke