我今天在我的代码中遇到了一个问题,即通过将我的COM对象转换为IUnknown**来导致访问冲突,AFAICT.传递给它的函数没有问题但是当调用我的一个对象的函数时,它会执行一些随机函数并破坏堆栈然后死掉.
指示性代码(只是忽略它为什么这样做 - 我知道它很糟糕,我知道如何修复它但这是一个问题,为什么会出现这样的问题):
void MyClass2::func(IMyInterface* pMyObj)
{
CComPtr<IMyInterface2> pMyObj2;
HRESULT hRes = pMyObj->GetInternalObject((IUnknown**)&pMyObj2);
if (SUCCEEDED(hRes))
pMyObj2->Function(); // corrupt stack
}
void MyClass::GetInternalObject(IUnknown** lpUnknown)
{
pInternalObject->QueryInterface(IID_IMyInterface2, (void**)lpUnknown);
}
Run Code Online (Sandbox Code Playgroud)
我一直有点怀疑在COM对象上使用C/C++强制转换但我从未遇到(可能通过未定义的行为)直到现在的任何问题.
我快速浏览了一下,从我可以告诉我的转换到IUnknown在技术上是有效的,只要在继承链中没有多重干扰,但它不被认为是最佳实践 - 我应该将IUnknown传递给MyClass::GetInternalObject(IUnknown** lpUnknown)然后查询返回我想要的接口的价值.
我的问题是,是否存在关于何时可以在COM对象上使用C/C++强制转换的规则,除了多重继承和它们带来的调整器thunks之外,如何转换COM对象会导致诸如访问冲突之类的惊喜?请详细说明.
编辑:他们都是应该如何正确完成的好例子,但我希望的是技术解释为什么你不应该强制转换COM对象(假设存在一个),例如,在情境x中,施法会返回pMyObj2-4 但是QueryInterface将返回pMyObj2-8因为y ...或者仅仅是一个糟糕的练习/风格的COM对象?
TIA
Mr.*_*C64 11
我只是使用CComPtr和CComQIPtr管理COM接口,而不是使用C风格的转换编写代码,在COM的上下文中我似乎不合适:
void MyClass2::Func(IMyInterface* pMyObj)
{
// Assuming:
// HRESULT IMyInterface::GetInternalObject( /* [out] */ IUnknown** )
CComPtr<IUnknown> spUnk;
HRESULT hr = pMyObj->GetInternalObject(&spUnk);
if (SUCCEEDED(hr))
{
// Get IMyInterface2 via proper QueryInterface() call.
CComQIPtr<IMyInterface2> spMyObj2( spUnk );
if ( spMyObj2 )
{
// QueryInterface() succeeded
spMyObj2->Function();
}
}
}
Run Code Online (Sandbox Code Playgroud)
此外,我不是COM专家,但我怀疑你的代码:
void MyClass::GetInternalObject(IUnknown** lpUnknown)
{
pInternalObject->QueryInterface(IID_IMyInterface2, (void**)lpUnknown);
}
Run Code Online (Sandbox Code Playgroud)
如果你是QueryInterface()作"ing IID_MyInterface2,你应该存储在一个IMyInterface2*,而不是一个IUnknown*.如果你的方法返回一个IUnknown*,那么我QueryInterface()的IID_IUnknown:
// NOTE on naming convention: your "lpUnknown" is confusing.
// Since it's a double indirection pointer, you may want to use "ppUnknown".
//
void MyClass::GetInternalObject(IUnknown** ppUnknown)
{
pInternalObject->QueryInterface(IID_IUnknown, (void**)ppUnknown);
}
Run Code Online (Sandbox Code Playgroud)
或者更好地使用IID_PPV_ARGS宏:
void MyClass::GetInternalObject(IUnknown** ppUnknown)
{
IUnknown* pUnk = NULL;
HRESULT hr = pInternalObject->QueryInterface(IID_PPV_ARGS(&pUnk));
// Check hr...
// Write output parameter
*ppUnknown = pUnk;
}
Run Code Online (Sandbox Code Playgroud)
COM样式转换具有特定名称:QueryInterface().
我认为问题在于,因为从IMyInterface*to进行强制转换IUnknown*是可以的(在 COM 中,一切都继承自对吧?),所以您认为从toIUknown进行强制转换也可以。但在 C++ 中情况并非如此,我也怀疑在 COM 中情况是否如此。IMyInterface**IUnknown**
对我来说,以下内容看起来更合乎逻辑,如果这不完全正确,我深表歉意,我的 COM 非常生锈,但希望您能明白这一点。
CComPtr<IUnknown> pMyObj2;
HRESULT hRes = pMyObj->GetInternalObject(&pMyObj2);
if (SUCCEEDED(hRes))
{
CComPtr<IMyInterface> pMyObj3 = (IMyInterface*)pMyObj2;
pMyObj3->Function();
}
Run Code Online (Sandbox Code Playgroud)
即首先获取一个 IUnknown 对象,然后将其向下转换为您的实际类型。