c++ 转换COM接口

1qczuiv0  于 2022-12-05  发布在  其他
关注(0)|答案(3)|浏览(167)

我今天在代码中遇到了一个问题,通过将我的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传递给MyClass::GetInternalObject(IUnknown** lpUnknown),然后查询所需接口的返回值。
我的问题是,对于何时可以在COM对象上使用C/C
强制转换,是否有规则?除了多重继承和它们带来的调整器thunk之外,强制转换COM对象如何会导致访问冲突之类的意外情况?请详细说明。

**编辑:**它们都是正确操作的好例子,但我希望得到的是一个技术解释,说明为什么不应该强制转换COM对象(假设存在一个),例如,在 x 情况下强制转换将返回pMyObj 2 -4,但QueryInterface将返回pMyObj 2 -8,因为 y......或者强制转换COM对象只是一个不好的做法/风格问题?

短暂性脑缺血

kuhbmx9i

kuhbmx9i1#

我只会使用CComPtrCComQIPtr来管理COM接口,而不是使用C风格的强制转换来编写代码,在我看来,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();
        }
    }
}

此外,我不是COMMaven,但我对您的代码表示怀疑:

void MyClass::GetInternalObject(IUnknown** lpUnknown)
{
    pInternalObject->QueryInterface(IID_IMyInterface2, (void**)lpUnknown);
}

如果你的方法返回一个IUnknown*,那么我会把QueryInterface()IID_IUnknown存储在一个IMyInterface2*中,而不是一个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() .

v8wbuo2f

v8wbuo2f2#

我认为问题在于,因为从IMyInterface*IUnknown*的转换是可以的(在COM中,一切都继承自IUknown,对吗?),所以您认为从IMyInterface**IUnknown**的转换也是可以的。但在C++中这是不正确的,我怀疑在COM中也是如此。
对我来说,下面看起来更合乎逻辑,如果这不是严格正确的道歉,我的COM是非常生 rust ,但希望你得到的想法。

CComPtr<IUnknown> pMyObj2;
HRESULT hRes = pMyObj->GetInternalObject(&pMyObj2);

if (SUCCEEDED(hRes))
{
    CComPtr<IMyInterface> pMyObj3 = (IMyInterface*)pMyObj2;
    pMyObj3->Function();
}

也就是说,首先获取一个IUnknown对象,然后将其向下转换为实际类型。

vxqlmq5t

vxqlmq5t3#

我没有看到任何问题,在您的代码片段,堆栈腐败也许有它的原因,但它的其他地方。
我不认为这是你的实际代码,因为GetInternalObject应该是HRESULT类型,而你的不是,所以你在复制/粘贴过程中丢失了一些东西。
为了更安全,避免直接调用QueryInterface,因为与强制转换一起使用时,它们可能会误解接口。但与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*

相关问题