即使find接口,CoCreateInstance也会返回E_NOINTERFACE

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

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

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

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

  • 首先,请求IUnknown – 没问题
  • 然后,像IMarshall等几个接口请求…这些不支持,所以E_NOINTERFACE返回
  • 最后,请求IMyInterface。 我validationQueryInterface返回S_OK并设置(IMyInterface *)this作为接口指针,如预期

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

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

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

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

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

当你正在调试你的COM服务器时,尽管你看到你在调用QueryInterface时返回了你的自定义接口,但是它并没有跨过进程边界,因为COM不知道如何编组这个接口,因此客户端看到了E_NOINTERFACE。

更新(根据您的评论)

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

发生这种情况是因为COM子系统试图编组您的自定义接口(IMyInterface),而根本不知道如何做到这一点。 发生这种情况是因为服务器是out-proc或因为服务器在进程中,并且调用CoCreateInstance()的客户应用程序的线程错误地调用了CoInitialize()/ CoInitializeEx(),所以需要提到“多线程的公寓”在用户托马斯在另一个答案中提到的文章。

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

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

  • 实现IMarshal – 最不可取的
  • 添加代理/存根并将其注册为您的自定义界面
  • (不知道它是否可以用于out-proc,但最简单的方法是)如果你的界面可以用自动编码器编组,只需在你的COM服务器的资源中包含一个typlib,并在注册表中注册该typelib。

难道这就是Raymond Chen写的关于线程模型的问题吗?

编辑回复评论:

如果您的线程模型与您正在创建的对象的线程模型不兼容,那么COM编组就会开始。如果编组的东西不在那里,那么出现的错误是E_NOINTERFACE,因为编组接口丢失了。

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

关于E_NOINTERFACE的以前的评论返回,因为编组接口丢失是非常有用的,但是,对于我们来说,答案是强制主应用程序(调用CoCreateInstance )为STA (单线程的公寓),这是通过设置高级链接器选项,即:

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

或者在链接命令行上执行:

 "/CLRTHREADATTRIBUTE:STA" 

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

希望别人认为这有帮助。