c++ c#结构体调用

14 篇文章 0 订阅

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#和.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

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
C++C#中,结构体是一种用户自定义的数据类型,可以包含多个不同类型的成员变量。在函数调用中,通过传递结构体指针作为参数,可以实现对结构体的修改。 在C++中,可以通过使用指针作为函数参数来传递结构体。示例代码如下: ```cpp #include <iostream> struct MyStruct { int num; char ch; }; void ModifyStruct(MyStruct* ptr) { ptr->num = 10; ptr->ch = 'A'; } int main() { MyStruct myStruct; ModifyStruct(&myStruct); std::cout << "Modified struct: " << myStruct.num << ", " << myStruct.ch << std::endl; return 0; } ``` 在上述代码中,定义了一个名为`MyStruct`的结构体,在`ModifyStruct`函数中,通过传递结构体指针`ptr`,可以直接修改结构体的成员变量。 在C#中,结构体是值类型,传递结构体参数时会进行值拷贝。为了实现类似于C++中的指针参数传递,可以使用`ref`关键字。示例代码如下: ```csharp using System; struct MyStruct { public int num; public char ch; } class Program { static void ModifyStruct(ref MyStruct myStruct) { myStruct.num = 10; myStruct.ch = 'A'; } static void Main(string[] args) { MyStruct myStruct; ModifyStruct(ref myStruct); Console.WriteLine("Modified struct: {0}, {1}", myStruct.num, myStruct.ch); } } ``` 在上述代码中,通过`ref`关键字将结构体参数传递给`ModifyStruct`函数,可以直接修改结构体的成员变量。 总结来说,C++C#都支持通过结构体指针参数传递来修改结构体的值。在C++中,可以直接使用指针作为参数;而在C#中,需要使用`ref`关键字来实现类似的效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值