来自C ++的Pinvoke结构转换
以下是一些经过validation的C ++:
typedef struct { PVOID buffer; UINT32 length; } DATA_BUFFER; typedef struct { DATA_BUFFER TxBuf [1]; DATA_BUFFER RxBuf [1]; } JVM_COMM_BUFFER; UINT32 SendAndRecv( IN JHI_HANDLE handle, IN CHAR* AppId, INOUT JVM_COMM_BUFFER* pComm );
以下是我尝试将其移植到C#:
[StructLayout(LayoutKind.Sequential)] public struct DATA_BUFFER { public byte[] buffer; public uint length; } [StructLayout(LayoutKind.Sequential)] public struct JVM_COMM_BUFFER { public DATA_BUFFER TxBuf; public DATA_BUFFER RxBuf; } [DllImport("jhi.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)] public static extern UInt32 SendAndRecv(IntPtr handle, string AppId, ref JVM_COMM_BUFFER pComm);
C#从编组中没有抛出任何exception,但C ++和C#版本的结果并不相同。 关于我缺少什么的任何想法?
问题可能出在你的第一个结构中。 C ++定义包括一个指向数据缓冲区的指针,但是Marshaller不能直接将它转换为.NET byte[]
(或者,在另一个方向上它可能将byte[]
转换为无效指针,因此你的非法Params错误)。 相反,您可以通过两个步骤手动执行此操作:
[StructLayout(LayoutKind.Sequential)] public struct DATA_BUFFER { public IntPtr buffer; public uint length; }
然后使用Marshal
手动读取缓冲区(这只是我对Marshal
API记忆的快速示例):
var txBufBytes = new byte[pComm.TxBuf.length]; Marshal.Copy(pComm.TxBuff.buffer, 0, pComm.TxBuf.length);
除此之外,正如@svick在评论中提到的,如果您的本机代码假定为非unicode / wide字符,则可能需要将CharSet
设置为CharSet.Ansi
。
最后,第二个结构的C ++定义似乎定义了单元素数组,而这些数组实际上可能是指针,而不是在内存中使用结构。 如果是这种情况,您可能必须替换您的定义以使用IntPtr
作为互操作,然后使用Marshal.PtrToStructure
来获取实际结构。
通过比较C ++中sizeof(...)
和C#中Marshal.SizeOf(...)
的结果,你至少应该比较结构大小是否相同。
鉴于您在评论中所说的内容,您应该能够使用我上面描述的DATA_BUFFER
修改,以及您用于JVM_COMM_BUFFER
的原始结构定义
[StructLayout(LayoutKind.Sequential)] struct JVM_COMM_BUFFER { public DATA_BUFFER TxBuf; public DATA_BUFFER RxBuf; }
将此与对DllImport
的略微修改相结合
[DllImport("jhi.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] public static extern UInt32 SendAndRecv(IntPtr handle, string AppId, [In, Out] ref JVM_COMM_BUFFER pComm);
CharSet.Ansi
对于确保将.NET字符串正确编组到ansi字符串非常重要(假设您的本机C函数不期望使用wchar_t
字符串类型)。 可能不需要[In, Out]
属性,但可能会向marshaller提示如何正确控制该参数。
如果JVM_COMM_BUFFER
确实是INOUT
,并且在调用函数之前使用数据预填充它,则可能需要确保数据全部有效。 您正在调用的函数可能包含有关其参数所需值的文档。 但是,此处的定义应根据您提供的C ++定义正确编组。
问题是仅分配内存和分配固定对象之间的区别。 一旦我将它切换到固定对象,那么除了缓冲区域之外没有任何IntPtrs的签名工作得很好。