我没有定义正确的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;
}
委托和功能导入
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);
更新:asnwer之后的新代码,仍然无效: 错误是:“尝试读取或写入受保护的内存这通常表示其他内存已损坏”
奇怪的是,我可以在第一个事件调用中使用结构,我打印所有字段并且它们似乎是正确的(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#处理程序头
private void _handler(ref DG_CCTALK_APP_EVT pEVT)
答案 0 :(得分:3)
正如您所发现的那样,封送者不喜欢您尝试将该数组与您的联合翻译中的其他字段重叠。你已经通过将数组的偏移量更改为4而不是0来抑制问题。但这没有帮助。
现在,由于编组人员不会为你处理联盟,你必须手动完成。我会通过省略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;
}
请注意,我已删除了您声明的大部分详细信息。我怀疑该类型是由工具自动生成的。但所有这些冗长都让人很难阅读。
这将为结构生成正确的布局,但是会让您做一些工作来访问联合中的其他字段。
那么,我们怎样才能阅读这些字段?好吧,数据包含在字节数组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
。我不太了解本机代码的协议,以确保这是你想要的。如果它不是您想要的,那么您可以像这样编写setter:
Array.Copy(BitConverter.GetBytes(value), uData, sizeof(int));
您现在已经添加了互操作边界的C ++端。最明显的问题是您的C#委托似乎有错误的调用约定。它使用stdcall
,但C ++代码需要cdecl
。这肯定足以解释回调返回后的灾难性崩溃。您需要确保您的委托指定了调用约定。
[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);
完成这样的操作后,您只需传递一个字符串,从而避免当前实现的内存泄漏。
答案 1 :(得分:-1)
检查你的int和long字段。 我认为C int是16位而C#int是32: