转换COM接口

时间:2012-11-15 10:20:55

标签: 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);
}

我一直有点怀疑在COM对象上使用C / C ++强制转换但我从未遇到(可能是通过未定义的行为)直到现在的任何问题。

我快速浏览一下,从我可以告诉我的转换到IUnknown在技术上是有效的,只要在继承链中没有多重干扰,但它不被认为是最佳实践 - 我应该将IUnknown传递给{{ 1}}然后查询我想要的接口的返回值。

我的问题是,是否存在关于何时可以在COM对象上使用C / C ++强制转换的规则,除了多重继承和它们带来的调整器thunk之外,如何强制转换COM对象会导致访问冲突等意外?请详细说明。

编辑:它们都是如何正确完成它们的好例子,但我希望的是技术解释为什么你不应该投射COM对象(假设一个存在),例如:在 x 的情况下,转换将返回pMyObj2-4,但是由于 y ,QueryInterface将返回pMyObj2-8 ...或者仅仅是一个糟糕的练习/风格来转换COM对象?

TIA

3 个答案:

答案 0 :(得分: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();
        }
    }
}

此外,我不是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*