我有一个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);
我已经用C#实现了它:
[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 );
*对于奖励点,在释放本机句柄后是否可以在结构中使用数据?
谢谢, PaulH
编辑:更新解决方案
[DllImport("my_lib.dll", SetLastError = true)]
internal static extern Int32 Foo(IntPtr replyBuffer, Int32 replySize);
public byte[] void Bar()
{
byte[] result_buffer = new byte[256];
int reply_buffer_len = Marshal.SizeOf(typeof(NativeMethods.NATIVE_METHOD_REPLY)) + result_buffer.Length;
IntPtr reply_buffer = Marshal.AllocCoTaskMem(reply_buffer_len);
NativeMethods.NATIVE_METHOD_REPLY reply;
try
{
NativeMethods.Foo(reply_buffer, reply_buffer_len);
reply = (NativeMethods.NATIVE_METHOD_REPLY)Marshal.PtrToStructure(
reply_buffer,
typeof(NativeMethods.NATIVE_METHOD_REPLY));
Marshal.Copy(reply.DataPtr, result_buffer, 0, reply.DataSize);
}
finally
{
Marshal.FreeCoTaskMem(reply_buffer);
}
return result_buffer;
}
答案 0 :(得分:1)
结构具有固定的尺寸。传递一个数组是没有意义的,只需传递结构:
[DllImport("my_lib.dll", SetLastError = true)]
internal static extern Int32 Foo(out NATIVE_METHOD_REPLY replyBuffer, Int32 replySize);
您确实遇到了内存管理问题。谁拥有指针?
好的,结构实际上是可变大小的,指针指向数组。你需要更进一步的方法。只需预先分配一块非托管内存,而不是让P / Invoke编组器将数据复制到托管数组中。这实际上是一个很难的要求,因为垃圾收集器可以移动数组,使指针无效。调用Marshal.CoTaskMemAlloc()来保留内存,你必须稍后释放它。并将函数的第一个参数更改为IntPtr(不是out)。
你会发现整理结构也很容易,不需要固定内存。完成后不要忘记Marshal.FreeCoTaskMem()。
答案 1 :(得分:0)
在完整框架下的C#中,您可以直接封送数组。见Default Marshaling for Arrays。我不知道Compact Framework的限制是什么。