为什么QueryInterface()会失败,当接口确实在Windows中实现并具有内置编组器时

Why would QueryInterface() fail when the interface is surely implemented and has built-in marshaller in Windows?

本文关键字:实现 内置 Windows QueryInterface 失败 接口 为什么      更新时间:2023-10-16

我有以下设置。有一个COM服务器安装在COM+中(在一个单独的进程中运行),并具有以下接口定义:

[object, uuid("InterfaceIdHere"), nonextensible, oleautomation, hidden]
interface IMyInterface : IUnknown {
   HRESULT MyMethod( [in] IUnknown* param );
};

调用者这样调用它:

HRESULT callComObject(IStream* stream)
{
    return comObject->MyMethod(stream);
}

注意,这里IStream*被隐式地提升为IUnknown*。这样做是因为在IDL中声明IStream*类型的参数引起了一些问题,我现在无法回忆起来。无论如何,它总是一个有效的IStream*传递在IUnknown*的位置。

COM服务器端有MyMethod()的实现:

STDMETHODIMP CServer::MyMethod(IUnknown* param)
{
    if(param == 0) {
       return E_INVALIDARG;
    }   
    ATL::CComQIPtr<IStream> stream(param);
    if(stream == 0) {
       return E_INVALIDARG;// control passes HERE
    }
    // whatever
}

因此,我在客户端将IStream*传递到callComObject(), CC_8隐式地上传到IUnknown*,后者传递给COM编组器。被封送的IUnknown*到达另一个进程的服务器,在那里获得IUnknown*,然后有一个QueryInterface()调用从同一个对象封送IStream*, QueryInterface()失败。

这看起来很疯狂,因为编组IStream*应该在任何时候都能工作——Windows中已经预先安装了这个接口的编组器。

为什么它不可能工作,我如何找到原因?

符合该行为的一种可能情况如下:

  • 在调用者和被调用者之间没有任何封送
  • 接口指针是有效的
  • 然而,实现IStream的对象没有相应的COM_INTERFACE_ENTRY映射条目,也没有使接口可用,调用者可能通过非com方式获得指针,例如直接c++强制转换

这很容易通过在调用之前在调用方端检查流来检查。

在这种情况下,被调用者可以将reinterpret_cast转换为IStream,并使其正常工作。

您可以让IDL导入objidl。或者在您自己的idl中手动定义IStream(及其祖先)(只是要确保使用标准的IID值)。然后你可以使用IStream而不是IUnknown作为你的参数类型,不用再担心QueryInterface()了。

你可以用IDispatch代替IUnknown作为你的方法的参数类型。在我看来,服务器实际上有一个存根而不是流本身,因为它在另一个进程中。

这只是一个猜测

希望对大家有所帮助