以下是一些经验证有效的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#中没有从编组中抛出异常,但C ++和C#版本的结果并不相同。对我缺少的东西有任何想法吗?
答案 0 :(得分:0)
问题可能在你的第一个结构中。 C ++定义包含一个指向数据缓冲区的指针,但Marshaller无法直接将其转换为.NET byte[]
(或者向另一个方向转换它可能会将byte[]
转换为无效指针,因此你的非法参数错误)。相反,您可以通过两个步骤手动执行此操作:
[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
s作为互操作,然后使用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 ++定义正确编组。
答案 1 :(得分:0)
问题是仅分配内存和分配固定对象之间的区别。一旦我将它切换到固定对象,那么除缓冲区外没有任何IntPtrs的签名工作正常。