转换COM接口

Spa*_*les 6 c++ com casting

我今天在我的代码中遇到了一个问题,即通过将我的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

我只是使用CComPtrCComQIPtr管理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().


joh*_*ohn 2

我认为问题在于,因为从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 对象,然后将其向下转换为您的实际类型。