Во время модификации существующего COM-объекта ATL я наткнулся на статью из блога «The Old New Thing» под названием «Пути людей испортили IUnknown :: QueryInterface», и было обсуждение в разделе комментариев, которое начиналось, когда один респондентов (Норман Даймонд) отметили, что в одном из примеров статьи, что литье в пустоту ** было неверным.Каков правильный способ использования при использовании ATL и IUnknownPtr?
Однако, когда я пытаюсь исправить свой код, чтобы правильно выполнить кастинг, я получаю утечку памяти.
пример следующим образом:
IShellFolder *psf = some object;
IUnknown *punk = NULL;
psf->QueryInterface(IID_IUnknown, (void**)&punk);
Норман сказал
панк не является недействительным *. punk - это IUnknown *.
void ** не является универсальным типом указателя. void * - универсальный тип указателя, а char * и родственники дедушкины, чтобы быть эквивалентными таким образом, но void ** - нет.
Если вы хотите повиноваться соглашению о вызове и избегать ужасных смертей, вы должны сделать это: IUnknown * punk; void * punkvoid; psf-> QueryInterface (IID_IUnknown, & punkvoid); punk = (IUnknown *) punkvoid;
Многие другие разработчики MSDN совершили ту же самую ошибку. Некоторые люди могут сказать, что она работает во всех реализациях VC++ до настоящего времени, но это не делает правильный код и по-прежнему нарушает соглашение о вызове.
В свете этого я пошел, чтобы изменить свой старый код - который был следующим:
#include <comdef.h>
...
HRESULT FinalConstruct()
{
if (m_dwROTCookie != 0)
return E_FAIL;
//Check whether there already is an instance of the Object
IUnknownPtr pUnk = NULL;
if (GetActiveObject(CLSID_Object, NULL, &pUnk) == S_OK)
{
TRACE_WARNING("An instance of Object already exists in the current context");
return S_OK;
}
HRESULT hr = QueryInterface(IID_IUnknown, reinterpret_cast<void **>(&pUnk));
hr = RegisterActiveObject(pUnk, CLSID_Object, ACTIVEOBJECT_WEAK, m_dwROTCookie);
if (FAILED(hr))
return hr;
hr = CoLockObjectExternal(pUnk, TRUE, TRUE);
pUnk = NULL;
ATLASSERT(m_dwRef == 2);
return hr;
}
Затем я изменил его следующим образом:
HRESULT FinalConstruct()
{
if (m_dwROTCookie != 0)
return E_FAIL;
//Check whether there already is an instance of the Object
IUnknownPtr pUnk = NULL;
if (GetActiveObject(CLSID_Object, NULL, &pUnk) == S_OK)
{
TRACE_WARNING("An instance of Object already exists in the current context");
return S_OK;
}
void* pUnkVoid = NULL;
HRESULT hr = QueryInterface(IID_IUnknown, &pUnkVoid);
if (SUCCEEDED(hr)
{
pUnk = reinterpret_cast<IUnknown*>(pUnkVoid);
hr = RegisterActiveObject(pUnk, CLSID_Object, ACTIVEOBJECT_WEAK, m_dwROTCookie);
if (FAILED(hr))
return hr;
hr = CoLockObjectExternal(pUnk, TRUE, TRUE);
pUnk = NULL;
}
ATLASSERT(m_dwRef == 2);
return hr;
Однако теперь мое приложение имеет утечка памяти из этого COM-объекта