美文网首页
C/C++ 动态库的生成

C/C++ 动态库的生成

作者: 西贝_贾 | 来源:发表于2020-12-25 17:32 被阅读0次

    1. Windows下生成及使用动态库

    1.1 使用__declspec生成dll

    1. 在菜单栏上,选择“文件”>“新建”>“项目”,打开“创建新项目”对话框 。


      新建dll项目.png
    2. 按步骤新建一个dll工程,此时vs会自动生成一些文件,这是Windows下特有的加速编译的预编译头。
    3. 新建一个头文件MathLibrary.h,内容如下:
    // MathLibrary.h - Contains declarations of math functions
    #pragma once
    
    #ifdef MATHLIBRARY_EXPORTS
    #define MATHLIBRARY_API __declspec(dllexport) //定义一个导出函数名的宏,可以分别用在导出和导入中
    #else
    #define MATHLIBRARY_API __declspec(dllimport)
    #endif
    /*
    DLL 项目的新项目模板会将 PROJECTNAME_EXPORTS 添加到定义预处理器宏 。
    此示例中,Visual Studio 在生成 MathLibrary DLL 项目时定义 MATHLIBRARY_EXPORTS 。
    在项目 “属性->C/C++->预处理器->预处理器定义”中可以看到自动定义的这个宏。
    MATHLIBRARY_API 宏会对函数声明设置 __declspec(dllexport) 修饰符。 
    此修饰符指示编译器和链接器从 DLL 导出函数或变量,以便其他应用程序可以使用它。
    当使用这个dll项目时,由于没有预定义MATHLIBRARY_EXPORTS宏,
    则MATHLIBRARY_API 会将 __declspec(dllimport) 修饰符应用于声明。
    */
    
    // The Fibonacci recurrence relation describes a sequence F
    // where F(n) is { n = 0, a
    //               { n = 1, b
    //               { n > 1, F(n-2) + F(n-1)
    // for some initial integral values a and b.
    // If the sequence is initialized F(0) = 1, F(1) = 1,
    // then this relation produces the well-known Fibonacci
    // sequence: 1, 1, 2, 3, 5, 8, 13, 21, 34, ...
    
    // Initialize a Fibonacci relation sequence
    // such that F(0) = a, F(1) = b.
    // This function must be called before any other function.
    
    #ifdef __cplusplus  // 如果编译器为c++编译器,则采用extern "C"指定导出方式为C语言的方式
    extern "C" {
    #endif // __cplusplus
    
        MATHLIBRARY_API void fibonacci_init(
            const unsigned long long a, const unsigned long long b);
    
        // Produce the next value in the sequence.
        // Returns true on success and updates current value and index;
        // false on overflow, leaves current value and index unchanged.
        MATHLIBRARY_API bool fibonacci_next();
    
        // Get the current value in the sequence.
        MATHLIBRARY_API unsigned long long fibonacci_current();
    
        // Get the position of the current value in the sequence.
        MATHLIBRARY_API unsigned fibonacci_index();
    
    #ifdef __cplusplus
    }
    #endif // __cplusplus
    
    
    1. 添加实现函数,新建cpp文件,复制如下代码:
    // MathLibrary.cpp : Defines the exported functions for the DLL.
    #include "pch.h" // use stdafx.h in Visual Studio 2017 and earlier
    #include <utility>
    #include <limits.h>
    #include "MathLibrary.h"
    
    // DLL internal state variables:
    static unsigned long long previous_;  // Previous value, if any
    static unsigned long long current_;   // Current sequence value
    static unsigned index_;               // Current seq. position
    
    // Initialize a Fibonacci relation sequence
    // such that F(0) = a, F(1) = b.
    // This function must be called before any other function.
    void fibonacci_init(
        const unsigned long long a,
        const unsigned long long b)
    {
        index_ = 0;
        current_ = a;
        previous_ = b; // see special case when initialized
    }
    
    // Produce the next value in the sequence.
    // Returns true on success, false on overflow.
    bool fibonacci_next()
    {
        // check to see if we'd overflow result or position
        if ((ULLONG_MAX - previous_ < current_) ||
            (UINT_MAX == index_))
        {
            return false;
        }
    
        // Special case when index == 0, just return b value
        if (index_ > 0)
        {
            // otherwise, calculate next sequence value
            previous_ += current_;
        }
        std::swap(current_, previous_);
        ++index_;
        return true;
    }
    
    // Get the current value in the sequence.
    unsigned long long fibonacci_current()
    {
        return current_;
    }
    
    // Get the current index position in the sequence.
    unsigned fibonacci_index()
    {
        return index_;
    }
    
    1. 此时已可以生成动态库

    1.2 使用def文件生成动态库

    1. 步骤与使用__declspec类似,几个文件需要修改,头文件中删除下列代码:
    #ifdef MATHLIBRARY_EXPORTS
    #define MATHLIBRARY_API __declspec(dllexport) //定义一个导出函数名的宏,可以分别用在导出和导入中
    #else
    #define MATHLIBRARY_API __declspec(dllimport)
    #endif
    

    在函数声明中删除MATHLIBRARY_API的声明。

    1. 项目添加def文件
      添加->新建项->代码->模块定义文件,输入MathLibrary.def,将下列代码添加到该文件中,第一行定义了dll文件名,exports下指定了要导出的函数名。
    LIBRARY MathLibrary.dll
    EXPORTS
    fibonacci_init
    fibonacci_next
    fibonacci_current
    fibonacci_index
    
    1. 可以用dumpbin查看这两种方法导出的lib和dll文件,观察其中导出函数名一致,这两种都可以生成dll,且没有差别。如果不采用这两种方法,则只会生成dll文件而不生成lib文件,后续调用有差别

    1.3 使用1.1生成的动态库

    1.3.1 隐式调用

    1.3.1.1 隐式调用1

    1. 新建一个win32控制台项目
    2. 将1.1的头文件添加到该项目下,并指定好路径
    3. 此时代码可进行编译,但不能链接。 如果现在生成客户端应用,则错误列表会显示几个 LNK2019 错误。 这是因为项目丢失了一些信息:你尚未指定项目在 MathLibrary.lib 库上有依赖项 。 而且,你尚未告诉链接器如何查找 MathLibrary.lib 文件 。
    4. 要解决此问题,可以直接将库文件复制到客户端应用项目中。 链接器将自动查找并使用它。 但是,如果库和客户端应用都处于开发过程中,则可能会导致一个副本中的更改未在另一个副本中显示。 要避免此问题,可以设置“附加依赖项”属性,告诉生成系统项目依赖于 MathLibrary.lib 。 此外,还可设置项目中的“附加库目录” 路径,使其在链接时包含指向原始库的路径。
    5. 3-4均在项目属性中设置,这样避免后续修改头文件时复制。此时可以编译成功,但运行时提示缺少dll文件,可以复制dll文件到相应目录,也可采用vs自带的命令“生成后事件”中调用复制命令,实现编译完自动复制dll。

    1.3.1.2 隐式调用2

    1. 与1.3.1.1类似,但不需要设置项目属性中的附加依赖项,而是在Client文件中添加预处理命令指定lib路径。
    #pragma comment(lib,"..\\Debug\\MathLibrary.lib")
    

    1.3.2 显式调用

    显式调用的优点是只需要dll文件,不需要lib文件。需要时加载,启动性能好。修改时无需重新链接,便于更新。

    本例中的代码如下,相关解释在代码间:

    #include <Windows.h> // 以前用的是windows.h头文件,win10下没有了
    #include <iostream>
    
    // #include"MathLibrary.h" // 这儿不需要原始的库头文件
    
    int main()
    {
            // 定义指向dll文件中对应函数的指针
        typedef void(*p_finit)(const unsigned long long a, const unsigned long long b);
        typedef bool(*p_fnext)();
        typedef unsigned long long (*p_f_current)();
        typedef unsigned (*p_findex)();
    
        HINSTANCE hdll; //实例化dll的句柄
        hdll = LoadLibrary(LPCSTR("MathLibrary.dll"));
        // 这儿优点坑,此处用LPCSTR,对应需要设置项目的字符集为多字节字符集,
        // 试过用LPCWSTR,对应需用Unicode字符集,但是后者不成功。
        
        // 将函数指针逐一指向对应dll中的函数,后续即可直接使用
        // GetProcAddress获取函数地址
        p_finit fibonacci_init = (p_finit)GetProcAddress(hdll, "fibonacci_init"); 
        p_fnext fibonacci_next = (p_fnext)GetProcAddress(hdll, "fibonacci_next");
        p_f_current fibonacci_current = (p_f_current)GetProcAddress(hdll, "fibonacci_current");
        p_findex fibonacci_index = (p_findex)GetProcAddress(hdll, "fibonacci_index");
    
        // Initialize a Fibonacci relation sequence.
        fibonacci_init(1, 1);
        // Write out the sequence values until overflow.
        do {
            std::cout << fibonacci_index() << ": "
                << fibonacci_current() << std::endl;
        } while (fibonacci_next());
        // Report count of values written before overflow.
        std::cout << fibonacci_index() + 1 <<
            " Fibonacci sequence values fit in an " <<
            "unsigned 64-bit integer." << std::endl;
    
        FreeLibrary(hdll); //使用完成后释放dll句柄
        std::cin.get();
        return 0;
    }
    

    2. Linux下创建及使用动态库

    2.1 创建与隐式调用

    与windows下差别不大,网上有很多参考

    2.2 显式调用

    Linux下显式调用相关的几个文件和函数:

    • 头文件 dlfcn.h,显式加载需要用

    • dlopen(library, RTLD_LAZY/RTLD_NOW)
      第一个参数:指定共享库的名称,将会在下面位置查找指定的共享库。

      1. 环境变量LD_LIBRARY_PATH列出的用分号间隔的所有目录。
      2. 文件/etc/ld.so.cache中找到的库的列表,用ldconfig维护。
      3. 目录usr/lib。
      4. 目录/lib。
      5. 当前目录。

      第二个参数:指定如何打开共享库。

      1. RTLD_NOW:将共享库中的所有函数加载到内存
      2. RTLD_LAZY:会推后共享库中的函数的加载操作,直到调用dlsym()时方加载某函数

      返回值:返回动态库的句柄

    • dlsym(handle, const char)
      调用dlsym时,利用dlopen()返回的共享库的句柄以及函数名称作为参数,返回要加载函数的入口地址。

    • dlclose()
      关闭动态链接库

    • dlerror()
      该函数用于检查调用共享库的相关函数出现的错误。 如果dlerror返回值不为空,则dlsym执行出错

    相关文章

      网友评论

          本文标题:C/C++ 动态库的生成

          本文链接:https://www.haomeiwen.com/subject/mbqinktx.html