COM interop passing wrong pointers, with only a single byte of my data

寵の児 提交于 2019-12-22 12:45:29

问题


I'm having a problem passing pointers to native code using COM. I want to build up a byte array in managed (C#) code and pass that array to native (C++) code. I'm handling the managed-code side, and my colleague owns the native side. Note that I'm much stronger on the managed side, and I've pretty much been using the COM object as written.

The COM signature (simplified) looks this:

unsafe void DoSomething([In] byte* buffer, [In] uint length)

And I call it like this:

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);
}

Debugging on the managed side, I see the data I expect at the memory location printed out (using Visual Studio's Memory view). When I debug the unmanaged side, I see the right data at that location as well. But, the pointer doesn't point to the right place! It points to a completely different memory location. That location contains the correct first byte of my data, but garbage for the rest of it. And then, of course, lots of wonderful crashy badness happens.

So, for instance, I see:

  • managed side: pointer (p) is 0x1234567, Memory at 0x1234567 contains the contents of my file.
  • unmanaged side: pointer (buffer) is 0x5678901, first byte of memory at that location contains the first byte of my file, rest is garbage. Memory at 0x1234567 contains the contents of my file.

I've also tried automatic marshaling:

void DoSomething([In] [MarshalAs(UnmanagedType.LPArray,
                                 ArraySubType = UnmanagedType.U1)]
                      byte[] buffer,
                 [In] uint length)

... using straight byte arrays on the managed side; the same result.

And I've tried allocating memory with Marshal.AllocHGlobal, copying data into that with Marshal.Copy and passing the resulting IntPtr, cast to a byte*. Same result.

What the heck am I missing here?

This is a 32-bit process. I've tried smallish (300B) and large (10MB) buffer sizes. C# 3.5, VS 2010.


回答1:


The core problem is that your COM method is not Automation compatible. A byte* is ambiguous. It can mean either a single byte passed by reference (ref byte in C#) or it can mean an array pointer passed by value (byte[] in C#). A type library cannot express the difference. Automation requires that you pass arrays as a SAFEARRAY.

You'll get away with this when you use the method from a C++ program, you simply pass a pointer to an array. But this goes wrong after Tlbimp.exe translates the type library into an interop library, the interop stub will have the argument declared as ref byte. Which is the exact reason you see only one byte copied.

Fixing this is painful if you can't fix the COM server. You'll have to disassemble the interop library with ildasm.exe /out. Then edit the method declaration in the IL file, then put humpty-dumpty back together with ilasm.exe. Use a little test program to know the exact way the IL should be edited.




回答2:


Have you tried:

void DoSomething([In] [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)]
                  byte[] buffer, [In] uint length)

?



来源:https://stackoverflow.com/questions/8829479/com-interop-passing-wrong-pointers-with-only-a-single-byte-of-my-data

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!