来自 C++ 的 Pinvoke 结构翻译
以下是一些已验证工作正常的 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# 版本的结果并不相同。你知道我缺少什么吗?
The following is a bit of C++ that is verified working:
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
);
The following is my attempt to port that to 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);
There is no exception thrown in the C# from marshalling, but the results are not the same for the C++ and C# versions. Any idea on what I'm missing?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
问题可能出在您的第一个结构中。 C++ 定义包含一个指向数据缓冲区的指针,但 Marshaller 无法直接将其转换为 .NET
byte[]
(或者,转到另一个方向,它可能会转换byte[ ]
指向无效指针,因此出现 Illegal Params 错误)。 ,您可以分两步手动执行此操作:然后使用
Marshal
手动读取缓冲区(这只是我记忆中的Marshal
API 的一个快速示例):相反 除此之外,正如 @svick 在评论中提到的,如果您的本机代码假设非 unicode/宽字符,您可能需要将
CharSet
设置为CharSet.Ansi
。最后,第二个结构的 C++ 定义似乎定义了单元素数组,而单元素数组实际上可能是指针,而不是将结构存储在内存中。如果是这种情况,您可能必须替换定义以使用
IntPtr
进行互操作,然后使用Marshal.PtrToStructure
获取实际结构。您至少应该通过比较 C++ 中
sizeof(...)
的结果与 C# 中Marshal.SizeOf(...)
的结果来比较结构大小是否相同对于结构定义。鉴于您在评论中所说的内容,您应该能够使用我上面描述的 DATA_BUFFER 修改,以及您用于 JVM_COMM_BUFFER 的原始结构定义,
将其与对
DllImport
进行轻微修改CharSet.Ansi
对于确保您的 .NET 字符串正确编组为 ansi 字符串非常重要(假设您的本机 C 函数不期望wchar_t
字符串类型)。[In, Out]
属性可能不是必需的,但可能会提示编组器如何正确控制该参数。如果
JVM_COMM_BUFFER
确实是INOUT
,并且您在调用函数之前用数据预先填充它,则可能需要确保数据全部有效。您调用的函数可能有关于它期望其参数具有什么值的文档。但是,此处的定义应根据您提供的 C++ 定义正确编组。The problem is probably in your first struct. The C++ definition includes a pointer to a data buffer, but the Marshaller can't directly convert this to a .NET
byte[]
(or, going the other direction it probably converts thebyte[]
to an invalid pointer, thus your Illegal Params error). Instead, you can do this manually in two steps:And then read the buffer manually using
Marshal
(this is just a quick sample from my memory of theMarshal
API):In addition to that, as @svick mentioned in the comments, you will probably need to set the
CharSet
toCharSet.Ansi
if your native code is assuming non-unicode/wide characters.Finally, the C++ definition of the second struct seems to define single-element arrays which might in turn actually be pointers, rather than having the structs in memory. If that is the case you will probably have to replace your definition to use
IntPtr
s for the interop and then useMarshal.PtrToStructure
to get the actual structures.You should at least compare that the struct sizes are the same by comparing the results of
sizeof(...)
in C++ withMarshal.SizeOf(...)
in C# for the struct definitions.Given what you've said in the comments, you should be able to use the
DATA_BUFFER
modification I described above, and the original struct definition you used forJVM_COMM_BUFFER
Combine this with a slight modification to your
DllImport
The
CharSet.Ansi
is important for ensuring your .NET string gets marshalled correctly to an ansi string (assuming your native C function doesn't expect awchar_t
string type). The[In, Out]
attributes might not be required, but might hint to the marshaller how to control that parameter properly.If the
JVM_COMM_BUFFER
is trulyINOUT
, and you're pre-populating it with data before calling the function, you may need to make sure the data is all valid. The function you're calling may have documentation on what values it expects its parameters to have. However, the definitions here should marshal correctly based on the C++ definitions you provided.问题在于仅分配内存和分配固定对象之间的区别。一旦我将其切换到固定对象,那么除了缓冲区字段之外没有任何 IntPtr 的签名就可以正常工作。
The issue is the difference between just allocating memory and allocating a pinned object. Once I switched it to a pinned object, then the signature without any IntPtrs except the buffer field worked just fine.