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

发布于 2024-08-12 07:02:05 字数 923 浏览 4 评论 0原文

我有一个 COM 类 CMyCOMServer 在一个应用程序中实现 IMyInterface,两者都具有正确的 GUID。如果请求 IUnknown 或 IMyInterface,CMyCOMServer::QueryInterface 将返回 S_OK(并将自身转换为正确的类型),否则返回 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。我验证 QueryInterface 返回 S_OK 并将 (IMyInterface *)this 设置为接口指针,如预期

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

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

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

In another app on the same PC, I call:

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 CoCreateInstance is called, QueryInterface is triggered several times for different interfaces:

  • 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 *)this as the interface pointer, as expected

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?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

我做我的改变 2024-08-19 07:02:05

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

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

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

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

更新(根据您的评论)

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

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".

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

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.

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.

梦里的微风 2024-08-19 07:02:05

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

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

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

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

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 Thomas refers to in the other answer.

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".

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.
蓬勃野心 2024-08-19 07:02:05

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

编辑回复评论:

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

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

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

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.

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

追我者格杀勿论 2024-08-19 07:02:05

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

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

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

"/CLRTHREADATTRIBUTE:STA"

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

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

The previous comments about E_NOINTERFACE returned 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.:

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

or on the link command line you do:

"/CLRTHREADATTRIBUTE:STA"

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

Hope someone else finds this helpful.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文