Pinvoke struct translation from C++

六月ゝ 毕业季﹏ 提交于 2020-01-03 02:52:07

问题


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?


回答1:


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.




回答2:


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.



来源:https://stackoverflow.com/questions/7923871/pinvoke-struct-translation-from-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!