如何在 C# 代码中使用 C/C++ 传递给回调的本机结构
How to use C/C++ native structure passed to callback in C# code
对不起,如果我不清楚。
我正在使用一些摄像机 SDK,因此需要收到某些警报的提醒,例如某些物体挡住了摄像机视图等。
SDK 提供订阅提醒功能。
该函数如下:
H264_DVR_SetDVRMessCallBack(fMessCallBack cbAlarmcallback, unsigned long lUser);
cbAlarmCallback 正在休假:
bool __stdcall MessCallBack(long lLoginID, char *pBuf,
unsigned long dwBufLen, long dwUser)
{
return DealwithAlarm(lLoginID,pBuf,dwBufLen);
}
//Note here char *pBuf, in c++ its declared as char* but it needs to be memcpy to SDK_AlarmInfo struct, this is declared in c++ as fallows:
//alarm information
typedef struct SDK_ALARM_INFO
{
int nChannel;
int iEvent; ///< refer to SDK_EventCodeTypes
int iStatus; ///< 0: start 1: stop
SDK_SYSTEM_TIME SysTime;
}SDK_AlarmInfo;
typedef struct SDK_SYSTEM_TIME{
int year;///< year
int month;///< month,January = 1, February = 2, and so on.
int day;///< day
int wday;///< week, Sunday = 0, Monday = 1, and so on
int hour;///< hour
int minute;///< minute
int second;///< second
int isdst;///< DST(daylight saving time) flag, Yes = 1, No = 0
}SDK_SYSTEM_TIME;
DealwithAlarm 函数:
void CClientDemoDlg::DealwithAlarm(long lDevcID, char* pBuf , DWORD dwLen)
{
SDK_AlarmInfo alarmInfo;
memcpy ( &alarmInfo, pBuf, dwLen );
if ( (SDK_EVENT_CODE_NET_ALARM == alarmInfo.iEvent ))
{
// Do something
}
}
所有这些在 C++ 中都运行良好,char* pBuf 转换为 SDK_AlarmInfo 完全没有问题。
在 C# 中我正在做:
public delegate bool fMessCallBack(int lLoginID, byte[] pBuf, uint dwBufLen, IntPtr dwUser);
[DllImport("NetSdk.dll")]
public static extern void H264_DVR_SetDVRMessCallBack(fMessCallBack cbAlarmcallback, IntPtr lUser);
public 结构 SDK_ALARM_INFO
{
int nChannel;
内部事件; //参考SDK_EventCodeType
int i状态; // 0:开始 1:停止
SDK_SYSTEM_TIME系统时间;
}
public struct SDK_SYSTEM_TIME
{
public int year;//
public int month;//January = 1, February = 2, and so on.
public int day;//
public int wday;//Sunday = 0, Monday = 1, and so on
public int hour;//
public int minute;//
public int second;//
public int isdst;//
}
public enum SDK_EVENT_CODE_TYPES
{
SDK_EVENT_CODE_INIT = 0,
SDK_EVENT_CODE_LOCAL_ALARM = 1, //local alarm
SDK_EVENT_CODE_NET_ALARM, //network alarm
SDK_EVENT_CODE_MANUAL_ALARM, //manual alarm
SDK_EVENT_CODE_VIDEO_MOTION, //motion detect
SDK_EVENT_CODE_VIDEO_LOSS, //loss detect
SDK_EVENT_CODE_VIDEO_BLIND, //blind detect
}
public int Init()
{
fMessCallBack msgcallback = new fMessCallBack(MessCallBack);
H264_DVR_SetDVRMessCallBack(msgcallback, dwUser);
}
bool MessCallBack(int lLoginID, byte[] pBuf, uint dwBufLen, IntPtr dwUser)
{
SDK_ALARM_INFO alarmInfo;
//here i should translate pBuf param into alarm Info
return true;
}
在 C# 代码中,SDK 正在调用 MessCallBack 函数,我的问题是我无法将 alarmInfo 读取为 SDK_ALAM_INFO 结构,因此我无法从该结构中读取值比如报警发生的时间或者报警类型。
非常感谢任何帮助。提前致谢
必须解决的问题是将本机结构编组为 C# 结构。第一步是创建 C/C++ 结构到 C# 结构的精确映射。
[StructLayout(LayoutKind.Sequential)]
public struct SDK_ALARM_INFO
{
int nChannel;
int iEvent; //refer to SDK_EventCodeType
int iStatus; // 0: start 1: stop
SDK_SYSTEM_TIME SysTime;
}
[StructLayout(LayoutKind.Sequential)]
public struct SDK_SYSTEM_TIME
{
public int year;
public int month;
public int day;
public int wday;
public int hour;
public int minute;
public int second;
public int isdst;
}
在上面的映射中有两个问题:iEvent 的大小有时可能是未知的,因为在 C 和 C++ 中枚举将采用枚举值适合的最小整数类型。因此,如果 iEvent 的大小在结构内部声明为这种类型,则可能取决于 C/C++ SDK_EventCodeType
定义。如果转换为 C# 表示整个枚举值集,那么 SDK_EventCodeType
的大小将是 1 个字节。如果 struct 声明为:
typedef struct SDK_ALARM_INFO
{
int nChannel;
SDK_EventCodeType iEvent; ///< refer to SDK_EventCodeTypes
int iStatus; ///< 0: start 1: stop
SDK_SYSTEM_TIME SysTime;
}SDK_AlarmInfo;
结构的大小和布局与以代码清单中显示的方式声明的结构不同。但是,这里可能不存在这种情况。我正在写这个 bcs 我已经在硬件随附的 SDK 中遇到过此类错误。为了避免这个问题,我会仔细检查 iEvent
和 iStatus
声明以及在 SDK 其他地方的使用。
SDK_SYSTEM_TIME
类型的声明也需要创建一个托管对应物。可以调整第一个代码清单中的结构声明,使其在互操作期间更易于使用。修改后的声明演示了 fixed Array
的用法和 C/C++ 的创建,例如 union
借助 [StructLayout(LayoutKind.Explicit)]
和 [FieldOffset(12)]
属性:
[StructLayout(LayoutKind.Explicit)]
public unsafe struct SDK_ALARM_INFO
{
[FieldOffset(0)]
int nChannel;
[FieldOffset(4)]
int iEvent; //refer to SDK_EventCodeType
[FieldOffset(8)]
int iStatus; // 0: start 1: stop
[FieldOffset(12)]
SDK_SYSTEM_TIME SysTime;
[FieldOffset(12)]
fixed int Time[8];
}
[StructLayout(LayoutKind.Sequential)]
public struct SDK_SYSTEM_TIME
{
public int year;
public int month;
public int day;
public int wday;
public int hour;
public int minute;
public int second;
public int isdst;
}
结构的最后一个字段是 fixed int Time[8]
和 SDK_SYSTEM_TIME
的并集。不必创建联合来编组本机数据 - 它可以与 SDK_SYSTEM_TIME
或 fixed int Time[8]
.
一起使用
最终从传递给回调的本机指针到我们的 struct SDK_ALARM_INFO
的转换可以通过调用来实现:
var result = Marshal.PtrToStructure<SDK_ALARM_INFO>((IntPtr)pBuff);
在这种情况下,编组最困难的部分是创建托管结构,该结构将准确反映本机结构的内存布局。
一开始的一个有用技术可以是在托管端使用 Marshal.SizeOf<T>()
方法和 [StructLayout(LayoutKind.Explicit)]
属性,在本机端使用 sizeof(T)
offsetof (type,member)
来检查是否正确陈述。
对不起,如果我不清楚。
我正在使用一些摄像机 SDK,因此需要收到某些警报的提醒,例如某些物体挡住了摄像机视图等。 SDK 提供订阅提醒功能。 该函数如下:
H264_DVR_SetDVRMessCallBack(fMessCallBack cbAlarmcallback, unsigned long lUser);
cbAlarmCallback 正在休假:
bool __stdcall MessCallBack(long lLoginID, char *pBuf,
unsigned long dwBufLen, long dwUser)
{
return DealwithAlarm(lLoginID,pBuf,dwBufLen);
}
//Note here char *pBuf, in c++ its declared as char* but it needs to be memcpy to SDK_AlarmInfo struct, this is declared in c++ as fallows:
//alarm information
typedef struct SDK_ALARM_INFO
{
int nChannel;
int iEvent; ///< refer to SDK_EventCodeTypes
int iStatus; ///< 0: start 1: stop
SDK_SYSTEM_TIME SysTime;
}SDK_AlarmInfo;
typedef struct SDK_SYSTEM_TIME{
int year;///< year
int month;///< month,January = 1, February = 2, and so on.
int day;///< day
int wday;///< week, Sunday = 0, Monday = 1, and so on
int hour;///< hour
int minute;///< minute
int second;///< second
int isdst;///< DST(daylight saving time) flag, Yes = 1, No = 0
}SDK_SYSTEM_TIME;
DealwithAlarm 函数:
void CClientDemoDlg::DealwithAlarm(long lDevcID, char* pBuf , DWORD dwLen)
{
SDK_AlarmInfo alarmInfo;
memcpy ( &alarmInfo, pBuf, dwLen );
if ( (SDK_EVENT_CODE_NET_ALARM == alarmInfo.iEvent ))
{
// Do something
}
}
所有这些在 C++ 中都运行良好,char* pBuf 转换为 SDK_AlarmInfo 完全没有问题。
在 C# 中我正在做:
public delegate bool fMessCallBack(int lLoginID, byte[] pBuf, uint dwBufLen, IntPtr dwUser);
[DllImport("NetSdk.dll")]
public static extern void H264_DVR_SetDVRMessCallBack(fMessCallBack cbAlarmcallback, IntPtr lUser);
public 结构 SDK_ALARM_INFO { int nChannel; 内部事件; //参考SDK_EventCodeType int i状态; // 0:开始 1:停止 SDK_SYSTEM_TIME系统时间; }
public struct SDK_SYSTEM_TIME
{
public int year;//
public int month;//January = 1, February = 2, and so on.
public int day;//
public int wday;//Sunday = 0, Monday = 1, and so on
public int hour;//
public int minute;//
public int second;//
public int isdst;//
}
public enum SDK_EVENT_CODE_TYPES
{
SDK_EVENT_CODE_INIT = 0,
SDK_EVENT_CODE_LOCAL_ALARM = 1, //local alarm
SDK_EVENT_CODE_NET_ALARM, //network alarm
SDK_EVENT_CODE_MANUAL_ALARM, //manual alarm
SDK_EVENT_CODE_VIDEO_MOTION, //motion detect
SDK_EVENT_CODE_VIDEO_LOSS, //loss detect
SDK_EVENT_CODE_VIDEO_BLIND, //blind detect
}
public int Init()
{
fMessCallBack msgcallback = new fMessCallBack(MessCallBack);
H264_DVR_SetDVRMessCallBack(msgcallback, dwUser);
}
bool MessCallBack(int lLoginID, byte[] pBuf, uint dwBufLen, IntPtr dwUser)
{
SDK_ALARM_INFO alarmInfo;
//here i should translate pBuf param into alarm Info
return true;
}
在 C# 代码中,SDK 正在调用 MessCallBack 函数,我的问题是我无法将 alarmInfo 读取为 SDK_ALAM_INFO 结构,因此我无法从该结构中读取值比如报警发生的时间或者报警类型。
非常感谢任何帮助。提前致谢
必须解决的问题是将本机结构编组为 C# 结构。第一步是创建 C/C++ 结构到 C# 结构的精确映射。
[StructLayout(LayoutKind.Sequential)]
public struct SDK_ALARM_INFO
{
int nChannel;
int iEvent; //refer to SDK_EventCodeType
int iStatus; // 0: start 1: stop
SDK_SYSTEM_TIME SysTime;
}
[StructLayout(LayoutKind.Sequential)]
public struct SDK_SYSTEM_TIME
{
public int year;
public int month;
public int day;
public int wday;
public int hour;
public int minute;
public int second;
public int isdst;
}
在上面的映射中有两个问题:iEvent 的大小有时可能是未知的,因为在 C 和 C++ 中枚举将采用枚举值适合的最小整数类型。因此,如果 iEvent 的大小在结构内部声明为这种类型,则可能取决于 C/C++ SDK_EventCodeType
定义。如果转换为 C# 表示整个枚举值集,那么 SDK_EventCodeType
的大小将是 1 个字节。如果 struct 声明为:
typedef struct SDK_ALARM_INFO
{
int nChannel;
SDK_EventCodeType iEvent; ///< refer to SDK_EventCodeTypes
int iStatus; ///< 0: start 1: stop
SDK_SYSTEM_TIME SysTime;
}SDK_AlarmInfo;
结构的大小和布局与以代码清单中显示的方式声明的结构不同。但是,这里可能不存在这种情况。我正在写这个 bcs 我已经在硬件随附的 SDK 中遇到过此类错误。为了避免这个问题,我会仔细检查 iEvent
和 iStatus
声明以及在 SDK 其他地方的使用。
SDK_SYSTEM_TIME
类型的声明也需要创建一个托管对应物。可以调整第一个代码清单中的结构声明,使其在互操作期间更易于使用。修改后的声明演示了 fixed Array
的用法和 C/C++ 的创建,例如 union
借助 [StructLayout(LayoutKind.Explicit)]
和 [FieldOffset(12)]
属性:
[StructLayout(LayoutKind.Explicit)]
public unsafe struct SDK_ALARM_INFO
{
[FieldOffset(0)]
int nChannel;
[FieldOffset(4)]
int iEvent; //refer to SDK_EventCodeType
[FieldOffset(8)]
int iStatus; // 0: start 1: stop
[FieldOffset(12)]
SDK_SYSTEM_TIME SysTime;
[FieldOffset(12)]
fixed int Time[8];
}
[StructLayout(LayoutKind.Sequential)]
public struct SDK_SYSTEM_TIME
{
public int year;
public int month;
public int day;
public int wday;
public int hour;
public int minute;
public int second;
public int isdst;
}
结构的最后一个字段是 fixed int Time[8]
和 SDK_SYSTEM_TIME
的并集。不必创建联合来编组本机数据 - 它可以与 SDK_SYSTEM_TIME
或 fixed int Time[8]
.
最终从传递给回调的本机指针到我们的 struct SDK_ALARM_INFO
的转换可以通过调用来实现:
var result = Marshal.PtrToStructure<SDK_ALARM_INFO>((IntPtr)pBuff);
在这种情况下,编组最困难的部分是创建托管结构,该结构将准确反映本机结构的内存布局。
一开始的一个有用技术可以是在托管端使用 Marshal.SizeOf<T>()
方法和 [StructLayout(LayoutKind.Explicit)]
属性,在本机端使用 sizeof(T)
offsetof (type,member)
来检查是否正确陈述。