1. Windows下生成及使用动态库
1.1 使用__declspec生成dll
-
在菜单栏上,选择“文件”>“新建”>“项目”,打开“创建新项目”对话框 。
新建dll项目.png
- 按步骤新建一个dll工程,此时vs会自动生成一些文件,这是Windows下特有的加速编译的预编译头。
- 新建一个头文件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
- 添加实现函数,新建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.2 使用def文件生成动态库
- 步骤与使用__declspec类似,几个文件需要修改,头文件中删除下列代码:
#ifdef MATHLIBRARY_EXPORTS
#define MATHLIBRARY_API __declspec(dllexport) //定义一个导出函数名的宏,可以分别用在导出和导入中
#else
#define MATHLIBRARY_API __declspec(dllimport)
#endif
在函数声明中删除MATHLIBRARY_API
的声明。
- 项目添加def文件
添加->新建项->代码->模块定义文件,输入MathLibrary.def,将下列代码添加到该文件中,第一行定义了dll文件名,exports下指定了要导出的函数名。
LIBRARY MathLibrary.dll
EXPORTS
fibonacci_init
fibonacci_next
fibonacci_current
fibonacci_index
- 可以用dumpbin查看这两种方法导出的lib和dll文件,观察其中导出函数名一致,这两种都可以生成dll,且没有差别。如果不采用这两种方法,则只会生成dll文件而不生成lib文件,后续调用有差别
1.3 使用1.1生成的动态库
1.3.1 隐式调用
1.3.1.1 隐式调用1
- 新建一个win32控制台项目
- 将1.1的头文件添加到该项目下,并指定好路径
- 此时代码可进行编译,但不能链接。 如果现在生成客户端应用,则错误列表会显示几个 LNK2019 错误。 这是因为项目丢失了一些信息:你尚未指定项目在 MathLibrary.lib 库上有依赖项 。 而且,你尚未告诉链接器如何查找 MathLibrary.lib 文件 。
- 要解决此问题,可以直接将库文件复制到客户端应用项目中。 链接器将自动查找并使用它。 但是,如果库和客户端应用都处于开发过程中,则可能会导致一个副本中的更改未在另一个副本中显示。 要避免此问题,可以设置“附加依赖项”属性,告诉生成系统项目依赖于 MathLibrary.lib 。 此外,还可设置项目中的“附加库目录” 路径,使其在链接时包含指向原始库的路径。
- 3-4均在项目属性中设置,这样避免后续修改头文件时复制。此时可以编译成功,但运行时提示缺少dll文件,可以复制dll文件到相应目录,也可采用vs自带的命令“生成后事件”中调用复制命令,实现编译完自动复制dll。
1.3.1.2 隐式调用2
- 与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)
第一个参数:指定共享库的名称,将会在下面位置查找指定的共享库。- 环境变量LD_LIBRARY_PATH列出的用分号间隔的所有目录。
- 文件/etc/ld.so.cache中找到的库的列表,用ldconfig维护。
- 目录usr/lib。
- 目录/lib。
- 当前目录。
第二个参数:指定如何打开共享库。
- RTLD_NOW:将共享库中的所有函数加载到内存
- RTLD_LAZY:会推后共享库中的函数的加载操作,直到调用dlsym()时方加载某函数
返回值:返回动态库的句柄
-
dlsym(handle, const char)
调用dlsym时,利用dlopen()返回的共享库的句柄以及函数名称作为参数,返回要加载函数的入口地址。 -
dlclose()
关闭动态链接库 -
dlerror()
该函数用于检查调用共享库的相关函数出现的错误。 如果dlerror返回值不为空,则dlsym执行出错
网友评论