绝对的 COM 混乱 - 具有早期绑定的 C# 互操作

发布于 2024-08-06 08:58:40 字数 1517 浏览 3 评论 0原文

我一整天都在与 VStudio、Google 和各种其他工具和网站作斗争,但没有找到解决方案 - 帮助!

我有两个 COM 接口(纯 COM,无 ATL):

IMyClassFactory 和 IMyClass 以及相应的实现,

我想从 C# 使用它们,但无需使用 regsvr32 注册 COM 服务器。 我使用 CoRegisterClassObject 公开类工厂,并且可以使用 CoCreateInstance 从非托管代码成功创建 IMyClass 对象。

因此,C# 互操作...

我使用 tlbimp myComServer.tlb 创建了一个 .NET 包装器,并将其加载为对我的 C# 客户端的引用。

然后,当我尝试创建 IMyClass 的实例时,我得到:

An unhandled exception of type 'System.InvalidCastException' occurred in COMTestClient.exe

Additional information: Unable to cast COM object of type 'MyComServerLib.MyClass' to interface type 'MyComServerLib.IMyClass'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{9F8CBFDC-8117-4B9F-9BDC-12D2E6A92A06}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).

现在,我已向 QueryInterface 添加了跟踪,并且返回 E_NOINTERFACE 的唯一情况是它请求任何与 Marshal 相关的接口或 IManagedObject 时。

我该如何解决这个问题?

编辑:我的 IDL 文件:

import "unknwn.idl";

[
    object, 
    uuid(...), 
    nonextensible,
    pointer_default(unique)
]
interface IMyClass : IUnknown
{
    HRESULT(SetFirstNumber)(long nX1);

    HRESULT(SetSecondNumber)(long nX2);

    HRESULT(DoTheAddition)([out,retval] long *pBuffer);
};

[
    uuid(...)
]
library MyLib
{
    importlib("stdole2.tlb");

    [
        uuid(...)
    ]
    coclass IMyClassImpl
    {
        [default] interface IMyClass;
    };
}

I've been battling with VStudio, Google and various other tools and websites all day and found no solution - HELP!!

I have two COM interfaces (pure COM, no ATL):

IMyClassFactory and IMyClass with corresponding implementations

I want to use them from C# BUT without registering the COM server with regsvr32.
I expose the Class Factory with CoRegisterClassObject and I can successfully create objects of IMyClass with CoCreateInstance from unmanaged code.

So the C# interop...

I created a .NET wrapper with tlbimp myComServer.tlb and loaded it as a reference to my C# client.

Then, when I try to create an instance of IMyClass, I get:

An unhandled exception of type 'System.InvalidCastException' occurred in COMTestClient.exe

Additional information: Unable to cast COM object of type 'MyComServerLib.MyClass' to interface type 'MyComServerLib.IMyClass'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{9F8CBFDC-8117-4B9F-9BDC-12D2E6A92A06}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).

Now, I have added traces to the QueryInterface and the only cases when I return E_NOINTERFACE are when it requests for any of Marshal-related interfaces or for IManagedObject.

How do I fix this??

EDIT: My IDL file:

import "unknwn.idl";

[
    object, 
    uuid(...), 
    nonextensible,
    pointer_default(unique)
]
interface IMyClass : IUnknown
{
    HRESULT(SetFirstNumber)(long nX1);

    HRESULT(SetSecondNumber)(long nX2);

    HRESULT(DoTheAddition)([out,retval] long *pBuffer);
};

[
    uuid(...)
]
library MyLib
{
    importlib("stdole2.tlb");

    [
        uuid(...)
    ]
    coclass IMyClassImpl
    {
        [default] interface IMyClass;
    };
}

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

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

发布评论

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

