我未能定义正确的C#代码来处理包含联合和数组的复杂结构的C++库,在执行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);注意: dad 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)发布于 2013-11-29 09:30:20
正如您已经发现的,封送员不喜欢您在联合翻译中尝试将数组与其他字段重叠。您已经通过将数组的偏移量更改为4而不是0来解决这个问题。但这没什么用。
现在,由于编组员不会为您处理工会,所以您必须手动处理。我会通过省略工会的非数组成员来处理这件事。
[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));现在您已经添加了interop边界的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);这样做,您可以简单地传递一个字符串,从而避免当前实现中的内存泄漏。
发布于 2013-11-28 20:45:26
检查您的int和长字段。我认为that是16位,而C# int是32位:
https://stackoverflow.com/questions/20274284
复制相似问题