绝对的COM混淆 - C#与早期绑定互操作

时间:2009-10-06 22:49:23

标签: c# com interop com-interop

我一直在与VStudio,Google以及其他各种工具和网站进行斗争,但没有找到任何解决方案 - 帮助!!

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

IMyClassFactory和IMyClass以及相应的实现

我想在没有使用regsvr32注册COM服务器的情况下从C#BUT使用它们。 我用CoRegisterClassObject公开了Class Factory,我可以用非托管代码用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;
    };
}

2 个答案:

答案 0 :(得分:4)

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

为了聚合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之类的东西 - 你需要修改它,以便在查询IMarshal时你的QueryInterface实现返回FTM指针。

请注意,聚合FTM并不是真正可以轻易完成的事情 - 它会产生一些并非总是有效的不安全假设。例如,您的类不能使用任何本身不是自由线程的接口。

另一种选择基本上就像@ [Franci Penov]所说,你需要确保你的界面能够被编组。我理解它的方式,有一个标准的marshaller能够编组类型库中的任何接口,或者你(也就是midl编译器或多或少地自动执行)可以创建一个proxy / stub dll(或合并代码)代理/存根到你自己的dll),它可以为你编组。

本文here describes the process of building and registering the proxy/stub更详细。

答案 1 :(得分:1)

  1. 将您的界面移动到库部分。这将在类型库中得到它的定义。
  2. 将您的界面标记为oleautomation。这将标记为可以使用标准marshaller和typelib信息进行编组的接口,而不是midl编译器生成的代理/存根。 (注意:虽然oleautomation确实来自旧的OLE世界,但它不需要您的界面从IDispatch派生)