天天看点

c++ c#结构体调用

1、最近做项目遇到,C#调用C++dll里的函数需要传递结构体参数,发现这个并不是简单的在C#里定义相应的结构体就可以的,下面以一个例子来说明解决的办法,C++中的函数和结构体如下:

uint msec_set_igr_gen_cfg(int port, IGR_GEN_T *igr_gen)

{

return 0;

}

typedef struct {

  int aa_disable;

  int badtag_rej;

  int pad_en;

  int byp_ctl_sl;

  int byp_ctl_v;

  int byp_ctl_sc;

  int byp_ctl_ec;

  int sectag_flag;

} IGR_GEN_T;

在C#中 首先需要使用Dllimport将相应的C++dll load进来,然后定义相应的结构体,具体如下:

        [DllImport("..\\debug\\mgd_MacSec.dll")]

        private static extern UInt32 msec_set_igr_gen_cfg(int port, IntPtr igr_gen);

        结构体定义:

    [StructLayout(LayoutKind.Sequential)]

    public class IGR_GEN_T 

    {

        int aa_disable;

        int badtag_rej;

        int pad_en;

        int byp_ctl_sl;

        int byp_ctl_v;

        int byp_ctl_sc;

        int byp_ctl_ec;

        int sectag_flag;

        public IGR_GEN_T()

        {

            aa_disable = 0;

            badtag_rej = 0;

            pad_en = 0;

            byp_ctl_ec = 0;

            byp_ctl_sc = 0;

            byp_ctl_sl = 0;

            byp_ctl_v = 0;

            sectag_flag = 0;

        }

    } ;

  在代码中具体引用函数时如下所示,

     IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(igr_gen));

            Marshal.StructureToPtr(igr_gen, ptr, false);

            UInt32 ret = _msec_set_igr_gen_cfg(port, ptr);

            igr_gen = (IGR_GEN_T)Marshal.PtrToStructure(ptr, typeof(IGR_GEN_T));

            Marshal.FreeHGlobal(ptr);

            return ret;

从以上步骤可以看出,结构体参数的传递需要marshal做辅助做相应的转化,以intptr的方式传输结构体参数。

2、还存在另外一种情况,是结构体中嵌套有结构体时需要做一些特殊处理,具体如下:

结构体

typedef struct {

  int ctx_num;

  int sec_level;

  int drop_maxpn;

  int drop;    

  int redir;  

  int auth_en;

  int enc_en;  

} ACT_FLD;

typedef struct {

  ACT_FLD   *lk_act_fld;  

} LKUP_T;

C++ 函数

uint  msec_port_set_egr_entry (IN int port, IN int ent_num, IN LKUP_T *egr_lkup)

{

//  

}

C#在调用时首先将相应dll import进来,进行相应结构体的定义和相应函数的声明,具体如下:

        [DllImport("..\\debug\\mgd_MacSec.dll")]

        private static extern UInt32 msec_set_igr_gen_cfg(int port, IntPtr igr_gen);

    [StructLayout(LayoutKind.Sequential)]

    public class ACT_FLD 

    {

        public int ctx_num;

        public int sec_level;

        public int drop_maxpn;

        public int drop;    

        public int redir;  

        public int auth_en;

        public int enc_en;  

        public ACT_FLD()

        {

            ctx_num = 0;

            sec_level = 0;

            drop_maxpn = 0;

            drop = 0;

            redir = 0;

            auth_en = 0;

            enc_en = 0;

        }

    } 

    [StructLayout(LayoutKind.Sequential)]

    public class LKUP_T

    {

        public IntPtr lk_act_fld;

        public LKUP_T()

        {

            ACT_FLD lk_act_fld_s = new ACT_FLD();

            lk_act_fld = Marshal.AllocHGlobal(Marshal.SizeOf(lk_act_fld_s));

            Marshal.StructureToPtr(lk_act_fld_s, lk_act_fld, false);

        }

    } 

    具体在代码中引用时如下所示:

            IntPtr egr_lkup_ptr = Marshal.AllocHGlobal(Marshal.SizeOf(egr_lkup));

            Marshal.StructureToPtr(egr_lkup, egr_lkup_ptr, false);

            uint ret = _msec_port_set_egr_entry(port, ent_num, egr_lkup_ptr);

            egr_lkup = (LKUP_T)Marshal.PtrToStructure(egr_lkup_ptr, typeof(LKUP_T));

            Marshal.FreeHGlobal(egr_lkup_ptr);

另外枚举(enum)参数传递时类似于int型,只需在C#里定义相应的枚举提即可,不需做相应转化,在此不再给出具体方法。

另,C++ dll要想被C#所使用,需要进行设置,支持通用语言,具体如下图所示:(Common Language Runtime support选项)

c++ c#结构体调用

关于跨语言之间的结构体参数传输问题,不知是否还有其他比较简洁的办法,欢迎大家讨论,小弟初学C#和.NET,还请大家多多指教。

文献来源:http://blog.csdn.net/sundk911/article/details/7766954

