• C#调用C/C++ DLL方式


    1、编写一个简单的DLL

    设置为导出函数,并采用C风格。函数前加extern "C" __declspec(dllexport)。定义函数在退出前自己清空堆栈,在函数前加__stdcall。

    新建一个头文件,在头文件中:

    /* 加入任意你想加入的函数定义*/

    extern "C" _declspec(dllexport) int _stdcall add(int *x,int *y); // 声明为C编译、链接方式的外部函数
    extern "C" _declspec(dllexport) int _stdcall sub(int x,int y); // 声明为C编译、链接方式的外部函数

    新建一个.cpp文件

    #include "mydll.h"//貌似这两个头文件的顺序不能颠倒。我试了很多次,但是不能确定。

    int add(int *x,int *y)//是否可以理解为,VS2010已经默认是 _stdcall,所以函数不用添加该修饰符
    {
    return *x+*y;
    }

    int sub(int x,int y)
    {
    return x-y;
    }

    把导出函数名称变为标准名称,需加模块定义文件,就是.def文件。

    内容如下:(需要注释,前面加分号就可以了,注释需要单独行)

    LIBRARY "TEST"

         EXPORTS

              ;add函数

              add

       ;sub函数

             sub

    LIBRARY 库名称

    EXPORTS 需要导出的各个函数名称

         重新编译之后,再用Depends工具看一下,函数已经变成标准add。这个在动态加载时很有用,特别是在GetProcAddress函数寻找入库函数的时候。

    2、静态调用

    新建一个C#的控制台项目,

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Runtime.InteropServices;//添加

    namespace mytest
    {
      class Program
      {
        //----------------------------------------------------------------------------------------------
        [DllImport("mydll.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        extern static int add(ref int a, ref int b);

        [DllImport("mydll.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        extern static int sub(int a, int b);
        //--------------------------------------------------------------------------------------------------

        static void Main(string[] args)
        {

          int a, b,c,d;
          a = 1;
          b = 2;
          c=d= 0;

          Console.WriteLine(add(ref a,ref b).ToString());
          Console.WriteLine(sub(b,a).ToString());
          Console.Read();
        }
      }
    }

     3、动态调用

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Runtime.InteropServices;//添加

    namespace mytest
    {
      class Program
      {

    [DllImport("kernel32.dll")]
    private extern static IntPtr LoadLibrary(String path);//path 就是dll路径 返回结果为0表示失败。
    [DllImport("kernel32.dll")]
    private extern static IntPtr GetProcAddress(IntPtr lib, String funcName);//lib是LoadLibrary返回的句柄,funcName 是函数名称 返回结果为0标识失败。
    [DllImport("kernel32.dll")]
    private extern static bool FreeLibrary(IntPtr lib);

    //声明委托
    [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
    delegate int ADD(ref int x, ref int y);
    [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
    delegate int SUB(int x, int y);

    static void Main(string[] args)
    {

    string dllPath = "G:\VS2010软件学习\c#调用C_dll\mytest\mytest\mydll.dll";
    string apiName1 = "add";
    string apiName2 = "sub";
    //使用动态加载

    IntPtr hLib = LoadLibrary(dllPath);//加载函数

    IntPtr apiFunction1 = GetProcAddress(hLib, apiName1);//获取函数地址
    IntPtr apiFunction2 = GetProcAddress(hLib, apiName2);//获取函数地址

    int i = Marshal.GetLastWin32Error();
    if (apiFunction1.ToInt32() == 0)//0表示函数没找到
    return;
    if (apiFunction2.ToInt32() == 0)//0表示函数没找到
    return;

    //获取函数接口,相当于函数指针
    ADD add1 = (Delegate)Marshal.GetDelegateForFunctionPointer(apiFunction1, typeof(ADD)) as ADD;
    SUB sub1 = (SUB)Marshal.GetDelegateForFunctionPointer(apiFunction2, typeof(SUB));

    // //调用函数
    int a, b,c;
    a = 1;
    b = 2;
    c= 0;
    //add1(ref a,ref b);
    c=sub1(b,a);
    // //释放句柄

    FreeLibrary(hLib );

    Console.WriteLine(c.ToString());
    //Console.WriteLine(add(ref a,ref b).ToString());
    //Console.WriteLine(sub(10,2).ToString());
    Console.Read();

    }

      }
    }

    注意:

    C#时常需要调用C/C++DLL,当传递参数时时常遇到问题,尤其是传递和返回字符串时。VC++中主要字符串类型为:LPSTR,LPCSTR, LPCTSTR, string, CString, LPCWSTR, LPWSTR等,但转为C#类型却不完全相同。

    类型对照:

    C/C++----------C#

    BSTR ---------  StringBuilder

    LPCTSTR --------- StringBuilder

    LPCWSTR ---------  IntPtr

    handle---------IntPtr

    hwnd-----------IntPtr

    char *----------string

    int * -----------ref int

    int &-----------ref int

    void *----------IntPtr

    unsigned char *-----ref byte

    Struct需要在C#里重新定义一个Struct

    CallBack回调函数需要封装在一个委托里,delegate static extern int FunCallBack(string str);

    注意在每个函数的前面加上public static extern +返回的数据类型,如果不加public ,函数默认为私有函数,调用就会出错。

    在C#调用C++ DLL封装库时会出现两个问题:

    1. 数据类型转换问题 
    2. 指针或地址参数传送问题

  • 相关阅读:
    物理层的三种编码方式
    Mysql中eft join、right join、inner join的区别
    Linux常用命令
    Linux中文件颜色所代表的属性和颜色
    phpcms v9 中的数据库操作函数
    NetBeans无法使用编码GBK安全打开文件
    PHP中的一些常用正则表达式
    eureka强制下线上线
    perl(JSON) is needed by mysql-community-test-5.7.30-1.el7.x86_64
    利用TikZ 宏包在 LaTeX 中绘制流程图
  • 原文地址:https://www.cnblogs.com/cyf-besti/p/5325186.html
Copyright © 2020-2023  润新知