Почему я получаю один и тот же __vfptr для 2 разных экземпляров?

Я запутался с __vfptr, который я вижу через указатели IDispatch/IUnknown. Я создаю внутрипроцессный свободный поток COM-объект (IMyContainer). Внутри этого объекта мне нужно сохранить указатели на 2 разных экземпляра com-объектов, которые реализуют один и тот же IMyInterface. Поэтому я вызываю CreateAndSaveDispToMap() дважды.

Моя идея состоит в том, чтобы сохранить их указатели IDispatch в некоторой std::map. На данный момент я подозреваю, что каждый экземпляр будет иметь refCount 1. И это так. Но на удивление я вижу, что я получаю один и тот же __vftbl через pUnk для двух разных указателей отправки.

Зачем? Как это возможно, что AddRef() и Release() работает нормально?

HRESULT CMyContainer::CreateAndSaveDispToMap(...)
{
...
IMyInterface* pMyInterface = NULL;
hr = ::CoCreateInstance(CLSID_MyInterface, NULL, CLSCTX_INPROC_SERVER, IID_IMyInterface, (void**)&pMyInterface);
pMyInterface->QueryInterface(IID_IDispatch, (void**)&pDisp);
pMyInterface->Release();    // Call Release since QI already called AddRef()
...

IUnknown* pUnk = NULL;
pDisp->QueryInterface(IID_IUnknown, (void**)&pUnk);
int refCount = pUnk->Release();
...
AddToMap(pDisp);
}

2 ответа

Решение

Каждый полиморфный объект будет иметь __vfptr это указатель на виртуальную таблицу фактического класса объекта. Один Vtable генерируется для каждого отдельного класса. Поэтому для любых двух объектов одного класса их __vfptrs будет иметь одинаковые значения.

Чтобы различать разные COM-объекты, извлекайте и сравнивайте их IUnknown указатели интерфейса. Это называется идентификацией объекта.

Спасибо, я понял, что адреса функций в IUnknown одинаковы и должны быть такими.

Но все же не недооценивайте поведение AddRef/Release. Когда я вхожу в режим отладки в ExposePointer(), я вижу, что второй последовательный вызов не принесет refCount к 3. Это вернет его к 2.

Но если я вызову ForgetExposePointer() дважды, вместо этого он будет равен 3.

Почему возврат указателя отправки через Variant* Result или забвение возврата такого значения дает мне другой результат? Мое понимание того, что между Call 1 и Call 2 происходит скрытый вызов Release()...

STDMETHODIMP CMyContainer::ExposePointer([in]int index, [out, retval] VARIANT* Result)
{
VariantInit(Result);
IDispatch* pDisp = m_map[index].second;
V_VT(Result) = VT_DISPATCH;
V_DISPATCH(Result) = pDisp;
refCount_x = pDisp->AddRef();    // Increment, because we expose
}

STDMETHODIMP CMyContainer::ForgetExposePointer([in]int index, [out, retval] VARIANT* Result)
{
VariantInit(Result);
IDispatch* pDisp = m_map[index].second;
refCount_y = pDisp->AddRef();
}


MyApp::Function1(...)
{
CreateAndSaveDispToMap(...);   // refCount is 1 now
VARIANT var1;
VARIANT var2;
pMyContainer->ExposePointer(index, &var1);  // Call 1
pMyContainer->ExposePointer(index, &var2);  // Call 2
}
MyApp::Function2(...)
{
CreateAndSaveDispToMap(...);   // refCount is 1 now
VARIANT var1;
VARIANT var2;
pMyContainer->ForgetExposePointer(index, &var1);
pMyContainer->ForgetExposePointer(index, &var2);
}
Другие вопросы по тегам