简体   繁体   English

从C ++转换Pinvoke结构

[英]Pinvoke struct translation from C++

The following is a bit of C++ that is verified working: 以下是一些经过验证的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
);

The following is my attempt to port that to C#: 以下是我尝试将其移植到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. 封送处理不会在C#中引发异常,但是对于C ++和C#版本,结果是不同的。 Any idea on what I'm missing? 对我缺少的东西有任何想法吗?

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). C ++定义包括一个指向数据缓冲区的指针,但Marshaller无法将其直接转换为.NET byte[] (或者,如果将其转换为另一个方向,它可能会将byte[]转换为无效的指针,因此您的非法参数是错误)。 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): 然后使用Marshal手动读取缓冲区(这只是我对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. 除此之外,正如注释中提到的CharSet.Ansi如果您的本机代码采用非Unicode /宽字符,则可能需要将CharSet设置为CharSet.Ansi

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. 最后,第二个结构的C ++定义似乎定义了单元素数组,该数组实际上可能是指针,而不是将结构存储在内存中。 If that is the case you will probably have to replace your definition to use IntPtr s for the interop and then use Marshal.PtrToStructure to get the actual structures. 如果是这种情况,您可能必须替换定义以对Interop使用IntPtr ,然后使用Marshal.PtrToStructure获取实际结构。

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. 通过比较C ++中的sizeof(...)和C#中的Marshal.SizeOf(...)来获得结构定义,您至少应该比较结构大小是否相同。

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 鉴于您在注释中所说的,您应该能够使用我上面描述的DATA_BUFFER修改以及用于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的轻微修改

[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). CharSet.Ansi对于确保将.NET字符串正确地编组为ansi字符串非常重要(假设您的本机C函数不希望使用wchar_t字符串类型)。 The [In, Out] attributes might not be required, but might hint to the marshaller how to control that parameter properly. 可能不需要[In, Out]属性,但可能会提示编组如何正确控制该参数。

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. 如果JVM_COMM_BUFFER确实是INOUT ,并且在调用该函数之前预先填充了数据,则可能需要确保数据全部有效。 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. 但是,此处的定义应根据您提供的C ++定义正确地编组。

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. 一旦将其切换到固定对象,则除了缓冲区字段之外,没有任何IntPtrs的签名就可以正常工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM