C# 将字节数组从本机方法转换为托管结构

C# 将字节数组从本机方法转换为托管结构,c#,marshalling,C#,Marshalling,我有一个c#.net 2.0 CF应用程序,它与实现如下功能的本机DLL接口: struct NATIVE_METHOD_REPLY { int other_irrelevant_data; int data_size; void* data; } // reply_buffer will contain an array of NATIVE_METHOD_REPLY structures // and their data. // // returns an erro

我有一个c#.net 2.0 CF应用程序,它与实现如下功能的本机DLL接口:

struct NATIVE_METHOD_REPLY
{
    int other_irrelevant_data;
    int data_size;
    void* data;
}

// reply_buffer will contain an array of NATIVE_METHOD_REPLY structures
// and their data.
//
// returns an error code
int Foo(NATIVE_METHOD_REPLY* reply_buffer, int reply_size);
[StructLayout(LayoutKind.Sequential)]
internal struct NATIVE_METHOD_REPLY
{
    public Int32 OtherIrrelevantData;
    public Int16 DataSize;
    public IntPtr DataPtr;
}

[DllImport("my_lib.dll", SetLastError = true)]
internal static extern Int32 Foo(byte[] replyBuffer, Int32 replySize);

public byte[] void Bar()
{
    // data returned to the user. May be an arbitrary size.
    byte[] result_buffer = new byte[256];

    // data sent to Foo() 
    byte[] reply_buffer = 
        new byte[Marshal.SizeOf(typeof(NativeMethods.NATIVE_METHOD_REPLY)) + 
            result_buffer.Length];

    NativeMethods.Foo(reply_buffer, reply_buffer.Length);

    // is there a better way of doing this?

    NativeMethods.NATIVE_METHOD_REPLY reply;
    GCHandle pinned_reply = GCHandle.Alloc(reply_buffer, 
        GCHandleType.Pinned);
    try
    {
        reply = (NativeMethods.NATIVE_METHOD_REPLY)Marshal.PtrToStructure(
            pinned_reply.AddrOfPinnedObject(), 
            typeof(NativeMethods.NATIVE_METHOD_REPLY));

        Marshal.Copy(reply.DataPtr, result_buffer, 0, reply.DataSize);
    }
    finally
    {
        pinned_reply.Free();
    }

    // bonus point*: is this okay to do after the Free() call?
    int test = reply.OtherIrrelevantData;

    return result_buffer;
}
我在C#中实现了它,如下所示:

struct NATIVE_METHOD_REPLY
{
    int other_irrelevant_data;
    int data_size;
    void* data;
}

// reply_buffer will contain an array of NATIVE_METHOD_REPLY structures
// and their data.
//
// returns an error code
int Foo(NATIVE_METHOD_REPLY* reply_buffer, int reply_size);
[StructLayout(LayoutKind.Sequential)]
internal struct NATIVE_METHOD_REPLY
{
    public Int32 OtherIrrelevantData;
    public Int16 DataSize;
    public IntPtr DataPtr;
}

[DllImport("my_lib.dll", SetLastError = true)]
internal static extern Int32 Foo(byte[] replyBuffer, Int32 replySize);

public byte[] void Bar()
{
    // data returned to the user. May be an arbitrary size.
    byte[] result_buffer = new byte[256];

    // data sent to Foo() 
    byte[] reply_buffer = 
        new byte[Marshal.SizeOf(typeof(NativeMethods.NATIVE_METHOD_REPLY)) + 
            result_buffer.Length];

    NativeMethods.Foo(reply_buffer, reply_buffer.Length);

    // is there a better way of doing this?

    NativeMethods.NATIVE_METHOD_REPLY reply;
    GCHandle pinned_reply = GCHandle.Alloc(reply_buffer, 
        GCHandleType.Pinned);
    try
    {
        reply = (NativeMethods.NATIVE_METHOD_REPLY)Marshal.PtrToStructure(
            pinned_reply.AddrOfPinnedObject(), 
            typeof(NativeMethods.NATIVE_METHOD_REPLY));

        Marshal.Copy(reply.DataPtr, result_buffer, 0, reply.DataSize);
    }
    finally
    {
        pinned_reply.Free();
    }

    // bonus point*: is this okay to do after the Free() call?
    int test = reply.OtherIrrelevantData;

    return result_buffer;
}
虽然这是正确的,但我想知道这是否是实现此功能的最有效/最正确的方法

是否有某种方法可以将托管字节数组转换为不涉及中间本机句柄和副本的托管结构?例如,在C++中,我只需要这样做:

NATIVE_METHOD_REPLY* reply = reinterpret_cast< NATIVE_METHOD_REPLY* >( reply.DataPtr );

