来自 C++ 的 Pinvoke 结构翻译

发布于 2024-12-12 07:54:05 字数 931 浏览 1 评论 0原文

以下是一些已验证工作正常的 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

停滞 2024-12-19 07:54:05

问题可能出在您的第一个结构中。 C++ 定义包含一个指向数据缓冲区的指针,但 Marshaller 无法直接将其转换为 .NET byte[] (或者,转到另一个方向,它可能会转换 byte[ ] 指向无效指针,因此出现 Illegal 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/宽字符,您可能需要将 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] 属性可能不是必需的,但可能会提示编组器如何正确控制该参数。

如果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 the byte[] to an invalid pointer, thus your Illegal Params error). Instead, you can do this manually in two steps:

[StructLayout(LayoutKind.Sequential)]
public struct DATA_BUFFER
{
    public IntPtr buffer;
    public uint length;
}

And then read the buffer manually using Marshal (this is just a quick sample from my memory of the Marshal API):

var txBufBytes = new byte[pComm.TxBuf.length];
Marshal.Copy(pComm.TxBuff.buffer, 0, pComm.TxBuf.length);

In addition to that, as @svick mentioned in the comments, you will probably need to set the CharSet to CharSet.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 IntPtrs for the interop and then use Marshal.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++ with Marshal.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 for JVM_COMM_BUFFER

[StructLayout(LayoutKind.Sequential)]
struct JVM_COMM_BUFFER
{
    public DATA_BUFFER TxBuf;
    public DATA_BUFFER RxBuf;
}

Combine this with a slight modification to your 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);

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 a wchar_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 truly INOUT, 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.

相权↑美人 2024-12-19 07:54:05

问题在于仅分配内存和分配固定对象之间的区别。一旦我将其切换到固定对象,那么除了缓冲区字段之外没有任何 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文