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