在C#中,在完整框架下,可以直接封送数组。看见我不知道紧凑框架有什么限制。

在C#中,在完整框架下,您可以直接封送数组。看见我不知道紧凑型框架有什么限制。

结构有固定的大小。传递数组没有意义,只需传递结构:

[DllImport("my_lib.dll", SetLastError = true)]
internal static extern Int32 Foo(out NATIVE_METHOD_REPLY replyBuffer, Int32 replySize);
您确实有内存管理问题。谁拥有指针


好的,结构实际上是可变大小的,指针指向数组。你需要另一种方法。只需预先分配一块非托管内存,而不是让P/Invoke封送拆收器将数据复制到托管数组中。这实际上是一个硬要求,因为垃圾收集器可以移动数组,从而使指针无效。调用Marshal.CoTaskMemAlloc()来保留内存,以后必须释放它。并将函数的第一个参数更改为IntPtr(而不是out)


您会发现封送结构也更容易,无需固定内存。完成后,不要忘记封送.FreeCoTaskMem()。

结构具有固定大小。传递数组没有意义,只需传递结构:

[DllImport("my_lib.dll", SetLastError = true)]
internal static extern Int32 Foo(out NATIVE_METHOD_REPLY replyBuffer, Int32 replySize);
您确实有内存管理问题。谁拥有指针


好的,结构实际上是可变大小的,指针指向数组。你需要另一种方法。只需预先分配一块非托管内存,而不是让P/Invoke封送拆收器将数据复制到托管数组中。这实际上是一个硬要求,因为垃圾收集器可以移动数组,从而使指针无效。调用Marshal.CoTaskMemAlloc()来保留内存,以后必须释放它。并将函数的第一个参数更改为IntPtr(而不是out)



您会发现封送结构也更容易,无需固定内存。完成后别忘了封送.FreeCoTaskMem()。

如果我切换传递结构,我将如何为该结构分配空间?另外,您可以进一步解释指针所有权问题吗?P/Invoke封送拆收器分配空间,然后将结构复制到您传递的参数中。该结构中有一个指针,可能是非托管代码将其设置为从堆中分配的内存块。谁发的?哦,我明白了。该结构位于大字节数组的顶部。结构中的指针指向结构结尾之后、字节数组结尾之前的某个区域。这就是为什么字节数组被声明为结构的长度+一些额外的数据。那么我的建议就不准确了,你得到的就像它将要得到的一样漂亮。@Hans Passant-好的。谢谢你的帮助。对加分问题有何评论?如果我通过结构切换,我将如何为该结构分配空间?另外,您可以进一步解释指针所有权问题吗?P/Invoke封送拆收器分配空间,然后将结构复制到您传递的参数中。该结构中有一个指针,可能是非托管代码将其设置为从堆中分配的内存块。谁发的?哦,我明白了。该结构位于大字节数组的顶部。结构中的指针指向结构结尾之后、字节数组结尾之前的某个区域。这就是为什么字节数组被声明为结构的长度+一些额外的数据。那么我的建议就不准确了,你得到的就像它将要得到的一样漂亮。@Hans Passant-好的。谢谢你的帮助。对加分问题有何评论?这不要求我在编译时知道回复缓冲区的大小吗?e、 g.
[marshallas(UnmanagedType.ByValArray,SizeConst=128)]
不,您不必在编译时知道大小。看见请记住,传递给该方法的
reply\u size
将是数组的大小(以字节为单位),而不是数组中的项数。好的。。。但这如何帮助我在不需要中间本机句柄的情况下将字节数组转换为结构呢?另外,我的数组是一个字节数组,因此数组中的元素数和数组大小应该是相同的,对吗?您是想从调用中获取一个本机\u方法\u应答结构,还是一个本机\u方法\u应答结构的数组?如果是前者,则更改您的注释,即“reply\u buffer将包含一个本机\u METHOD\u reply结构数组”,这不需要我在编译时知道reply buffer的大小吗?e、 g.
[marshallas(UnmanagedType.ByValArray,SizeConst=128)]
不,您不必在编译时知道大小。看见请记住,传递给该方法的
reply\u size
将是数组的大小(以字节为单位),而不是数组中的项数。好的。。。但这如何帮助我在不需要中间本机句柄的情况下将字节数组转换为结构呢?另外,我的数组是一个字节数组,因此数组中的元素数和数组大小应该是相同的,对吗?您是想从调用中获取一个本机\u方法\u应答结构,还是一个本机\u方法\u应答结构的数组?如果是前者,则更改您的注释,即“reply\u buffer将包含一个本机\u METHOD\u reply结构数组”