我正在使用C#中的本机C ++ dll。在C ++方面,有一个只实现一个接口和IUnknown的类,但它的QueryInterface为IUnknown以外的任何东西返回E_NOINTERFACE。
在C#端,我在dll中调用一个返回此类实例的方法,但即使我将参数声明为ISomeInterface,.NET仍会调用QueryInterface,这会导致异常。 E.g:
[ComImport, Guid(" ... ")]
public interface ISomeInterface { ... }
[DllImport("mylib.dll")]
public static extern void GetThing([MarshalAs(UnmanagedType.Interface)] out ISomeInterface thing);
调用GetThing
抛出InvalidCastException
消息"此操作失败,因为QueryInterface调用COM组件上的IID' {...}'由于以下错误而失败:不支持此类接口(来自HRESULT的异常:0x800040002(E_NOINTERFACE))。"
如果我将类型更改为对象
public static extern void GetThing([MarshalAs(UnmanagedType.Interface)] out object thing);
..然后调用GetThing
成功但尝试将结果System.__ComObject
强制转换为ISomeInterface
会抛出相同的异常。
我在接口定义上尝试了每个不同的InterfaceType
属性,但总是得到相同的异常。
我也尝试将接口定义为抽象类:
[ComImport, Guid(" ... "), ClassInterface(ClassInterfaceType.None)]
public abstract class ISomeInterface { ... }
调用GetThing
然后成功,我得到ISomeInterface
的实例,但是一旦我调用任何方法,它就会抛出BadImageFormatException
消息"错误的IL格式。&# 34;
我尝试了每个不同的ClassInterfaceType
属性,结果相同。
有没有办法在不修改C ++代码来修复QueryInterface实现的情况下从C#中使用这个类?
答案 0 :(得分:0)
通过这种方法,你提到了:
public static extern void GetThing([MarshalAs(UnmanagedType.Interface)] out object thing);
...假设您使用的是.NET 4+,请尝试以下操作:
object thing;
GetThing (out thing); // prefix me with the class defining the `DllImport`
dynamic d = thing;
d.DoSomething(); // CHANGE DoSomething() to a known method that ISomeInterface exposes
dynamic
与COM的行为是提前不需要任何类型库。当您在动态对象上键入方法的代码但将编译时,将无法在Visual Studio中看到语句完成/智能感知。它将在运行时进行评估。希望绕过QueryInterface()
并使用您指明COM类被装饰的default(ISomeInterface )
。
OP:
在C ++端,ISomeInterface是默认接口
OP:
...但我仍然有兴趣以任何方式让它工作而不必分发这个dll的新/非官方版本
如果这样可行则不需要更改COM DLL
答案 1 :(得分:0)
根据COM的规则,对象按照定义仅在在任何接口指针上调用QueryInterface时才实现接口所寻找接口的UUID返回一个相应的新(AddRef' d)接口指针(当然我的意思是返回S_OK并通过第二个参数提供接口指针)。 1
...这是一个只实现一个接口和IUnknown的类,但是它的QueryInterface为IUnknown以外的任何东西返回E_NOINTERFACE。
不,那是不对的。该对象没有实现这样的接口。如果对象是"假装"它实现了该接口,但不遵循COM的规则,那么该对象有一个主要的错误并且被破坏。您从这种情况中获得的任何功能基本上都是未定义的行为。
具体来说,如果在ISomething
的指针上调用QueryInterface返回E_NOINTERFACE(参见脚注中的推论),则返回类型为ISomething
的接口指针的方法是非法的。您必须修复该对象的C ++实现。
1 有几个微妙的要点:
首先,奇怪的是,在某人要求之前,对象不必决定是否实现给定的接口(尽管大多数对象当然都有硬编码的接口列表)。
其次,一旦对象已经做出决定并宣布关于给定接口的决定(通过返回新的接口指针或E_NOINTERFACE),该特定对象永远不会在其生命的剩余时间内改变主意,并且必须始终提供相同的答案。作为推论,从给定的接口指针调用QueryInterface,要求相同的接口,必须成功。这是您的对象被破坏的地方。
第三,我故意说" 任何"接口指针:例如,如果一个对象实现了IUnknown之外的五个接口,当被问及第六个接口时,它必须在从任何指向其他五个接口的任何指针以及IUnknown指针中提供相同的答案。也就是说,所有实现的接口必须可以从所有其他接口访问。
最后,请注意我一直在讨论对象,而不是 CoClasses 。 CoClasses,默认接口,注册表项,类型库,等,这些都是(有些可选的)机制,为了标准" COM工厂"的利益而存在。基础设施,并协助开发人员'工装。一旦你从该基础结构获得一个指向对象的接口指针,所有接口指针和底层对象(这意味着短语"是默认接口"是无关紧要的,并且不会从遵循COM接口的规则)
我推荐Don Box" Essential COM",您可以在其中详细列出所有这些内容。