评论(2

此岸叶落 2024-08-13 08:58:40

您需要允许对您的接口进行编组(即,通过在 .idl 文件中将其标记为非“本地”,以便它最终位于类型库和代理/存根中),或者聚合自由线程marshaller 如果你走那条路。

为了聚合 FTM,我做了如下操作:

#define DECLARE_FTM() \
protected: CComPtr<IUnknown> _m_Marshal; \
DECLARE_GET_CONTROLLING_UNKNOWN() \
public: HRESULT FinalConstruct() \
{ return CoCreateFreeThreadedMarshaler(GetControllingUnknown(),&_m_Marshal); }

#define COM_INTERFACE_ENTRY_FTM() COM_INTERFACE_ENTRY_AGGREGATE(IID_IMarshal,_m_Marshal.p)

然后,在您的 COM 映射中:

BEGIN_COM_MAP(Blah)
    COM_INTERFACE_ENTRY(IBlah)
    COM_INTERFACE_ENTRY_FTM()
END_COM_MAP()
DECLARE_FTM()

我注意到您没有使用 ATL 等 - 您需要修改它,以便您的 QueryInterface 实现在查询 IMarshal 时返回 FTM 指针为了。

请注意,聚合 FTM 并不是一件容易就能完成的事情 - 它会做出许多不安全的假设,而这些假设并不总是有效。例如,您的类不能使用任何本身不是自由线程的接口。

另一种选择基本上正如@[Franci Penov]所说,您需要确保您的接口能够被编组。按照我的理解,有一个标准的编组器能够编组类型库中的任何接口,或者您(即 midl 编译器或多或少自动执行此操作)可以创建代理/存根 dll(或合并代码将代理/存根放入您自己的 dll 中),它能够为您编组它。

本文此处介绍了构建和注册代理的过程/stub 更详细。

You need to either allow your interface to be marshalled (i.e., by marking it as not "local" in the .idl file so that it ends up in the type library, and in the proxy/stub), or aggregate the free-threaded marshaller if you go that way.

To aggregate the FTM, I did something like this:

#define DECLARE_FTM() \
protected: CComPtr<IUnknown> _m_Marshal; \
DECLARE_GET_CONTROLLING_UNKNOWN() \
public: HRESULT FinalConstruct() \
{ return CoCreateFreeThreadedMarshaler(GetControllingUnknown(),&_m_Marshal); }

#define COM_INTERFACE_ENTRY_FTM() COM_INTERFACE_ENTRY_AGGREGATE(IID_IMarshal,_m_Marshal.p)

Then, in your COM map:

BEGIN_COM_MAP(Blah)
    COM_INTERFACE_ENTRY(IBlah)
    COM_INTERFACE_ENTRY_FTM()
END_COM_MAP()
DECLARE_FTM()

I note that you aren't using ATL and the like - you would need to modify this so that your QueryInterface implementation returns the FTM pointer when IMarshal is queried for.

Note that aggregating the FTM is not something that can really be done lightly - it makes a number of unsafe assumptions that are not always valid. For instance, your class cannot make use of any interfaces which are not themselves free-threaded.

The other alternative is basically as @[Franci Penov] said, you need to ensure that your interface is able to be marshalled. The way I understand it, there is a standard marshaller which is able to marshal any interface in the type library, or you (that is the midl compiler does it more or less automatically) can make a proxy/stub dll (or merge the code for the proxy/stub into your own dll) which is able to marshal it for you.

This article here describes the process of building and registering the proxy/stub in more detail.

溺渁∝ 2024-08-13 08:58:40
  1. 将您的界面移至库部分。这将在类型库中得到它的定义。
  2. 将您的界面标记为 oleautomation。这会将其标记为可以使用标准编组器和 typelib 信息进行编组的接口,而不是由 midl 编译器生成的代理/存根。 (注意:虽然 oleautomation 确实来自旧的 OLE 世界,但它不需要您的接口从 IDispatch 派生)
  1. Move your interface into the library section. This will get its definition in the type library.
  2. Mark your interface as oleautomation. This will mark it as an interface that can be marshalled by using the standard marshaller and the typelib info, as opposed to the proxy/stub generated by the midl compiler. (NOTE: while oleautomation does come from the old OLE world, it does not require your interface to derive from IDispatch)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文