zoukankan      html  css  js  c++  java
  • 动态库

    1 动态库的特点

    (1)运行时独立存在

    (2)不会链接到执行程序

    (3)使用时加载(使用动态库,必须使动态库执行)

    2 动态库和静态库的比较

    (1)由于静态库使将代码嵌入到使用程序中,多个程序使用时,会有多份代码,所以代码体积会增大,动态库的代码只需要存在一份,其他程序通过函数地址使用,所以代码体积小

    (2)静态库发生变化后,新的代码需要重新链接嵌入到执行程序中(需要重新编译),动态库发生变化后,如果库中的函数的定义(或地址)未变化,其他使用dll的程序不需要重新链接(直接运行即可,不需要重新编译)。

    (3)动态库编译链接后,也会生成lib文件,是作为动态库函数映射使用,与静态库不相同

    3 动态库的创建

    3.1 使用__declspec(dllexport)导出函数

    (1)应用程序选择dll,选择空项目

    (2)添加头文件和源文件

    头文件如下:

    #ifndef _DLLTEST_H_
    #define _DLLTEST_H_
    
    extern "C" __declspec(dllexport) void MyPrint();
    
    #endif // _DLLTEST_H_

    源文件如下:

    #include "dlltest.h"
    
    #include <iostream>
    
    void MyPrint()
    {
        std::cout << "it is myprint function" << std::endl;
    }

    注:当使用显示链接(dll函数的使用方法之一)的时候动态库导出函数前面要加extern "C",因为函数名会被编译器改变,导致GetProcAddress函数找不到函数

    (3)测试程序如下

    #include "stdafx.h"
    #include <windows.h>
    
    int main()
    {
        HMODULE hmodule  = LoadLibrary("dlltest.dll");
        if (hmodule)
        {
            typedef void(*LoadProc)();
            LoadProc loadproc = (LoadProc)GetProcAddress(hmodule, "MyPrint");
            if (loadproc)
            {
                loadproc();
                FreeLibrary(hmodule);
            }
            else
            {
                FreeLibrary(hmodule);
            }
        }
        return 0;
    }

    注:测试程序使用显示链接方式加载动态库

    3.2 使用模块定义文件.def

    (1)应用程序选择dll类型,选择空项目

    (2)添加头文件和源文件

    头文件如下:

    #ifndef _DEFTEST_H_
    #define _DEFTEST_H_
    
    void MyPrint();
    
    #endif //_DEFTEST_H_

    源文件如下:

    #include "deftest.h"
    
    #include <iostream>
    
    void MyPrint()
    {
        std::cout << "def test function" << std::endl;
    }

    (3)添加def文件

     

    (4)编写def文件内容,def文件内容的格式如下:

    LIBRARY DllName
    EXPORTS
    Insert @1
    Delete @2
    Member @3
    Min @4

    注:

    (1).def 文件中的第一条 LIBRARY 语句不是必须的,但LIBRARY 语句后面的 DLL 的名称必须正确,即与生成的动态链接库的名称必须匹配。此语句将 .def 文件标识为属于 DLL。链接器将此名称放到 DLL 的导入库中。

    (2)EXPORTS 语句列出名称,可能的话还会列出 DLL 导出函数的序号值。通过在函数名的后面加上 @ 符和一个数字,给函数分配序号值。当指定序号值时,序号值的范围必须是从 1 到 N,其中 N 是 DLL 导出函数的个数。

    def文件的内容如下:

    LIBRARY deftest
    
    EXPORTS
        MyPrint @1

    (5)测试动态库,测试程序如下:

    #include "stdafx.h"
    #include <windows.h>
    
    int main()
    {
        HMODULE hmodule  = LoadLibrary("deftest.dll");
        if (hmodule)
        {
            typedef void(*LoadProc)();
            LoadProc loadproc = (LoadProc)GetProcAddress(hmodule, "MyPrint");
            if (loadproc)
            {
                loadproc();
                FreeLibrary(hmodule);
            }
            else
            {
                FreeLibrary(hmodule);
            }
        }
        return 0;
    }

    4 动态库的使用,动态库的使用方法有两种,一种隐式链接,一种显示链接,上面的测试程序使用的都是显示链接

    4.1 显示链接动态库

    (1)加载动态库

    HMODULE LoadLibrary(LPCTSTR lpFileName  /*动态库文件名或全路径*/
    ); //返回DLL的实例句柄(HINSTANCE)

    (2)使用typedef定义函数指针类型

    (3)获取函数的绝对地址

    FARPROC GetProcAddress(
            HMODULE hModule,    //DLL句柄
            LPCSTR lpProcName   //函数名称 //可以查出来导出来的相对地址
        ); //成功返回函数地址

    (4)使用函数

    (5)卸载动态库

    BOOL FreeLibrary(
            HMODULE hModule   //DLL的实例句柄
        );

    注:具体使用方法见上述测试程序

    4.2 隐式链接动态库

    (1)将lib文件,dll文件,头文件拷贝到测试项目目录下

    (2)包含头文件,lib文件(可在项目设置链接器里加,也可通过代码显示加载,具体方法见静态库的加载使用)

    (3)直接使用库中函数

    注:也可不包含头文件,使用语句__declspec(dllimport) void MyPrint();告诉编译器函数声明,一般会在头文件通过条件编译设置不同的值,如下:

    #ifndef DLL_H_
    #define DLL_H_
    
    #ifdef DLLProvider
    #define DLL_EXPORT_IMPORT __declspec(dllexport)
    #else
    #define DLL_EXPORT_IMPORT __declspec(dllimport)
    #endif
    
    DLL_EXPORT_IMPORT int add(int ,int);
    
    #endif

    但一般我们在使用过程中,不需要添加__declspec(dllimport) 调用一般函数也可以,所以上面的测试程序就没有使用__declspec(dllimport)导入函数,那是不是__declspec(dllimport)没啥用?

    当然不是,当我们使用动态库的类中静态函数时,必须使用__declspec(dllimport)导出函数,否则会出现无法解析的外部符号,一般动态库包含类时,都需要条件编译区分导入导出宏,因为只是记录动态库的基础知识,故不深究,以后有时间再记录这部分的知识。

    测试程序很简单,如下:

    #include "stdafx.h"
    
    // #include "deftest.h"
    
    __declspec(dllimport) void MyPrint();
    
    int main()
    {
        MyPrint();
        return 0;
    }

    注:我已经在项目属性中的链接器里加上lib库,故在代码里没有加载静态库的代码(#pragma comment语句)

    (4)隐式链接的情况,dll可以存放的路径:与执行文件在同一个目录下(建议使用),当前工作目录,windows目录,windows/system32目录,windows/system目录,环境变量path指定目录

    4.3 隐式链接和显示链接对比

    隐式链接,动态库是在程序启动时被加载,当dll不存在时,程序无法启动

    显示链接,动态库只在使用loadlibrary函数,才会被加载

    建议:调用大公司的库,使用隐式链接,小公司的库使用显示链接

    4 dll中类的使用,前面的测试程序都是直接使用函数,当在dll中声明定义类时,也使用_declspec(dllexport) 导出,如下:

    class _declspec(dllexport) CMath {
        ...
        }; //导出类的成员函数
    //类的声明给用户看,类的实现不给用户看。

    当使用dll中的类时,使用方法相同,先导入dll中lib,然后定义类的实例,调用类中的函数

    5 显示链接动态库注意事项

    (1)LoadLibrary加载动态库失败,一般为文件路径错误和字符串格式(项目编码方式使用多字节方式,也可使用TEXT宏包含字符串)问题

    (2)GetProcAddress返回空,找不到函数地址,在dll中的函数定义前加上 extern ”C“即可(因为函数名字被编译器改变产生的问题)

    (3)64位进程和32位的动态库不可互相调用,当然,32位的进程和64位的动态库也不可调用,两者版本需要统一

    6 静态库lib和动态库输出的lib的区别

    静态库的lib:lib包含函数代码本身(即包括函数的索引,也包括实现),在编译时直接将代码加入程序当中

    动态库的lib:lib包含了函数所在的dll文件和文件中函数位置的信息(索引),函数实现代码由运行时加载在进程空间中的dll提供

  • 相关阅读:
    OpenERP Framework API存档
    OpenERP 7 picking order 继承需要注意的地方
    Unity战斗系统之AI自主决策
    简易2D横版RPG游戏制作
    UGUI之Canvas Group
    UGUI之Canvas和EventSystem
    NGUI之scroll view的制作和踩坑总结
    NGUI之Toggle实现单选框
    Unity中对象池的使用
    继承MonoBehaviour类的优缺点和相关报错
  • 原文地址:https://www.cnblogs.com/LuckCoder/p/11208158.html
Copyright © 2011-2022 走看看