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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1781906/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 13:28:56  来源:igfitidea点击:

CoCreateInstance returning E_NOINTERFACE even though interface is found

c++windowsvisual-c++comcom-interop

提问by Mr. Boy

I have a COM class CMyCOMServerimplementing IMyInterfacein one application, both with correct GUIDs. CMyCOMServer::QueryInterfacewill return S_OK (and cast itself to the right type) if IUnknown or IMyInterface is requested, otherwise it returns E_NOINTERFACE.

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

In another app on the same PC, I call:

在同一台 PC 上的另一个应用程序中,我调用:

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

It returns E_NOINTERFACE. So I assumed I was doing something wrong and added a breakpoint on CMyCOMServer::QueryInterface. I found that when CoCreateInstanceis called, QueryInterfaceis triggered several times for different interfaces:

它返回 E_NOINTERFACE。所以我认为我做错了什么,并在CMyCOMServer::QueryInterface. 我发现当CoCreateInstance被调用时,QueryInterface对于不同的接口会触发多次:

  • First, IUnknown is requested - no problem
  • Then, several interfaces like IMarshall etc are requested... these are not supported so E_NOINTERFACE is returned
  • Finally, IMyInterface is requested. I verify QueryInterface returns S_OK and sets (IMyInterface *)thisas the interface pointer, as expected
  • 首先,请求 IUnknown - 没问题
  • 然后,请求多个接口,如 IMarshall 等......这些不支持,因此返回 E_NOINTERFACE
  • 最后,请求 IMyInterface。我验证 QueryInterface 返回 S_OK 并按(IMyInterface *)this预期设置为接口指针

So my confusion is why the calling CoCreateInstance is leaving me a NULL pointer and return code of E_NOINTERFACE, when the COM server app is clearly returning the interface I ask for?

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

EDIT: my client app calls CoInitialize(NULL) at startup, this makes no difference.

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

采纳答案by DSO

If your COM server is running in a different process, or a different apartment in the same process, COM needs to know how to package and transmit parameters when you make calls to your interface. This process is called "marshaling".

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

If you define a custom interface, you need to implement marshaling for it using one of the following approaches.

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

  • Standard marshaling: have the MIDL compiler to generate a proxy and stub which you must register on the system. This is probably the best option since you have already defined your interface.
  • OLE Automation marshaling: you define an automation compatible custom interface and use the marshaller which is already part of the COM framework
  • Custom marshaling: you implement the methods of IMarshal
  • 标准封送处理:让 MIDL 编译器生成必须在系统上注册的代理和存根。这可能是最好的选择,因为您已经定义了接口。
  • OLE 自动化封送处理:您定义自动化兼容的自定义接口并使用已经是 COM 框架一部分的封送处理程序
  • 自定义封送处理:您实现 IMarshal 的方法

When you are debugging your COM server, although you see that you are returning your custom interface in the call to QueryInterface, it does not make it across the process boundary because COM cannot figure out how to marshal that interface, hence the client sees E_NOINTERFACE.

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

UPDATE (based on your comment)

更新(根据您的评论)

If this is an existing COM server app then you probably already have a proxy/stub. You need to register this on both the client and server. Could it be that you were testing this on a new machine(s) and you simply forgot to register this? To register you simply do regsvr32 on the proxy/stub dll.

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

回答by sharptooth

This happens because COM subsystem tries to marshal your custom interface (IMyInterface) and simply has no idea how to do that. That happens either because the server is out-proc or because the server is in-proc and the thread of the consumer application that calls CoCreateInstance() has called CoInitialize()/ CoInitializeEx() incorrectly so that "multithreaded apartment" is requested as mentioned in the article user Thomasrefers to in the other answer.

发生这种情况是因为 COM 子系统试图编组您的自定义接口 (IMyInterface) 并且根本不知道如何执行此操作。发生这种情况的原因要么是服务器在进程外,要么是因为服务器在进程内,并且调用 CoCreateInstance() 的消费者应用程序的线程错误地调用了 CoInitialize()/ CoInitializeEx(),因此如上所述请求了“多线程单元”在用户 Thomas在其他答案中提到的文章中。

If you only need an in-proc server you could suppress marshalling by ensuring that the thread calling CoCreateInstance() either calls CoInitialize() or CoInitializeEx() with COINIT_APARTMENTTHREADED to enforce "single-threaded apartment".

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

If you need an out-proc server you can't get around marshalling. In the latter case you could do one of the following:

如果您需要一个进程外服务器,您将无法绕过编组。在后一种情况下,您可以执行以下操作之一:

  • implement IMarshal - least preferable
  • add proxy/stubs and register them for your custom interface
  • (not sure if it will work for out-proc, but it's the simplest) if your interface can be marshalled with automation marshaller simply include a typlib into the resources of your COM server and register that typelib in the registry.
  • 实施 IMarshal - 最不理想
  • 添加代理/存根并将它们注册到您的自定义界面
  • (不确定它是否适用于out-proc,但它是最简单的)如果您的接口可以用自动化编组器编组,只需将一个typlib 包含到您的COM 服务器的资源中,并在注册表中注册该typelib。

回答by Mike Watry

The previous comments about E_NOINTERFACEreturned because marshalling interface is missing was very helpful, however, for us the answer/fix was to force the main application (the one calling CoCreateInstance) to be STA(single threaded apartment), and this was done by setting an advanced linker option, i.e.:

之前关于E_NOINTERFACE由于缺少编组接口而返回的评论非常有帮助,但是,对我们来说,答案/修复是强制主应用程序(调用者CoCreateInstance)成为STA(单线程单元),这是通过设置高级链接器来完成的选项,即:

"CLR Thread Attribute" is set to "STA threading attribute"

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

or on the link command line you do:

或在链接命令行上执行以下操作:

"/CLRTHREADATTRIBUTE:STA"

This prevents a mix of MTA and STA, which causes a call across threads.

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

Hope someone else finds this helpful.

希望其他人觉得这有帮助。

回答by Thomas

Could this be the threading model problem that Raymond Chen wrote about?

这可能是Raymond Chen写的线程模型问题吗?

Edit in reply to the comment:

编辑回复评论:

If your threading model is incompatible with the threading model of the object you're creating, then COM marshalling kicks in. And if the marshalling stuff isn't there, the error that comes out is E_NOINTERFACE, because the marshalling interface is missing.

如果您的线程模型与您正在创建的对象的线程模型不兼容,则 COM 编组开始。如果编组的东西不存在,则出现的错误是 E_NOINTERFACE,因为缺少编组接口。

It's more about threading models than about marshalling, really.

实际上,更多的是关于线程模型而不是编组。