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

时间:2009-11-23 09:06:19

标签: c++ windows visual-c++ com com-interop

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

在同一台电脑上的另一款应用中,我打电话给:

HRESULT hr = ::CoCreateInstance(__uuidof(CMyCOMServer), 0, CLSCTX_SERVER,
 __uuidof(IMyInterface ),(void **)&pInterface);

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

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

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

编辑:我的客户端应用程序在启动时调用CoInitialize(NULL),这没什么区别。

4 个答案:

答案 0 :(得分:5)

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

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

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

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

答案 1 :(得分:5)

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

如果您定义了自定义界面,则需要使用以下方法之一为其实现封送处理。

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

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

更新(根据您的评论)

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

答案 2 :(得分:2)

这可能是Raymond Chen wrote about

的线程模型问题

编辑回复评论:

  

如果您的线程模型与您正在创建的对象的线程模型不兼容,那么COM编组就会启动。如果编组内容不存在,则出现的错误是E_NOINTERFACE,因为编组接口是丢失。

它更多的是关于线程模型而不是关于编组,真的。

答案 3 :(得分:2)

之前关于E_NOINTERFACE的评论因为缺少编组界面而返回非常有帮助, 然而, 对我们来说,答案/解决方法是强制主应用程序(调用CoCreateInstance的那个)为STA(单线程单元),这是通过设置高级链接器选项来完成的,即:

  

“CLR线程属性”设置为“STA线程属性”

或在链接命令行中执行:

"/CLRTHREADATTRIBUTE:STA"

这可以防止混合使用MTA和STA,从而导致跨线程调用。

希望其他人认为这有帮助。