C/C++ 动态库的生成

2020-12-25  本文已影响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下显式调用相关的几个文件和函数:

上一篇下一篇

猜你喜欢

热点阅读