从COM库传递对COM接口的引用

时间:2011-03-13 00:49:29

标签: com

如何从COM库中将引用作为参数传递给COM接口?

以下是样本:

1)客户端代码成功创建了coclass并在pFunctionDiscovery中接收接口指针,如下所示:

hr = CoCreateInstance(
        __uuidof(FunctionDiscovery),
        NULL,
        CLSCTX_INPROC_SERVER,
        __uuidof(IFunctionDiscovery),
        (LPVOID*)&pFunctionDiscovery );

if (FAILED(hr))
{
    TRACE_MESSAGE(Error,"Failed to get IFunctionDiscovery COM %08x\n",hr);
    goto Exit;
}

2)现在调用pFunctionDiscovery的成员函数如下所示给出了错误消息:800706f4,它对应于一个空引用指针被传递给存根。

hr = pFunctionDiscovery->GetInstanceCollection(
        FCTN_CATEGORY_DEVICEDISPLAYOBJECTS,
        NULL,
        FALSE,
        &pFICollection );

3)COM库是在ATL库的帮助下编写的,代码如下:

// The module attribute is specified in order to implement DllMain,
// DllRegisterServer and DllUnregisterServer
[ module(dll, name = "MyServer", helpstring = "MyServer 1.0 Type Library") ];
[ emitidl ];

/////////////////////////////////////////////////////////////////////////////

// IFunctionInstanceCollection interface
[
   object,
   uuid("F0A3D895-855C-42A2-948D-2F97D450ECB1"),
   oleautomation,
   helpstring("IFunctionInstanceCollection Interface"),
   pointer_default(unique)
]
__interface IFunctionInstanceCollection : IUnknown
{
public:
    virtual HRESULT STDMETHODCALLTYPE GetCount(__RPC__out DWORD *pdwCount) = 0;
};


// IFunctionDiscovery interface
[
   object,
   uuid("4df99b70-e148-4432-b004-4c9eeb535a5e"),\
   oleautomation,
   helpstring("IFunctionDiscovery Interface"),
   pointer_default(unique)
]
__interface IFunctionDiscovery : IUnknown
{
   virtual HRESULT GetInstanceCollection(
       __RPC__in_string const WCHAR* functionCategory,
       __RPC__in_opt_string const WCHAR* subcategory,
       BOOL fIncludeAllSubCategories,
       __RPC__deref_out_opt IFunctionInstanceCollection **ppIFunctionInstanceCollection
       ) = 0;
};


/////////////////////////////////////////////////////////////////////////////
// FunctionDiscovery class
[
   coclass,
   threading(apartment),
   vi_progid("FunctionDiscovery.Discovery"),
   progid("FunctionDiscovery.Discovery.1"),
   version(1.0),
   uuid("C72BE2EC-8E90-452c-B29A-AB8FF1C071FC"),
   helpstring("FunctionDiscovery Class")
]
class ATL_NO_VTABLE FunctionDiscovery : 
   public IFunctionDiscovery
{
public:
    FunctionDiscovery() {};
   virtual ~FunctionDiscovery(){};

   virtual HRESULT GetInstanceCollection(
       __RPC__in_string const WCHAR* functionCategory,
       __RPC__in_opt_string const WCHAR* subcategory,
       BOOL fIncludeAllSubCategories,
       __RPC__deref_out_opt IFunctionInstanceCollection **ppIFunctionInstanceCollection
       )
   {
       printf("GetInstanceCollection called");
       return 0;
   }
   DECLARE_PROTECT_FINAL_CONSTRUCT()
   HRESULT FinalConstruct()
   {
      return S_OK;
   }

   void FinalRelease() 
   {
   }

   static BOOL DllMainAttach();
   static void DllMainDetach();
};

请告诉我问题在哪里?

由于 尼克

好的,在客户端的调用中传递非空字符串,工作正常。我将对象分配给* ppIFunctionInstanceCollection的方式如下:

