1

我对通过 IDispatch/IUnknown 指针看到的 __vfptr 感到困惑。我正在创建进程内免费线程 COM obj (IMyContainer)。在这个对象中,我需要保留指向实现相同 IMyInterface 的 com 对象的 2 个不同实例的指针。所以我两次调用 CreateAndSaveDispToMap()。

我的想法是将他们的 IDispatch 指针保存在一些 std::map 中。此时我怀疑每个实例的 refCount 都为 1。事实就是如此。但令人惊讶的是,我发现我通过 pUnk 获得了相同的 __vftbl 以获取 2 个不同的调度指针。

为什么?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);
}
4

2 回答 2

0

每个多态对象都有__vfptr一个指向对象实际类的 vtable 的指针。每个不同的类生成一个 vtable。这就是为什么对于同一类的任何两个对象,它们__vfptr的 s 将具有相同的值。

为了区分不同的 COM 对象,检索并比较它们的IUnknown接口指针。这称为对象标识

于 2011-11-11T06:41:31.980 回答
0

谢谢,我发现 IUnknown 上的函数地址是相同的,并且必须如此。

但是还是不要理解 AddRef/Release 的行为。当我在 ExposePointer() 中进入调试模式时,我看到第二个后续调用不会将 refCount 变为 3。它会将其带回 2。

但是,如果我调用 ForgetExposePointer() 两次,它会将其变为 3。

为什么通过 Variant* Result 返回调度指针或忘记返回这样的值会给我不同的结果?我的理解是在呼叫 1 和呼叫 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);
}
于 2011-11-11T17:02:10.260 回答