Reputation: 63
Sorry if I wasn't clear.
I'm using some video camera SDK, so a need to get alerted of some alarms like some object blocking the camera view and so on. SDK provides a function to subscribe to get alerted. That function is as fallowing:
H264_DVR_SetDVRMessCallBack(fMessCallBack cbAlarmcallback, unsigned long lUser);
cbAlarmCallback is as fallowing:
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 function:
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
}
}
All of this is working perfectly fine in c++, and char* pBuf is converted to SDK_AlarmInfo with no problem at all.
In C# I'm doing:
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 struct SDK_ALARM_INFO { int nChannel; int iEvent; //refer to SDK_EventCodeType int iStatus; // 0: start 1: stop SDK_SYSTEM_TIME SysTime; }
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;
}
In c# code the MessCallBack function is being invoked by the SDK, my problem here is that I'm not able to read alarmInfo as SDK_ALAM_INFO structure, so I can't read from inside this structure values like the time when the alarm occurs or the type of alarm ocurred.
I' ll really appreciate any help. Thank in advance
Upvotes: 3
Views: 284
Reputation: 3269
Problem which has to be solved is a marshalling of native structure to C# structure. First step to do is to create an exact mapping of C/C++ struct to C# structure.
[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;
}
In the above mapping there are two problems: the size of iEvent may be sometimes unknown as both in C and C++ enumerations will adapt smallest integral type in which enumeration values will fit. Therefore iEvent size may depend on C/C++ SDK_EventCodeType
definition if it is declared inside structure as this type. If translation to C# represents whole set of enumeration values than SDK_EventCodeType
would have a size of 1 byte. If struct would be declared as:
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;
than size and layout of the struct would be different than if declared in a way shown in the code listing. However this case is probably not present here. I am writing about this bcs I have already encountered this types of errors in SDK's coming with hardware. To avoid that problem I would double check iEvent
and iStatus
declarations and uses in other places of the SDK.
The declaration of SDK_SYSTEM_TIME
type requires creating a managed counterpart as well. The declaration of the structure in the first code listing can be tweaked making it easier to use during interop. Modified declaration demonstrates usage of fixed Array
and creation of C/C++ like union
with help of [StructLayout(LayoutKind.Explicit)]
and [FieldOffset(12)]
attributes:
[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;
}
The last field of the structure is a union of fixed int Time[8]
and SDK_SYSTEM_TIME
. It is not necessary to create union to marshal native data - it will work with either SDK_SYSTEM_TIME
or fixed int Time[8]
.
Finally conversion from native pointer passed to callback to our struct SDK_ALARM_INFO
can be achieved with call to:
var result = Marshal.PtrToStructure<SDK_ALARM_INFO>((IntPtr)pBuff);
The most difficult part of marshaling in such cases is creation of managed structure which would exactly reflect memory layout of native structure.
One of useful techniques at the beginning could be use of Marshal.SizeOf<T>()
method adn [StructLayout(LayoutKind.Explicit)]
attribute on managed side and sizeof(T)
offsetof (type,member)
on native side to check for correct representations.
Upvotes: 4