创建COM接口,返回在C#中编组为IntPtr的指针

时间:2013-11-28 11:01:39

标签: c# com marshalling midl

我想在MIDL中声明一个允许返回指针的COM接口(就像在ID3D11Blob中一样)。我知道指针在COM中是一个特殊的东西,因为为RPC调用生成了存根。我不需要RPC,但只想从C#访问COM服务器。问题是:我可以以C#stub返回IntPtr的方式声明接口吗?我尝试添加[local]以启用void指针,但这还不够。

接口应该在MIDL中看起来像

[local] void *PeekData(void)

和C#一样

IntPtr PeekData()

这可能吗?如果是这样,怎么样?

提前致谢, 克里斯托弗

修改:重新提问:为什么

HRESULT GetData([in, out, size_is(*size)] BYTE data[], [in, out] ULONG *size);

成为

void GetData(ref byte, ref uint)

如何避免第一个参数在C#中成为单个byte

2 个答案:

答案 0 :(得分:3)

这是错误的,因为您从类型库导入了COM服务器声明。类型库最初设计用于支持最初称为“OLE自动化”的COM子集。哪个限制可用于方法参数的类型。特别是,原始指针允许。必须将数组声明为SAFEARRAY。这确保了调用者可以始终安全地索引数组,安全数组具有描述数组的排名和下限/上限的额外元数据。

[size_is]属性仅由MIDL理解,它用于创建代理和接口的存根。当需要将数据复制到通过线路发送到存根的互操作数据包时,知道数组包含多少元素也很重要。

由于类型库不支持这样的声明,因此[size_is]属性被剥离,类型库导入器只能看到BYTE *。这是不明确的,可以是通过引用传递的字节,也可以是指向字节数组的指针。导入器选择前者,因为它没有希望使数组工作,它不知道数组的大小。所以你得到ref byte

要解决此问题,您必须更改导入库,以便提供方法的正确声明。这需要[MarshalAs]属性将byte []参数声明为具有SizeParamIndex属性集的LPArray,这样您就可以告诉CLR数组大小由 size 参数决定。有两种基本方法可以解决这个问题:

  • 使用ildasm.exe反编译互操作库,修改.il文件并将其与ilasm.exe一起重新放回。您将使用ildasm.exe查看的示例C#声明来了解如何编辑IL。这是Microsoft recommends

  • 的方法
  • 使用可以将IL反编译回C#的优秀反编译器。 Reflector和ILSpy很受欢迎。将生成的代码复制/粘贴到项目的源文件中,并使用[MarshalAs]属性编辑方法。优点是编辑更容易,您不再再依赖于互操作库了。

在任何一种情况下,您都希望确保COM服务器稳定,因此您不必经常这样做。如果不是,那么强烈建议修改服务器本身,使用安全阵列。

答案 1 :(得分:0)

我想我在http://msdn.microsoft.com/en-gb/library/z6cfh6e6(v=vs.110).aspx#cpcondefaultmarshalingforarraysanchor2上找到了解决方案:这是C风格数组的默认行为。可以使用SAFEARRAY s。

来避免这种情况