COM互操作传递错误的指针,只有一个字节的数据

时间:2012-01-12 02:43:34

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

我在使用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。

2 个答案:

答案 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)