我在一个应用程序中有一个实现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
:
(IMyInterface *)this
设置为接口指针,如预期的那样所以我的困惑是为什么调用CoCreateInstance给我留下一个NULL指针并返回E_NOINTERFACE代码,当COM服务器应用程序显然返回我要求的界面时?
编辑:我的客户端应用程序在启动时调用CoInitialize(NULL),这没什么区别。
答案 0 :(得分:5)
这是因为COM子系统试图封送您的自定义界面(IMyInterface)并且根本不知道如何做到这一点。发生这种情况要么是因为服务器是out-proc,要么是因为服务器处于进程中,并且调用CoCreateInstance()的消费者应用程序的线程错误地调用了CoInitialize()/ CoInitializeEx(),因此请求提供“多线程单元”在文章user Thomas中引用了另一个答案。
如果您只需要进程内服务器,则可以通过确保调用CoCreateInstance()的线程使用COINIT_APARTMENTTHREADED调用CoInitialize()或CoInitializeEx()来强制执行“单线程单元”来抑制编组。
如果你需要一个out-proc服务器,你就无法绕过编组。在后一种情况下,您可以执行以下操作之一:
答案 1 :(得分:5)
如果您的COM服务器在不同的进程中运行,或者在同一进程中运行不同的公寓,则COM需要知道在调用接口时如何打包和传输参数。这个过程称为“编组”。
如果您定义了自定义界面,则需要使用以下方法之一为其实现封送处理。
当您调试COM服务器时,尽管您在调用QueryInterface时看到您正在返回自定义接口,但它并没有跨越进程边界,因为COM无法弄清楚如何编组该接口,因此客户端看到E_NOINTERFACE。
更新(根据您的评论)
如果这是一个现有的COM服务器应用程序,那么您可能已经有了一个代理/存根。您需要在客户端和服务器上注册它。难道你是在新机器上测试它而你忘了注册吗?要注册,只需在proxy / stub dll上执行regsvr32。
答案 2 :(得分:2)
编辑回复评论:
如果您的线程模型与您正在创建的对象的线程模型不兼容,那么COM编组就会启动。如果编组内容不存在,则出现的错误是E_NOINTERFACE,因为编组接口是丢失。
它更多的是关于线程模型而不是关于编组,真的。
答案 3 :(得分:2)
之前关于E_NOINTERFACE
的评论因为缺少编组界面而返回非常有帮助,
然而,
对我们来说,答案/解决方法是强制主应用程序(调用CoCreateInstance
的那个)为STA
(单线程单元),这是通过设置高级链接器选项来完成的,即:
“CLR线程属性”设置为“STA线程属性”
或在链接命令行中执行:
"/CLRTHREADATTRIBUTE:STA"
这可以防止混合使用MTA和STA,从而导致跨线程调用。
希望其他人认为这有帮助。