繁体   English   中英

从C ++转换Pinvoke结构

[英]Pinvoke struct translation from C++

以下是一些经过验证的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#版本,结果是不同的。 对我缺少的东西有任何想法吗?

问题可能出在您的第一个结构中。 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);

除此之外,正如注释中提到的CharSet.Ansi如果您的本机代码采用非Unicode /宽字符,则可能需要将CharSet设置为CharSet.Ansi

最后,第二个结构的C ++定义似乎定义了单元素数组,该数组实际上可能是指针,而不是将结构存储在内存中。 如果是这种情况,您可能必须替换定义以对Interop使用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 ++定义正确地编组。

问题是仅分配内存和分配固定对象之间的区别。 一旦将其切换到固定对象,则除了缓冲区字段之外,没有任何IntPtrs的签名就可以正常工作。

暂无
暂无

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

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