即使找到接口,CoCreateInstance也会返回E_NOINTERFACE

Mr.*_*Boy 8 c++ windows com com-interop visual-c++

我有一个COM类在一个应用程序中CMyCOMServer实现IMyInterface,两个都具有正确的GUID.CMyCOMServer::QueryInterface如果请求IUnknown或IMyInterface,则返回S_OK(并将其自身转换为正确的类型),否则返回E_NOINTERFACE.

在同一台PC上的另一个应用程序中,我打电话给:

HRESULT hr = ::CoCreateInstance(__uuidof(CMyCOMServer), 0, CLSCTX_SERVER,
 __uuidof(IMyInterface ),(void **)&pInterface);
Run Code Online (Sandbox Code Playgroud)

它返回E_NOINTERFACE.所以我认为我做错了什么并添加了一个断点CMyCOMServer::QueryInterface.我发现在CoCreateInstance调用时,QueryInterface会针对不同的接口多次触发:

  • 首先,要求IUnknown - 没问题
  • 然后,请求几个接口,如IMarshall等...这些不受支持,因此返回E_NOINTERFACE
  • 最后,请求IMyInterface.我验证QueryInterface返回S_OK并按(IMyInterface *)this预期设置为接口指针

所以我的困惑是为什么调用CoCreateInstance给我留下一个NULL指针并返回E_NOINTERFACE代码,当COM服务器应用程序显然返回我要求的接口?

编辑:我的客户端应用程序在启动时调用CoInitialize(NULL),这没有任何区别.

DSO*_*DSO 6

如果您的COM服务器在不同的进程中运行,或者在同一进程中运行不同的公寓,则COM需要知道在调用接口时如何打包和传输参数.这个过程称为"编组".

如果定义自定义接口,则需要使用以下方法之一为其实现封送处理.

  • 标准编组:使用MIDL编译器生成必须在系统上注册的代理和存根.这可能是您已定义界面的最佳选择.
  • OLE自动化编组:您定义自动化兼容的自定义接口并使用已经是COM框架一部分的编组器
  • 自定义编组:你实现了IMarshal的方法

在调试COM服务器时,尽管您在调用QueryInterface时看到要返回自定义接口,但它并没有跨越进程边界,因为COM无法弄清楚如何编组该接口,因此客户端看到了E_NOINTERFACE.

更新(根据您的评论)

如果这是一个现有的COM服务器应用程序,那么您可能已经有了一个代理/存根.您需要在客户端和服务器上注册它.难道你是在新机器上测试它而你忘了注册吗?要注册,只需在proxy/stub dll上执行regsvr32.


sha*_*oth 5

这是因为COM子系统试图封送您的自定义接口(IMyInterface)并且根本不知道如何做到这一点.发生这种情况要么是因为服务器是out-proc,要么是因为服务器处于进程中,并且调用CoCreateInstance()的消费者应用程序的线程错误地调用了CoInitialize()/ CoInitializeEx(),因此请求提供"多线程单元" 用户托马斯在另一个答案中引用的文章中.

如果您只需要进程内服务器,则可以通过确保调用CoCreateInstance()的线程使用COINIT_APARTMENTTHREADED调用CoInitialize()或CoInitializeEx()来强制执行"单线程单元"来抑制编组.

如果你需要一个out-proc服务器,你就无法绕过编组.在后一种情况下,您可以执行以下操作之一:

  • 实施IMarshal - 最不可取的
  • 添加代理/存根并将其注册为自定义界面
  • (不确定它是否适用于out-proc,但它是最简单的)如果您的接口可以使用自动化编组器编组,只需在您的COM服务器的资源中包含一个typlib并在注册表中注册该类型库.