C# 封装 C++的dll

本文详细介绍了C#程序如何引用C++DLL,强调了平台一致性、数据类型映射的重要性,包括char*和char[]数组、Handle类型、结构体转换等内容,以及遇到的常见问题如堆栈不对称和平台不匹配的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C# 的程序引用C++的dll时,首先要保证两者基于的平台一致,比如都是x64,或者都是x86的程序,否者两者之间不能直接调用,然后,要保证两者的数据类型可以相互识别,相互通用。在此重点介绍几个常用的数据转换。

  1. C++的char* 和 char[] 数组,对应到C#的string 类型
  2. C++的Handle 类型,一般是一个很大的整数,C#可以使用 IntPtr
  3. 对于指针类型的要使用ref
  4. C++中的结构体,可以在C#中声明同样名称的结构体,但是要进行一定的设置
    1. 使用StructLayout 特性设置结构体,当有char时,要设置 CharSet = CharSet.Ansi
    2. 使用string时,要使用 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)] 特性修饰,并分配与原c++中的char数组相同大小的内存

使用DllImportAttribute(string dllPath) 来构造C++的同名函数,此特性有几个常用字段

    • CharSet 指示如何向方法传送字符串参数,并控制名称重整
      • None 与 Ansi具有相同的行为
      • Ansi 以单字节的形式封装传送字符串
      • Auto针对操作系统自动封装字符串
      • Unicode 以双字节的形式封送字符串
    • CallingConvention 指示入口点的调用规则
      • Cdecl 调用方清理堆栈
      • StdCall 被调用方清理堆栈
      • ThisCall 第一个参数是this指针
      • Winapi 默认平台调用约定

结构体举例1:

C++结构体
struct SGP_ANALYTIC_TEMP
{
    int rule_id;     
    char rule_name[50];   
    float max_temp; 
}; 
 
C#对应的结构体声明
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct SGP_ANALYTIC_TEMP
{
    public int rule_id = 0;    

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
    public string rule_name="123";   

    public float max_temp = 0;//最高温度值

}

结构体举例2:结构体中嵌套结构体,此时对于结构体数组,也要使用MarshalAs指定类型,并分配内存

C++结构体
struct SGP_GENERAL_INFO
{       
       int range_num;//测温范围数量
       
       SGP_RANGE range[3];//测温范围
       
       char vl_rtsp_url[50];//可见光主码流rtsp地址
};

C++ 函数类型 
int SGP_GetGeneralInfo(SGP_HANDLE handle, SGP_GENERAL_INFO *output);

C# 对应的结构体
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct SGP_GENERAL_INFO
{

    
    public int range_num ;//测温范围数量
   
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
    public SGP_RANGE[] range;//测温范围

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
    public string vl_sub_rtsp_url;//可见光辅码流rtsp地址
}

C# 函数类型
[DllImport(sdkPath, EntryPoint = "SGP_GetGeneralInfo", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern int SGP_GetGeneralInfo(IntPtr handle, ref SGP_GENERAL_INFO output);

遇到的问题

问题1 :

在引入DLL中的方法时,有时会出现以下错误报告:“在使用托管代码调用非托管代码时,发生“ 对 PInvoke 函数的调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配。请检查 PInvoke 签名的调用约定和参数与非托管的目标签名是否匹配。

解决方法:

在DllImport时添加 CallingConvention = CallingConvention.Cdecl 即调用方清理堆栈,注意,当调用的C++dll还依赖其他dll时,需要把所调用的dll文件都放在一起,否则会抛出 System.DllNotFoundException的异常

问题2:

解决方法:

主要是由于Dll编译的平台和调用它的工程的平台不一致导致的,可都统一打到X64平台下编译

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值