C#调用C++dll 结构体参数传递问题

时间:2022-08-31 09:19:56

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; /*/< authentiation adjust checking disable */
  int badtag_rej; /*/< reject packet if it is bypassed due to badtag */
  int pad_en; /*/< pad non-rejected packets up to 64B */
  int byp_ctl_sl; /*/< bypass packet if SL field does not correspond to packet len */
  int byp_ctl_v; /*/< bypass packet if V bit is set */
  int byp_ctl_sc; /*/< bypass packet if SC bit and either ES or SCB bits are set */
  int byp_ctl_ec; /*/< bypass packet if DC bits are not 00 or 11 */
  int sectag_flag; /*/< select which flag bit indicates that a SEC tag was present in pkt */
} 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; /*/< authentiation adjust checking disable */
        int badtag_rej; /*/< reject packet if it is bypassed due to badtag */
        int pad_en; /*/< pad non-rejected packets up to 64B */
        int byp_ctl_sl; /*/< bypass packet if SL field does not correspond to packet len */
        int byp_ctl_v; /*/< bypass packet if V bit is set */
        int byp_ctl_sc; /*/< bypass packet if SC bit and either ES or SCB bits are set */
        int byp_ctl_ec; /*/< bypass packet if DC bits are not 00 or 11 */
        int sectag_flag; /*/< select which flag bit indicates that a SEC tag was present in pkt */
        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; /*/< Index to the context to use */
  int sec_level; /* < Security Level to validate frames per context based */
  int drop_maxpn; /* < Enable packet drop when max pn is reached for this context>*/
  int drop;    /*/< Drop this Packet */
  int redir;   /*/< For Egress, redirect the packet to ingress path (NDL). For Ingress, redirect the packet to alternate destination. */
  int auth_en; /*/< Encapsulate and authenticate this packet. */
  int enc_en;  /*/< Encrypt this packet. auth_en must also be set when this bit is set. (Valid only for egress path). */
} ACT_FLD;


typedef struct {
  ACT_FLD   *lk_act_fld;   /*/< Action to take for an entry within a port */
} 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; /*/< Index to the context to use */
        public int sec_level; /* < Security Level to validate frames per context based */
        public int drop_maxpn; /* < Enable packet drop when max pn is reached for this context>*/
        public int drop;    /*/< Drop this Packet */
        public int redir;   /*/< For Egress, redirect the packet to ingress path (NDL). For Ingress, redirect the packet to alternate destination. */
        public int auth_en; /*/< Encapsulate and authenticate this packet. */
        public int enc_en;  /*/< Encrypt this packet. auth_en must also be set when this bit is set. (Valid only for egress path). */
        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++dll 结构体参数传递问题



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