来自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的签名工作得很好。