在VS中创建和使用动态库
DLL(在基于UNIX的操作系统中也称为 共享库)是最有用的Windows组件之一。
• 可以用来共享代码和资源,并缩小应用程序的大小。
• DLL甚至可以使服务和扩展应用程序变得更加容易。
DLL使用C调用约定。
• 只要平台,调用约定和链接约定匹配,就可以从用其它编程语言编写的应用程序中调用它。
• 客户端应用程序使用隐式链接,其中Windows在加载时将应用程序链接到DLL。通过此链接,应用程序可以调用DLL提供的函数,就像静态链接库中的一样。
创建一个DLL项目
1. 在Vs2017中创建DLL项目
1.1 在菜单栏中,选择 “文件” ->“新建”->“项目”,打开“ 新建项目”对话框。
1.2 选择 “Visual C++”->“Windows桌面”->“动态链接库”,设置文件名称及解决方案名称,选择确定按钮。
2. 向DLL项目添加头文件
2.1 在解决方案资源管理器中打开MathLibrary项目的快捷菜单,然后选择“ 添加” ->“ 新建项”。选择“ 头文件(.h)”。指定头文件的名称(例如MathLibrary.h),然后选择“ 添加”按钮。显示头文件。
2.2 添加头文件代码
#ifdef MATHLIBRARY_EXPORTS
// dllexport dllimport: https://docs.microsoft.com/en-us/cpp/cpp/dllexport-dllimport?view=vs-2017
// __declspec(dllexport) 告诉编译器和链接器从DLL导出函数或变量,以供其它应用程序使用。
#define MATHLIBRARY_API __declspec(dllexport)
// 当未定义MATHLIBRARY_EXPORTS时,
// 例如,当客户端应用程序包含头文件时,MATHLIBRARY_API会将__declspec(dllimport)修饰符应用于声明。
#else
// __declspec(dllimport)该修饰符优化了应用程序中函数或变量的导入
#define MATHLIBRARY_API __declspec(dllimport)
#endif // MATHLIBRARY_EXPORTS
// 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, ...
//初始化斐波那契关系序列
//这样F(0)= a,F(1)= b
//必须先调用此函数
extern "C" MATHLIBRARY_API void fibonacci_init(const unsigned long long a, const unsigned long long b);
//产生序列中的下一个值。
//成功返回true并更新当前值和索引;
//溢出时为false,保持当前值和索引不变。
extern "C" MATHLIBRARY_API bool fibonacci_next();
//获取序列中的当前值。
extern "C" MATHLIBRARY_API unsigned long long fibonacci_current();
//获取当前值在序列中的位置。
extern "C" MATHLIBRARY_API unsigned fibonacci_index();
2.3 在解决方案资源管理器中打开MathLibrary项目的快捷菜单,然后选择“ 添加” ->“ 新建项”。选择“ C++文件(.cpp)”。指定源文件的名称(例如MathLibrary.h),然后选择“ 添加”按钮。显示源文件。
2.4 添加源文件代码
// MathLibrary.cpp : 定义 DLL 应用程序的导出函数。
//
#include "stdafx.h"
#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_;
//初始化斐波那契关系序列
//这样F(0)= a,F(1)= b
//必须先调用此函数
void fibonacci_init(const unsigned long long a,
const unsigned long long b)
{
index_ = 0;
current_ = a;
previous_ = b;
}
//产生序列中的下一个值。
//成功返回true并更新当前值和索引;
//溢出时为false,保持当前值和索引不变。
bool fibonacci_next()
{
// 检查其值及其索引位置是否溢出
if ((ULLONG_MAX - previous_ < current_) ||
(ULLONG_MAX == index_))
{
return false;
}
// 特殊情况下,当index == 0时,仅返回b值
if (index_ > 0)
{
// 否则,计算下一个序列值
previous_ += current_;
}
std::swap(current_, previous_);
++index_;
return true;
}
//获取序列中的当前值。
unsigned long long fibonacci_current()
{
return current_;
}
//获取当前值在序列中的位置。
unsigned fibonacci_index()
{
return index_;
}
- 在菜单栏上,选择 “生成”->“生成解决方案”,编译器会创建一共可供其它程序使用的动态库。
创建使用DLL的客户端应用
DLL使用条件:
- 需要找到声明DLL导出的标头
- 链接器的导入库
- DLL本身
如何实现:
一种解决方案是将所有这些文件复制到您的客户端项目中。
• 对于客户端开发过程中不太可能更改的第三方DLL,此方法可能是使用它们的最佳方法。
• 但是,当您还构建DLL时,最好避免重复。如果制作正在开发的DLL文件的本地副本,则可能会意外地在一个副本中更改头文件,而在另一个副本中更改头文件,或者使用过期的库。
为避免代码不同步,建议您在客户端项目中设置包含路径,以直接从DLL项目中包含DLL头文件。另外,在客户端项目中设置库路径以包括DLL项目中的DLL导入库。最后,将生成的DLL从DLL项目复制到客户端生成输出目录。此步骤允许您的客户应用程序端应用程序使用您生成的相同DLL代码。
属性页操作:
1. 包含头文件:属性页->C/C++ ->常规->附加包含目录下指定.h文件路径
包含头文件后,可编译,不能链接
2. 指定.lib库的依赖项:属性页->链接器->输入->附加依赖项,添加.lib
3. 告诉链接程序如何找到.lib:属性页->链接器->常规->附加库目录
绝对路径(.lib所在完整路径):如C:\Users\Administrator\source\repos\MathLibrarySolution\Debug
或填写相对路径(解决方案名称):..\..\MathLibrarySolution(解决方案名称)\$(IntDir)
此时,可以成功编译并链接,但可能存在找不到DLL问题。
4. 将DLL复制到生成输出目录的命令操作:属性页->生成事件->生成后事件->命令行,输入命令:
xcopy /y /d "..\..\MathLibrarySolution(解决方案名称)\$(IntDir)MathLibrary.dll(.dll文件名称)" "$(OutDir)"
1. 在VS2017中创建客户端应用程序
- 在VS2017中创建客户端应用程序
1.1 新建项目
2. 将DLL表头添加到包含路径
2.1 在菜单栏,选择“项目”,在MathClient的“ 属性页”对话框中,展开“ 配置属性”节点,再展开“ C / C ++”节点,然后选择“ 常规”。在“附加包含目录”,指定MathLibrary.h头文件的位置的路径,或浏览它【浏览到项目路径下即可】。
2.2 点击 “确定”按钮,则现在可以包含MathLibrary.h头文件,并使用它在客户端应用程序中声明的功能。
- MathClient.cpp代码:
// MathClient.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "pch.h"
#include <iostream>
// 属性页: 附加包含目录:选择含有MathLibrary.h的文件目录
#include "MathLibrary.h"
int main()
{
// 初始化斐波那契数列
fibonacci_init(1,3);
do
{
std::cout << fibonacci_index() << ": "
<< fibonacci_current() <<std::endl;
} while (fibonacci_next());
return 0;
}
-
此代码可以编译但不能链接。
还需要做:
(1)指定项目对MathLibrary.lib库的依赖项
(2)告诉链接程序如何找到MathLibrary.lib文件 -
属性页 - >“链接器” -> “输入” ->“附加依赖项”,添加MathLibrary.lib
-
属性页 - >“链接器” -> “常规” ->“附加库目录”,添加MathLibrary.lib所在目录
或相对路径…\MathLibrarySolution(解决方案名称)$(IntDir) -
此时运行,可以成功编译并链接,此时操作系统加载应用程序时,它将查找MathLibrary.dll,如果在某些系统目录,环境路径或本地应用目录中找不到DLL,则加载失败。
即可能出现问题找不到.dll错误
解决方案是:在构建过程中将DLL复制到包含客户端可执行文件的目录中。可以在“生成后事件”添加到项目中,以添加将DLL复制到生成输出目录的命令
操作: 属性页 - >“生成事件” -> “生成后事件” ->命令行,输入以下命令
xcopy /y /d “…\MathLibrarySolution(解决方案名称)$(IntDir)MathLibrary.dll(.dll文件名称)” “$(OutDir)”
此时运行正常