zoukankan      html  css  js  c++  java
  • C++动态链接库

    1、动态链接库概述:

    • 动态链接库通常都不能直接运行,也不能接受消息;只有在其他模块调用动态链接库中的函数时,它才发挥作用。
    • Windows API中所有的函数都包含在动态链接库中。
    • 动态链接库分静态库和动态库。

    2、导出函数的声明方式

    1)强制用C语言方式进行修饰,且用C的默认调用约定,即__cdecl方式。这种方式编译产生的DLL中有一个导出函数:add,不加任何修饰。(这种方式最好)

    extern "C" int __declspec(dllexport) add();

    2)强制用C语言方式进行修饰,且用__stdcall约定。这种方式编译产生的DLL中有一个导出函数:_add@0,即前面有“_”,后面加了参数长。

    extern "C" int __declspec(dllexport) __stdcall add();

    3)不强制用C语言方式进行修饰,但是用__stdcall约定。这种方式编译产生的DLL中有一个导出函数:?add@@YGHXZ。这个名字很怪,后面的不好理解。

    int __declspec(dllexport) __stdcall add();

    4)不强制用C语言方式进行修饰,并且用 __cdecl 约定。这种方式编译产生的DLL中有一个导出函数:?add@@YAHXZ。注意看,和第三种方有一点不同。

    int __declspec(dllexport) __cdecl add();

      对于DLL导出函数声明的四种写法,在动态调用时, 声明成第一种方式是最好的。但是,C/C++缺省的调用约定为__cdecl约定,如果想别的语言能用DLL的话,最好是将调用约定写成__stdcall方式(不能动态调用),然后静态(隐式)调用。
      在隐式调用时,四种声明方式都是可以的,只要调用者的声明方式和DLL声明时的方式一致即可。

    3、动态链接库的创建:

      我们以第一种声明方式对函数进行声明,要导出的函数的形式为:

    extern "C" int _declspec(dllexport) add()
    {
          return 5;   
    }

    为解决名字改编问题,可为工程添加一个模块定义文件.def

    LIBRARY 
             MFC-Add
    
    EXPORTS 
             add@0 ;函数名@参数长    

    要导出的类的形式为:

    class __declspec(dllexport) Point
    {
    public:
          void OutPut(int x, int y);
    };

      编译之后会生成:一个DLL(.dll)文件、一个引入库(.lib)文件等

      注:若在声明类时,指定了导出标志,那么该类中的所有函数均被导出;否则只导出那些声明时指定了导出标志的类成员函数

    4、动态链接库的加载:

     (1) 隐式加载

    利用 extern 或 __declspec(dllimport) 声明外部函数(最好将此声明写在一个头文件*.h中)

    extern "C" int _declspec(dllimport) add();

    导入对应的引入库(.lib)文件:在项目“设置”中链接lib库 或 在源文件中使用代码链接lib库:

    #pragma comment(lib, "*.lib")

    将动态链接库文件所在目录添加到系统的环境变量path中。

      附:Qt中库的隐式加载:  

      VC的引用库文件为xxx.lib, GCC的为xxx.a,通过比较两种库文件的格式,发现很相似,于是把xxx.dll,xxx.lib和xxx.h复制到Qt的project下,直接把xxx.lib改为xxx.a, 根据Qt的库名字的格式, 在xxx.a的前面加上lib, 即为libxxx.a。再在Qt的.pro文件中最后面加上

    LIBS += -L. –lxxx //增加当前目录下的libxxx.a

      先在pro文件中加入一行,LIBS += -L ./ -lPlotDll,然后将PlotDll.dll和PlotDll.lib拷贝到工程build目录下(就是含有Makefile的目录),再重新编译一次即可。

     (2) 显式加载

    将指定的可执行模块映射到调用进程的地址空间

    #include <Windows.h>
    HINSTANCE handlerDLL = NULL;
    handlerDLL = LoadLibrary(_T("..\..\MFC-Add\Debug\MFC-Add.dll"));

    获取该动态链接库中导出函数的地址

    typedef int(__stdcall *ADDPROC)(int a,int b);//声明ADDPROC为指向函数的指针类型,函数返回值类型为int; _stdcall即标准调用约定
    ADDPROC AddProc = NULL;
    AddProc = (ADDPROC)GetProcAddress(handlerDLL,_T("add"));

    调用导出函数

    AddProc(5,6);

    当不需要访问dll时,释放对dll的引用

    FreeLibrary("..\..\MFC-Add\Debug\MFC-Add.dll");

    Qt中的动态调用方式,代码:

    //动态加载
    QLibrary hDll(".\Libs\GHC02DLL.dll");
    if(hDll.load())
    {
        typedef int (__stdcall *ADDFunc)();
        ADDFunc Add=(ADDFunc)hDll.resolve("fnGHC02DLL");
    
        QMessageBox::about(this,"about",QString::number(Add(),10));
    
        hDll.unload();
    }

    注:

    • Dumpbin 和 Depends 工具
    • 调用约定:__stdcall:标准调用约定即WINAPI调用约定,也就是pascal调用约定,非C调用约定
    • 名字改编:C++编译器在生成DLL时,会对导出函数进行名字改编,且不同的编译器使用的改编规则不同

      相关链接http://www.cnblogs.com/DxSoft/archive/2011/04/22/2024686.html

    =======================================================================
    中文名:高洪臣
    英文名:Gordon Scott
    E-mail:gaohongchen01@163.com
    =======================================================================
  • 相关阅读:
    六、Oracle的数据库管理及数据字典和动态视图
    八、Oracle的数据完整性
    js回调函数
    PL/SQL编程(三)
    四、Oracle的复杂查询
    三、Oracle的简单查询
    C#实现双向链表
    什么是Unix时间戳 [转]
    ANSI escape sequences
    Gibbs Sampling [转]
  • 原文地址:https://www.cnblogs.com/gaohongchen01/p/3969583.html
Copyright © 2011-2022 走看看