gpt4 book ai didi

C# pinvoke 具有 union 和数组的结构

转载 作者:太空狗 更新时间:2023-10-29 21:44:22 28 4
gpt4 key购买 nike

我未能定义正确的 C# 代码来与 C++ 库一起工作,该库定义了一个具有 union 和数组的复杂结构,我在执行 C++ 代码时不断遇到一些内存异常,我很确定这是由于这个原因造成的。

C++ 结构如下,忽略枚举和其他结构定义(如果需要我会发布它们,但它们非常广泛)

typedef struct
{
DG_CCTALK_APP_EVT_CODE eEventCode;
DG_CCTALK_APP_EVT_TYPE eEventType;
int iTime;
int iHandle;
unsigned char uchAddress;
DG_CCTALK_BILL_INFO sBillInfo;
int iBillAmount;
union
{
long lData;
int iData;
unsigned char ucArray[128];
char *cString;
void *pvoid;
} uData;
void *_private; // for use by cctalk app layer
} DG_CCTALK_APP_EVT, *PDG_CCTALK_APP_EVT;

C# 代码是这样的:

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Explicit)]
public struct Anonymous_92d21a81_2a8b_42f4_af32_f7606aa9cf40
{
//deberian llevar todos 0, pero el array genera problemas al ponerle 0, y cambiandolo explota en otro lado...

/// int
[System.Runtime.InteropServices.FieldOffsetAttribute(0)]
public int lData;

/// int
[System.Runtime.InteropServices.FieldOffsetAttribute(0)]
public int iData;

/// unsigned char[128]
[System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst = 128, ArraySubType = System.Runtime.InteropServices.UnmanagedType.I1)]
[System.Runtime.InteropServices.FieldOffsetAttribute(4)]
public byte[] ucArray;

/// char*
[System.Runtime.InteropServices.FieldOffsetAttribute(0)]
public System.IntPtr cString;

/// void*
[System.Runtime.InteropServices.FieldOffsetAttribute(0)]
public System.IntPtr pvoid;
}

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct DG_CCTALK_APP_EVT
{

/// DG_CCTALK_APP_EVT_CODE->_DG_CCTALK_APP_EVT_CODE
public DG_CCTALK_APP_EVT_CODE eEventCode;

/// DG_CCTALK_APP_EVT_TYPE->_DG_CCTALK_APP_EVT_TYPE
public DG_CCTALK_APP_EVT_TYPE eEventType;

/// int
public int iTime;

/// int
public int iHandle;

/// unsigned char
public byte uchAddress;

/// DG_CCTALK_BILL_INFO->_DG_CCTALK_BILL_INFO
public DG_CCTALK_BILL_INFO sBillInfo;

/// int
public int iBillAmount;

/// Anonymous_92d21a81_2a8b_42f4_af32_f7606aa9cf40
public Anonymous_92d21a81_2a8b_42f4_af32_f7606aa9cf40 uData;

/// void*
public System.IntPtr _private;
}

委托(delegate)和函数导入

public delegate void DG_CCTALK_APP_EVT_HANDLER(ref DG_CCTALK_APP_EVT pEVT);

[System.Runtime.InteropServices.DllImportAttribute("cctalk.dll", EntryPoint = "cctalk_app_enable_device", CallingConvention = CallingConvention.Cdecl)]
public static extern int cctalk_app_enable_device(ref DG_CCTALK_APP_EVT param0);

更新:回答后的新代码,仍然无法正常工作:错误是:“尝试读取或写入 protected 内存这通常表明其他内存已损坏”

奇怪的是,我可以在第一次事件调用时使用该结构,我打印所有字段并且它们似乎是正确的(lData 和 iData 具有相同的值,cData 具有我发送的字符串)但是在事件之后结束程序死亡。好像我的 C# 代码破坏了结构或其他东西......

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet = System.Runtime.InteropServices.CharSet.Ansi)]
public struct DG_CCTALK_APP_EVT
{

/// DG_CCTALK_APP_EVT_CODE->_DG_CCTALK_APP_EVT_CODE
public DG_CCTALK_APP_EVT_CODE eEventCode;

/// DG_CCTALK_APP_EVT_TYPE->_DG_CCTALK_APP_EVT_TYPE
public DG_CCTALK_APP_EVT_TYPE eEventType;

/// int
public int iTime;

/// int
public int iHandle;

/// unsigned char
public byte uchAddress;

/// DG_CCTALK_BILL_INFO->_DG_CCTALK_BILL_INFO
public DG_CCTALK_BILL_INFO sBillInfo;

/// int
public int iBillAmount;

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
public byte[] uData;

#region setters y getters para mapeo

public int lData
{
set
{
Array.Copy(BitConverter.GetBytes(value), uData, sizeof(int));
}
get
{
return BitConverter.ToInt32(uData, 0);
}
}

public int iData
{
set
{
Array.Copy(BitConverter.GetBytes(value), uData, sizeof(int));
}
get
{
return BitConverter.ToInt32(uData, 0);
}
}

public byte[] ucArray
{
set
{
Array.Copy(value, uData, 128);
}
get
{
return uData;

}
}

public IntPtr cString
{
set
{
Array.Copy(BitConverter.GetBytes(value.ToInt32()), uData, 32);
}
get
{
return (IntPtr)BitConverter.ToInt32(uData, 0);
}
}

public IntPtr pvoid
{
set
{
Array.Copy(BitConverter.GetBytes(value.ToInt32()), uData, 32);
}
get
{
return (IntPtr)BitConverter.ToInt32(uData, 0);
}
}


#endregion

/// void*
public System.IntPtr _private;
}

