我在使用COM传递指向本机代码的指针时遇到问题。我想在托管(C#)代码中构建一个字节数组,并将该数组传递给本机(C ++)代码。我正在处理托管代码方面,我的同事拥有本机方面。请注意,我在托管端更强大,而且我几乎一直在使用COM对象。
COM签名(简化)看起来像这样:
unsafe void DoSomething([In] byte* buffer, [In] uint length)
我称之为:
var arr = File.ReadAllBytes(@"c:\temp\foo.bar");
fixed (byte* p = arr)
{
// let's see what we're actually pointing at
IntPtr ip = new IntPtr(p);
Console.WriteLine(ip.ToInt32().ToString("x"));
interop.DoSomething(p, arr.Length);
}
在托管端调试,我看到打印出的内存位置的数据(使用Visual Studio的内存视图)。当我调试非托管端时,我也在该位置看到了正确的数据。但是,指针并没有指向正确的位置!它指向一个完全不同的内存位置。该位置包含我的数据的正确的第一个字节,但其余部分包含垃圾。然后,当然,发生了许多奇妙的崩溃。
所以,例如,我看到了:
p
)为0x1234567,内存为0x1234567,包含我文件的内容。buffer
)是0x5678901,该位置的内存的第一个字节包含我文件的第一个字节,其余是垃圾。 0x1234567处的内存包含我文件的内容。我也尝试过自动封送:
void DoSomething([In] [MarshalAs(UnmanagedType.LPArray,
ArraySubType = UnmanagedType.U1)]
byte[] buffer,
[In] uint length)
...在托管端使用直字节数组;同样的结果。
我尝试使用Marshal.AllocHGlobal
分配内存,将数据复制到Marshal.Copy
并将结果IntPtr
传递给byte*
。结果相同。
我在这里错过了什么?
这是一个32位进程。我尝试过小型(300B)和大型(10MB)缓冲区大小。 C#3.5,VS 2010。
答案 0 :(得分:6)
核心问题是您的COM方法不兼容自动化。字节*不明确。它可以表示通过引用传递的单个字节(C#中的ref字节),也可以表示通过值传递的数组指针(C#中的byte [])。类型库无法表达差异。自动化要求您将数组作为SAFEARRAY传递。
当您使用C ++程序中的方法时,您只需将指针传递给数组即可。但是,在Tlbimp.exe将类型库转换为互操作库之后,这会出错,互操作存根将声明为ref字节。这是您只看到一个字节被复制的确切原因。
如果无法修复COM服务器,修复此问题会很痛苦。您将不得不使用ildasm.exe / out反汇编互操作库。然后编辑IL文件中的方法声明,然后将humpty-dumpty与ilasm.exe一起放回去。使用一个小测试程序来了解IL的编辑方式。
答案 1 :(得分:2)
你试过了吗?
void DoSomething([In] [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)]
byte[] buffer, [In] uint length)