C#调用c++dll文件是一件很麻烦的事情,首先面临的是数据类型转换的问题,相信经常做c#开发的都和我一样把学校的那点c++底子都忘光了吧(语言特性类)。

网上有一大堆得转换对应表,也有一大堆的转换实例,但是都没有强调一个更重要的问题,就是c#数据类型和c++数据类型占内存长度的对应关系。

如果dll文件中只包含一些基础类型,那这个问题可能可以被忽略,但是如果是组合类型(这个叫法也许不妥),如结构体、类类型等,在其中的成员变量的长度的申明正确与否将决定你对dll文件调用的成败。

如有以下代码,其实不是dll文件的源码,而是厂商给的c++例子代码

c++中的结构体申明 

typedef struct

{

unsigned char Port;

unsigned long Id;

unsigned char Ctrl;

unsigned char pData[8];

}HSCAN_MSG;

c++中的函数申明(一个c++程序引用另一个c++的dll文件)

extern "C" int _stdcall HSCAN_SendCANMessage(unsigned char nDevice,unsigned char nPort,HSCAN_MSG *msg,int nLength); 

c++中的调用:

....

HSCAN_MSG msg[100]; 

..... 

HSCAN_SendCANMessage(m_nDevice,m_nPort,msg,nFrames);

由上述代码可见,msg是个结构体的数组。

下面是我的c#的代码

c#结构体申明:(申明成) 

[StructLayout(LayoutKind.Sequential)]

  public struct HSCAN_MSG

  {

    // UnmanagedType.ByValArray, [MarshalAs(UnmanagedType.U1)]这个非常重要,就是申明对应类型和长度的

  [MarshalAs(UnmanagedType.U1)]

  public byte Port;

  [MarshalAs(UnmanagedType.U4)]

  public uint nId;

  [MarshalAs(UnmanagedType.U1)]

  public byte nCtrl;

  [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]

  public byte[] pData;

  }; 

 c#函数申明

[DllImport("HS2106API.dll")]

  public static extern int HSCAN_SendCANMessage(

  byte nDevice, byte nPort, HSCAN_MSG[] pMsg, int nLength); 

C#函数调用

 HSCAN_MSG[] msg = new HSCAN_MSG[1]; //发送缓冲区大小可根据需要设置;

  for (int yy = 0; yy < msg.Length; yy++)

  {

  msg[yy] = new HSCAN_MSG();

  }

    //...结构体中的成员的实例化略

    HSCAN_SendCANMessage(0x0, 0x0, msg, 1) 

那些只能用指针不能用结构体和类的地方

 c++中的结构体申明 

typedef struct

{

unsigned char Port;

unsigned long Id;

unsigned char Ctrl;

unsigned char pData[8];

}HSCAN_MSG;

 c++中的函数申明(一个c++程序引用另一个c++的dll文件)

extern "C" int _stdcall HSCAN_SendCANMessage(unsigned char nDevice,unsigned char nPort,HSCAN_MSG *msg,int nLength);

c#中的结构体申明:

[StructLayout(LayoutKind.Sequential)]

  public struct HSCAN_MSG

  {

  [MarshalAs(UnmanagedType.U1)]

  public byte Port;

  /// <summary>

  /// 节点标识,nEFF=1 时(扩展帧),为29 位nEFF=0(标准帧)时,为11 位;

  /// </summary>

  [MarshalAs(UnmanagedType.U4)]

  public uint nId;

  [MarshalAs(UnmanagedType.U1)]

  public byte nCtrl;

  [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]

  public byte[] pData;

  }; 

c#函数的调用:包含使用指针IntPtr替代结构体数组和读取IntPtr的方法

HSCAN_MSG[] msg1 = new HSCAN_MSG[10];

  for (int i = 0; i < msg1.Length; i++)

  {

  msg1[i] = new HSCAN_MSG();

  msg1[i].pData = new byte[8];

  }

  IntPtr[] ptArray = new IntPtr[1];

  ptArray[0] = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(HSCAN_MSG)) * 10);

  IntPtr pt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(HSCAN_MSG)));

  Marshal.Copy(ptArray, 0, pt, 1); 

  int count = HSCAN_ReadCANMessage(0x0, 0,pt, 10);

  textBoxStatus.Text += "\r\n" + "读取0口:" + count.ToString() + "帧数据";

  for (int j = 0; j < 10; j++)

  {

  msg1[j] =

  (HSCAN_MSG)Marshal.PtrToStructure((IntPtr)((UInt32)pt+ j * Marshal.SizeOf(typeof(HSCAN_MSG)))

  , typeof(HSCAN_MSG));

  textBoxStatus.Text += "\r\n收到0口" + Convert.ToByte(msg1[j].pData[0]).ToString()

  + "|" + Convert.ToByte(msg1[j].pData[1]).ToString()

  + "|" + Convert.ToByte(msg1[j].pData[2]).ToString()

  + "|" + Convert.ToByte(msg1[j].pData[3]).ToString()

  + "|" + Convert.ToByte(msg1[j].pData[4]).ToString()

  + "|" + Convert.ToByte(msg1[j].pData[5]).ToString()

  + "|" + Convert.ToByte(msg1[j].pData[6]).ToString()

  + "|" + Convert.ToByte(msg1[j].pData[7]).ToString();

  }

