• C# 调用C/C++动态链接库,结构体中的char*类型


    用C#掉用C++的dll直接import就可以之前有不同的类型对应,当要传递结构体的时候就有点麻烦了,这里有一个结构体里边有char*类型,这个类型在C#中调用没法声明,传string是不行的默认string是对应const char*,传stringbuilder得指定大小,不然是没法传的,

    查了好久,最后只能用unsafe代码来实现了

    用C/C++写一个标准的动态链接库:

    头文件,定义了三个接口函数,

    #pragma once
    #define  TESTDLL _declspec(dllexport)
    
    #ifndef PKI_DATA_ST
    #define PKI_DATA_ST
    typedef  struct PKI_DATA_st {
        int size;
        char *value;
    }PKI_DATA, *PKI_PDATA;
    #endif
    
    
    extern "C"
    {
        TESTDLL  double  Add(double a, double b);
    
        TESTDLL  double  Subtract(double a, double b);
    
        TESTDLL int TestDn(PKI_DATA cert, PKI_DATA* dn);
        
    }

    简单实现:

    #include "stdafx.h"
    #include "TestClass.h"
    
    
    
     double  Add(double a, double b)
    {
         printf("Add 
    ");
        return a + b;
    }
    
     double  Subtract(double a, double b)
     {
         printf("Subtract");
         return a - b;
     }
    
    
     int TestDn(PKI_DATA cert, PKI_DATA* dn)
     {
         printf("TestDn  cert.value: %s
    ", cert.value);
         dn->size = 10;
         //dn->value =  "helloworld";     //这个种写法需要转换
         dn->value =(char*) TEXT("helloworld");//这种写法在选择unicode字符集时C#是可以直接接受的
         return 1;
     }

    C#调用,路径可以用当前程序的相对路径,也可以是系统的环境变量中的路径,声明import

            [DllImport("USBLoad.dll", EntryPoint = "Add", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
            static extern double  Add(double a,double b);
    
            [DllImport("USBLoad.dll", EntryPoint = "TestDn", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
            static extern int TestDn(PKI_DATA cert, ref PKI_DATA dn);
    
            [DllImport("USBLoad.dll", EntryPoint = "Subtract", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
            static extern double Subtract(double  a, double b);

    声明结构体:

            internal unsafe struct PKI_DATA
            {
                internal int size;
                internal char* value;
            }

    调用:记得属性中勾上允许不安全代码

           [HandleProcessCorruptedStateExceptions]//这个可以捕获C++中的异常 
        [SecurityCritical]
    static unsafe void TestUSBLoad()//加unsafe { try { var a = Add(10, 10); w(a); a = Subtract(15, 10); w(a); PKI_DATA cert = new PKI_DATA(); cert.size = 4; char[] test = "test".ToCharArray(); string s = ""; fixed (char * v=test)//传值初始化。需要fixed关键字 { cert.value = v; PKI_DATA dn = new PKI_DATA(); TestDn(cert, ref dn); var encode = Encoding.ASCII; var c = dn.value; List<char> list = new List<char>(); for (int i=0;i<dn.size;++i,++c) { list.Add(*c); }
                //兼容ansi编码,先转byte
    var bytes = Encoding.Unicode.GetBytes(list.ToArray()); s = Encoding.ASCII.GetString(bytes);//转码 w(new string(dn.value));//设置unicode字符集的情况下可以直接这么写就能得到要字符 } s = s.Replace("","");//转码的时候后边会有很多,移除 w(string.Format("[{0}]",s)); } catch (Exception ex) { w(ex.Message); } }

    顺便研究了下C++的动态调用、

    如果是静态调用需要头文件和lib库,直接引用头文件,加入lib的链接路径,引用后可以直接用,编译就可以了

    动态调用用loadlibrary,记得freelibrary,这些函数在windows.h中,这里建的是QT的项目所以引用的是qt_windows.h:

    #include "qt_windows.h"
    
    #define DLLPATH  (LPCTSTR)"USBLoad.dll"
    
    #ifndef PKI_DATA_ST
    #define PKI_DATA_ST
    typedef  struct PKI_DATA_st {
        int size;
        char *value;
    }PKI_DATA, *PKI_PDATA;
    #endif
    
    
    //声明入口函数 typedef
    double(*Add_Func)(double, double); typedef int(*TestDn)(PKI_DATA, PKI_DATA*); int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); qDebug() << "hello world!"; HINSTANCE m_hModule = LoadLibrary(DLLPATH);//加载dll if (m_hModule == NULL) { printf("LoadLibrary %s failed,err %d ", DLLPATH, GetLastError()); goto END; } Add_Func add; add = (Add_Func)GetProcAddress(m_hModule,"Add");//获取方法地址,后边调用 double c = add(10, 11); printf("Add_Func: %lf ", c); TestDn test; test = (TestDn)GetProcAddress(m_hModule, "TestDn"); PKI_DATA cert; PKI_DATA dn; cert.size = 4; cert.value = (char*)"test"; int res = test(cert, &dn); printf("TestDn:%s ", dn.value); END: if (m_hModule != NULL) { FreeLibrary(m_hModule); m_hModule = NULL; printf("FreeLibrary "); } return a.exec(); }

     关于怎么把char*转码到正确的编码格式下的C#string,

     char* cchar=data   
     IntPtr value = new IntPtr(cchar);
     var s = Marshal.PtrToStringAnsi(value);

    可以用上边的方法,看fcl的源码可以看到上边的方法实质是把char*强转为sbyte*,然后直接new string,这样可以避免C++和C#的byte差异

    marshal还有touni的方法,只转unicode的方法,实质是直接用char* new string,

    char*和sbyte*两种初始化参数string类型都接受。

  • 相关阅读:
    java进阶(36)--IO和Properties联合使用(配置文件)
    java进阶(34)--File类、目录复制
    java进阶(33)--IO流
    java进阶(32)--Collections工具类
    java进阶(31)--TreeSet集合、TreeMap集合、自平衡二叉树
    解决Excel打开空白或慢的问题
    CCS
    CCS
    CCS
    CCS
  • 原文地址:https://www.cnblogs.com/onegarden/p/8527832.html
Copyright © 2020-2023  润新知