我今天在我的代码中遇到了一个问题,即通过将我的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);
}
我一直有点怀疑在COM对象上使用C / C ++强制转换但我从未遇到(可能是通过未定义的行为)直到现在的任何问题。
我快速浏览一下,从我可以告诉我的转换到IUnknown在技术上是有效的,只要在继承链中没有多重干扰,但它不被认为是最佳实践 - 我应该将IUnknown传递给{{ 1}}然后查询我想要的接口的返回值。
我的问题是,是否存在关于何时可以在COM对象上使用C / C ++强制转换的规则,除了多重继承和它们带来的调整器thunk之外,如何强制转换COM对象会导致访问冲突等意外?请详细说明。
编辑:它们都是如何正确完成它们的好例子,但我希望的是技术解释为什么你不应该投射COM对象(假设一个存在),例如:在 x 的情况下,转换将返回pMyObj2-4,但是由于 y ,QueryInterface将返回pMyObj2-8 ...或者仅仅是一个糟糕的练习/风格来转换COM对象?
TIA
答案 0 :(得分: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();
}
}
}
此外,我不是COM专家,但我怀疑你的代码:
void MyClass::GetInternalObject(IUnknown** lpUnknown)
{
pInternalObject->QueryInterface(IID_IMyInterface2, (void**)lpUnknown);
}
如果您QueryInterface()
正在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);
}
或更好地使用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;
}
COM样式转换具有特定名称:QueryInterface()
。
答案 1 :(得分:2)
我认为问题在于,因为从IMyInterface*
到IUnknown*
的强制转换是正常的(在COM中,所有内容都是从IUknown
继承的吗?)你认为来自{{1}的强制转换} IMyInterface**
也行。但是在C ++中并非如此,我怀疑它在COM中也是如此。
对我来说,以下看起来更合乎逻辑,如果这不是严格正确的道歉,我的COM非常生疏,但希望你能得到这个想法。
IUnknown**
即。首先获取一个IUnknown对象,然后将其转换为您的实际类型。
答案 2 :(得分:0)
我没有看到你的代码片段中的任何问题,堆栈损坏可能有其原因,但它在其他地方。
我认为这不是您的实际代码,因为GetInternalObject
应为HRESULT
类型,而您的类型不是,因此您在复制/粘贴期间丢失了一些内容。
为了保持更安全,请避免直接QueryInterface
调用,因为与强制转换一起可能会误解接口。尽管如此,来往IUnknown*
的投射可能是不可避免的。如果不能信任被调用者返回到IUnknown的适当接口,那么在调用者方面你可能更愿意再次进行QI以确保你拥有你感兴趣的界面。
如果GetInternalObject本身就是一个COM接口方法,你可以这样:
void MyClass2::func(IMyInterface* pMyObj)
{
CComPtr<IUnknown> pMyObj2Unknown;
pMyObj->GetInternalObject((IUnknown**)&pMyObj2Unknown);
CComQIPtr<IMyInterface2> pMyObj2 = pMyObj2Unknown; // This is only needed if callee is not trusted to return you a correct pointer
if (pMyObj2)
pMyObj2->Function(); // corrupt stack
}
STDMETHODIMP MyClass::GetInternalObject(IUnknown** lpUnknown) // COM method is typically both HRESULT and __stdcall
{
CComQIPtr<IMyInterface2> pMyInterface2 = pInternalObject;
if(!pMyInterface2)
return E_NOINTERFACE;
*lpUnknown = pMyInterface2.Detach(); // *lpUnknown will have to me IMyInterface2 this way
return S_OK;
}
PS如果GetInternalObject是本机方法而不是COM,则根本不会转换为IUnknown*
。