最新要闻

广告

手机

157-156!梅西反超C罗,成为国家队参与进球数全球第1人

157-156!梅西反超C罗,成为国家队参与进球数全球第1人

适合45至55岁创业项目有哪些

适合45至55岁创业项目有哪些

家电

C# 封装 C++的dll

来源:博客园


(资料图)

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平台下编译

关键词: