获取没有QueryInterface的COM接口

时间:2016-04-05 01:05:58

标签: c# c++ com interop com-interop

我正在使用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#中使用这个类?

2 个答案:

答案 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",您可以在其中详细列出所有这些内容。