文献来源:http://topic.csdn.net/u/20090716/10/1388ad60-4968-400f-ab65-8a1fffe72e16.html?1983796426

##########################################################

C 的原型结构体

typedef struct _wfs_result

{

  REQUESTID RequestID;

  HSERVICE hService;

  SYSTEMTIME tsTimestamp;//这个对应下面的SYSTEMTIME 结构

  HRESULT hResult;

  union {

  DWORD dwCommandCode;

  DWORD dwEventID;

  } u;

  LPVOID lpBuffer; //返回的指针,指向WFSIDCSTATUS 这个结构

} WFSRESULT, * LPWFSRESULT;

typedef struct _SYSTEMTIME {

  WORD wYear;

  WORD wMonth;

  WORD wDayOfWeek;

  WORD wDay;

  WORD wHour;

  WORD wMinute;

  WORD wSecond;

  WORD wMilliseconds;

} SYSTEMTIME, *PSYSTEMTIME, *LPSYSTEMTIME;

typedef struct _wfs_idc_status

{

  WORD fwDevice;

  WORD fwMedia;

  WORD fwRetainBin;

  WORD fwSecurity;

  USHORT usCards;

  WORD fwChipPower;

  LPSTR lpszExtra;

} WFSIDCSTATUS, * LPWFSIDCSTATUS;

C#对应定义的结构体:

[StructLayout(LayoutKind.Sequential)]

public unsafe struct _wfs_result

{

  public UInt32 RequestID;

  public ushort hService;  

  public _SYSTEMTIME tsTimestamp;//对应下面的 _SYSTEMTIME

  public int hResult;

  public AnonymousStruct U;//对应下面的 AnonymousStruct 结构

  public IntPtr lpBuffer;//返回的指针,指向_wfs_idc_status 这个结构

}

[StructLayout(LayoutKind.Sequential)]

public unsafe struct _SYSTEMTIME

{

  public UInt16 wYear;

  public UInt16 wMonth;

  public UInt16 wDayOfWeek;

  public UInt16 wDay;

  public UInt16 wHour;

  public UInt16 wMinute;

  public UInt16 wSecond;

  public UInt16 wMilliseconds;

}

[StructLayout(LayoutKind.Explicit)]

public unsafe struct AnonymousStruct

{

  [FieldOffset(0)]

  public UInt32 dwCommandCode;

  [FieldOffset(0)]

  public UInt32 dwEventID;

}

[StructLayout(LayoutKind.Sequential)]

public unsafe struct _wfs_idc_status

{  

  public UInt16 fwDevice;  

  public UInt16 fwMedia;  

  public UInt16 fwRetainBin;  

  public UInt16 fwSecurity;  

  public ushort usCards;  

  public UInt16 fwChipPower;

  [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]

  public string lpszExtra;

}

通过Windows消息机制处理,我从WinForm的Message.LParam中获得了结构体(_wfs_result)的指针并将其转化为了_wfs_result的一个变量,通过这个变量可以正常访问其中定义的字段,唯有:“public IntPtr lpBuffer; //指向结构体(_wfs_idc_status)的指针”使用失败,该如何定义和操作结构体才能正确使用该指针呢?

补充说明:我用WinForm窗体来接收C++编写的DLL发送过来的消息,DLL导出函数中将WinForm窗体句柄传给导出函数,其会将消息传送给WinForm。经测试证明:DLL传过来的消息Message.LParam指针可以转化为对应的结构体,就是该结构体内的指向另结构体的指针出错,不能转化为对于的结构体,但值有的。为什么呢?该如何解决这类问题?

产生错误的代码: Status = (STATUS)Marshal.PtrToStructure(Result.lpBuffer, typeof(STATUS)); 其中Status为STATUS类型的结构体,Result为RESULT类型的结构体变量。

错误提示: 检测到FatalExecutionEngineError 运行时遇到了错误。此错误的地址为 0x661ffc37,在线程 0xe0c 上。错误代码为 0xc0000005。此错误可能是 CLR 中的 bug,或者是用户代码的不安全部分或不可验证部分中的 bug。此 bug 的常见来源包括用户对 COM-interop 或 PInvoke 的封送处理错误,这些错误可能会损坏堆栈。

谢谢你 “sdl2005lyx” 我的C#调用C 动态库DLL 是第三方的是一个准标的,不能被修改。

这个问题我通过5天的攻关,终于解决了。关键是 C# 中 public struct WFSRESULT

这个结构的定义要改变一下:

将原来的 

[StructLayout(LayoutKind.Sequential)]

 public struct WFSRESULT

 {

改为:(关键)

 [StructLayout(LayoutKind.Explicit)]

 public unsafe struct _wfs_result

 {

这种方式的定义就可以了。

文献来源:http://topic.csdn.net/u/20110714/12/5affa2f1-3c47-4817-a73d-7cf29ec75605.html