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

Posted

技术标签:

【中文标题】创建 COM 接口,返回一个在 C# 中编组为 IntPtr 的指针【英文标题】:Create COM interface returning a pointer that is marshalled as IntPtr in C# 【发布时间】:2013-11-28 11:01:39 【问题描述】:

我想在 MIDL 中声明一个允许返回指针的 COM 接口(如在 ID3D11Blob 中)。我知道指针在 COM 中是一个特殊的东西,因为为 RPC 调用生成了存根。我不需要 RPC,但只想从 C# 访问 COM 服务器。问题是:我能否以 C# 存根返回 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

【问题讨论】:

【参考方案1】:

这是错误的,因为您从 类型库 导入了 COM 服务器声明。类型库最初旨在支持 COM 的一个子集,最初称为“OLE 自动化”。这限制可以用于方法参数的类型。特别是,不允许允许使用原始指针。数组必须声明为 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 服务器是稳定的,因此您不必经常这样做。如果不是,那么强烈建议修改服务器本身,使用安全数组。

【讨论】:

感谢您的详细解释。我正在决定SAFEARRAY 是否是更好的解决方案。有了你的详细信息,我认为这是要走的路。我真的不喜欢修补 IL 的想法,尽管这在 MSDN 上被明确提及为可能的解决方案...... 我发现的另一件事:如果没有输入返回值(即它必须是void *)并且具有@987654326 @ 属性。【参考方案2】:

我想我在http://msdn.microsoft.com/en-gb/library/z6cfh6e6(v=vs.110).aspx#cpcondefaultmarshalingforarraysanchor2 上找到了解决方案:这是 C 样式数组的默认行为。使用SAFEARRAYs 可以避免这种情况。

【讨论】:

您好,很高兴您找到了答案。请您从链接中提取相关信息,以供其他人试图找出相同的问题。另外,能否请您链接到英文版而不是德文版? :)

以上是关于创建 COM 接口,返回一个在 C# 中编组为 IntPtr 的指针的主要内容,如果未能解决你的问题,请参考以下文章

在 C++ 和 C# 之间编组类实例的指针

创建要在 C# 中编组的 C++ Dll 的最佳实践 [关闭]

如何在 C# 中编组 wstring *?

为 COM 接口启用编组需要啥?

c#编组尝试将结构从cpp dll返回到c#应用程序,该应用程序被分配为它自己的dll

将 C++ int* 编组为 C#