在服务器端,我声明了一个这样的新类,并从GetInstanceCollection中创建一个对象。当客户端调用GetInstanceCOllection时,将返回此创建的对象。我确实在服务器端获得了一个有效的实例,但在客户端,它显示为NULL。

1)

class CFunctionInstanceCollection : public IFunctionInstanceCollection  
{  
public:  
     HRESULT STDMETHODCALLTYPE QueryInterface(REFIID IID, void **pv) throw()  
     {  return 0;  };  
     ULONG STDMETHODCALLTYPE AddRef(void) throw()  
     {  return 0;  };  
     ULONG STDMETHODCALLTYPE Release(void) throw()  
     { return 0; };  
     virtual HRESULT STDMETHODCALLTYPE GetCount(__RPC__out DWORD *pdwCount)  
     { return 10; };  
};

2)在GetInstanceCollection中,我正在做:

*ppIFunctionInstanceCollection = new CFunctionInstanceCollection();

我希望上面的赋值有效的CFunctionInstanceCollection实例进入GetInstanceCollection()方法的最后一个参数,即* ppIFunctionInstanceCollection。我已在服务器端验证了这一点并打印了有效指针,类的大小为4(虚函数的存在使类大小为4)。

但在客户端,值为NULL。我认为客户端/服务器之间的参数传递更多。如果你还有别的什么,请告诉我。感谢!!!

2 个答案:

答案 0 :(得分:1)

这个blog post解释了为什么你遇到这个问题,pointer_default(unique)不能做你想象的那样。使用[unique]将子类别参数归类。

答案 1 :(得分:1)

如果我告诉你已经知道的事情,请道歉。

在RPC(和COM)中,proxy是客户端在调用远程过程时实际调用的代码段。代理通常编组输入参数,然后将请求发送到服务器,其中一段代码stub解组参数,然后使用它们来调用被调用的实际过程。

当被调用的过程返回结果时,存根调整out参数和结果,并将响应发送回代理,代理又解组out参数等,并将它们交还给客户端。

无论如何,这是一般模型,事情有时被优化掉(例如,在进程COM对象的情况下),在这种情况下可能没有实际的存根和实际的代理。不过,这是我们可以用来理解“代理”和“存根”是什么的背景。

“将空引用指针传递给存根”错误表明问题发生在存根(即服务器)端。可能将事物传递给存根的两段代码是代理,GetInstanceCollection的实现更可能是罪魁祸首。

我怀疑你的问题是GetInstanceCollection实现没有为* ppIFunctionInstanceCollection赋值。

尝试在GetInstanceCollection返回之前添加代码以指定* ppIFunctionInstanceCollection。


更新3/15

您更新的GetCount实现返回值10.但是,这将被解释为HRESULT 10而不是计数值10. GetCount的实现应该看起来像这样......

virtual HRESULT STDMETHODCALLTYPE GetCount(__RPC__out DWORD *pdwCount)
{ 
    *pdwCount = 10;
    return S_OK;
};

也就是说,假设IUnknown方法(QueryInterface,AddRef和Release)并不是一个好主意,因为你可以意外地破坏各种各样的东西。例如,每次调用GetInstanceCollection时,程序都会泄漏一个CFunctionInstanceCollection实例,因为一个实例被创建并且永远不会被销毁。

您拥有的代码可以用于实验,但最好使用ATL为您的FunctionDiscovery类执行Iuncnknown for CFunctionInstanceCollection的完整实现。


更新3/16

为了完整起见,我可能还应该提到,以你的方式分配* ppIFunctionInstanceCollection是有效的,但一般来说可能存在风险。

您已经编写了CFunctionInstanceCollection类,因此您知道它直接实现了IFunctionInstanceCollection接口,因此您知道您的分配是安全的。但是在更常见的情况下,你没有编写类,CFunctionInstanceCollection类可能会做一些不太直接的事情 - 例如它可能聚合一些其他实现接口的类。要真正安全,您应该使用QueryInterface来检索IFunctionInstanceCollection接口指针。