【问题标题】:How to use C/C++ native structure passed to callback in C# code如何在 C# 代码中使用传递给回调的 C/C++ 本机结构
【发布时间】:2017-11-01 13:22:01
【问题描述】:

对不起,如果我不清楚。

我正在使用一些摄像机 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);

公共结构 SDK_ALARM_INFO { 诠释 nChannel; 诠释 iEvent; //参考SDK_EventCodeType 诠释 iStatus; // 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# 代码 - 您将无法逐字逐句/逐函数翻译 - 不同的语言做不同的事情。 DealwithAlarm 是否处理过 alarm 以外的任何事情?
  • 我觉得我们错过了很多关于你最初是如何来到这里的背景信息。 pBuf 来自哪里?无论如何,只需 memcpy 进入 POD 的安全性令人怀疑,在这种情况下实际上存在安全风险,因为 dwLen 可能大于 sizeof(alarmInfo),从而导致经典的缓冲区溢出漏洞。
  • C#中memcpy的翻译是使用赋值运算符。

标签: c# c++ .net interop marshalling


【解决方案1】:

必须解决的问题是将本地结构编组为 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 的硬件中遇到了这种类型的错误。为了避免这个问题,我会仔细检查 iEventiStatus 声明并在 SDK 的其他地方使用。

SDK_SYSTEM_TIME 类型的声明也需要创建一个托管副本。可以调整第一个代码清单中的结构声明,使其更易于在互操作期间使用。修改后的声明演示了fixed Array 的使用以及在[StructLayout(LayoutKind.Explicit)][FieldOffset(12)] 属性的帮助下创建union 之类的C/C++:

[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_TIMEfixed int Time[8]

最终,从传递给回调的本机指针到我们的struct SDK_ALARM_INFO 的转换可以通过调用来实现:

var result = Marshal.PtrToStructure<SDK_ALARM_INFO>((IntPtr)pBuff);

在这种情况下,编组最困难的部分是创建托管结构,该结构将准确反映本机结构的内存布局。

一开始有用的技术之一是在托管端使用Marshal.SizeOf&lt;T&gt;() 方法和[StructLayout(LayoutKind.Explicit)] 属性,在本机端使用sizeof(T) offsetof (type,member) 来检查正确的表示。

【讨论】:

  • 您好,非常感谢您的帮助。我的 C# 声明。 public struct SDK_SYSTEM_TIME { public int year;// public int month;//一月=1,二月=2,以此类推。 public int day;// public int wday;//Sunday = 0,Monday = 1,以此类推 public int hour;公共整数分钟;公共 int 秒;公开的 int isdst; }
  • @Gilbert:您能否编辑您的问题并添加到 SDK_SYSTEM_TIME 的代码清单声明中?我将为您的 C/C++ 结构创建完整的托管结构表示。
  • 对不起,我对这件事真的很陌生。
  • 没有问题可以来帮忙:)
  • 我添加了 SDK_SYSTEM_TIME 声明我的实际 c# 和 c++ 定义
猜你喜欢
  • 2018-08-16
  • 2011-07-03
  • 1970-01-01
  • 2014-10-07
  • 1970-01-01
  • 1970-01-01
  • 2019-06-27
相关资源
最近更新 更多