C++ 声明:

typedef void (*DG_CCTALK_APP_EVT_HANDLER)(PDG_CCTALK_APP_EVT pEVT);

DGCCTALK_PREFIX DG_ERROR cctalk_app_init(DG_CCTALK_APP_EVT_HANDLER pfnHandler);
DGCCTALK_PREFIX DG_ERROR cctalk_app_add_link(char *szPortName);

注意爸爸 DGCCTALK_PREFIX 定义为 __declspec(dllexport)

那些的C#代码:

public delegate void DG_CCTALK_APP_EVT_HANDLER(ref DG_CCTALK_APP_EVT pEVT);

[System.Runtime.InteropServices.DllImportAttribute("cctalk.dll", EntryPoint = "cctalk_app_init", CallingConvention = CallingConvention.Cdecl)]
public static extern int cctalk_app_init(DG_CCTALK_APP_EVT_HANDLER pfnHandler);

[System.Runtime.InteropServices.DllImportAttribute("cctalk.dll", EntryPoint = "cctalk_app_add_link", CallingConvention = CallingConvention.Cdecl)]
public static extern int cctalk_app_add_link(System.IntPtr szPortName);

调用它们的 C# 代码

var handler = new DG_CCTALK_APP_EVT_HANDLER(this._handler);
var resultado = cctalk_app_init(handler);
cctalk_app_add_link(Marshal.StringToHGlobalAnsi("port=" + port));

C# 处理程序 header

private void _handler(ref DG_CCTALK_APP_EVT pEVT)

最佳答案

正如您所发现的,编码(marshal)拆收器不喜欢您在 union 转换中尝试将数组与其他字段叠加。您通过将数组的偏移量更改为 4 而不是 0 来抑制问题。但这没有帮助。

现在,由于编码(marshal)拆收器不会为您处理 union ,您必须手动进行。我会通过省略 union 的非数组成员来处理这个问题。

[StructLayout(LayoutKind.Sequential)]
public struct DG_CCTALK_APP_EVT
{
public DG_CCTALK_APP_EVT_CODE eEventCode;
public DG_CCTALK_APP_EVT_TYPE eEventType;
public int iTime;
public int iHandle;
public byte uchAddress;
public DG_CCTALK_BILL_INFO sBillInfo;
public int iBillAmount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
public byte[] uData;
public IntPtr _private;
}

请注意,我已经删除了您声明中的大部分冗长内容。我怀疑该类型是由工具自动生成的。但是所有这些冗长的内容使其很难阅读。

这将为结构生成正确的布局,但会让您需要做一些工作才能访问 union 中的其他字段。

那么,我们如何读取这些字段呢?好吧,数据包含在字节数组 uData 中,因此只需从那里读取值即可。例如,您可以将以下属性添加到 DG_CCTALK_APP_EVT 结构中:

public int lData
{
get { return BitConverter.ToInt32(uData, 0); }
set { uData = BitConverter.GetBytes(value); }
}

public int iData
{
get { return BitConverter.ToInt32(uData, 0); }
set { uData = BitConverter.GetBytes(value); }
}

// etc.

请注意,setter 将删除数组的前 4 个字节以外的所有字节,因为它会覆盖 uData。我对 native 代码的协议(protocol)知之甚少,无法确定这就是您想要的。如果这不是您想要的,那么您可以这样编写 setter:

Array.Copy(BitConverter.GetBytes(value), uData, sizeof(int));

您现在已经添加了互操作边界的 C++ 端。最明显的问题是您的 C# 委托(delegate)似乎有错误的调用约定。它使用 stdcall 但 C++ 代码需要 cdecl。这当然足以解释回调返回后的灾难性崩溃。您需要确保您的委托(delegate)指定了调用约定。

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DG_CCTALK_APP_EVT_HANDLER(ref DG_CCTALK_APP_EVT pEVT);

你对cctalk_app_add_link的翻译也是错误的。应该是:

[DllImport("cctalk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int cctalk_app_add_link(string szPortName);

这样做你可以简单地传递一个字符串,从而避免你当前实现的内存泄漏。

关于C# pinvoke 具有 union 和数组的结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20274284/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com