动态库分为二种,一种隐式链接,另一种显示调用。不论哪种动态库,本质都是运行时动态加载。
隐式链接:程序运行时,由编译系统自动加载动态库,然后根据程序的引入表进行重定位,当程序退出时自动卸载动态库
显示调用:程序运行时,在需要使用动态库时手动使用LoadLibrary进行加载,当不需要动态库时使用FreeLibrary进行卸载
动态库创建:
1.新建项目,创建项目和解决方案
DynamicLibrary 这是动态库项目
G:\C++Learn\Library Library文件夹用于存放库相关文件,包含静态库与动态库工程和解决方案
DynamicLibraryDemo 动态库解决方案名称
2.点击下一步,出现如下界面
选择DLL,选择导出符号,如果需要使用MFC就勾选上,最后点击完成
3.然后VS左边解决方案管理器会显示相关文件
这就是个最简单的动态库,编译生成下
4.文件夹生成了相关文件
解决方案目录:
解决方案目录下,项目文件夹DynamicLibrary,Debug解决方案调试目录下面就存放了编译生成的 输入库(.lib)和动态链接库(.dll)
回顾下静态库生成,一对比就会发现静态库生成时没有.dll文件
5. 动态库比静态库生成的文件稍显复杂些,这里稍微介绍下
首先看下DynamicLibrary.h,这里由于在第二步时勾选了导出符号,所以VS会默认产生一个与工程同名的.h文件包含
示例代码,导出了相关类和函数帮助我们了解动态库使用
注意:如果没有勾选导出符号,是不会生成DynamicLibrary.h文件的,需要自己进行创建.h进行导出
1 // 下列 ifdef 块是创建使从 DLL 导出更简单的 2 // 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 DYNAMICLIBRARY_EXPORTS 3 // 符号编译的。在使用此 DLL 的 4 // 任何其他项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将 5 // DYNAMICLIBRARY_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的 6 // 符号视为是被导出的。 7 #ifdef DYNAMICLIBRARY_EXPORTS 8 #define DYNAMICLIBRARY_API __declspec(dllexport) 9 #else 10 #define DYNAMICLIBRARY_API __declspec(dllimport) 11 #endif 12 13 // 此类是从 DynamicLibrary.dll 导出的 14 class DYNAMICLIBRARY_API CDynamicLibrary { 15 public: 16 CDynamicLibrary(void); 17 // TODO: 在此添加您的方法。 18 }; 19 20 extern DYNAMICLIBRARY_API int nDynamicLibrary; 21 22 DYNAMICLIBRARY_API int fnDynamicLibrary(void);
7-11行的宏定义在不同工程中起到不同的作用。
在当前动态库工程中由于定义了DYNAMICLIBRARY_EXPORTS宏, 所以这个宏表示的是导出
而在外部程序,虽然使用动态库包含了这个头文件,但是没有定义这个宏,所以这个宏表示的是导入
13-22行用到的宏也是一样的作用,在当前动态库工程中代表的是导出,在外部程序中代表的导入
可能有人会发现工程下文件并没有定义这个宏,为什么会说这个宏己经被定义呢?
答案就是这个宏在VS的预处理器定义了,创建动态库项目时,VS会自动帮你在预处理器中添加一个与工程项目名相同,全部大写后面加上_API的一个宏
当然也可以在头文件中修改宏的名字,但是相应的预处理器定义里也得保持一致
DynamicLibrary.cpp文件
// DynamicLibrary.cpp : 定义 DLL 应用程序的导出函数。
//
#include "stdafx.h"
#include "DynamicLibrary.h"
// 这是导出变量的一个示例
DYNAMICLIBRARY_API int nDynamicLibrary=0;
// 这是导出函数的一个示例。
DYNAMICLIBRARY_API int fnDynamicLibrary(void)
{
return 42;
}
// 这是已导出类的构造函数。
// 有关类定义的信息,请参阅 DynamicLibrary.h
CDynamicLibrary::CDynamicLibrary()
{
return;
}
6. 给CDynamicLibrary类添加一个MathAdd方法,另外添加一个全局函数MathSub. 最后编译生成
DynamicLibrary.h
// 下列 ifdef 块是创建使从 DLL 导出更简单的
// 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 DYNAMICLIBRARY_EXPORTS
// 符号编译的。在使用此 DLL 的
// 任何其他项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将
// DYNAMICLIBRARY_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的
// 符号视为是被导出的。
#ifdef DYNAMICLIBRARY_EXPORTS
#define DYNAMICLIBRARY_API __declspec(dllexport)
#else
#define DYNAMICLIBRARY_API __declspec(dllimport)
#endif
// 此类是从 DynamicLibrary.dll 导出的
class DYNAMICLIBRARY_API CDynamicLibrary {
public:
CDynamicLibrary(void);
int MathAdd(int a,int b);//加法计算(类成员函数)
};
DYNAMICLIBRARY_API int MathSub(int a ,int b);//减法计算(全局函数)
DynamicLibrary.cpp
// DynamicLibrary.cpp : 定义 DLL 应用程序的导出函数。
//
#include "stdafx.h"
#include "DynamicLibrary.h"
//减法计算(全局函数)
DYNAMICLIBRARY_API int MathSub(int a ,int b)
{
return a - b;
}
// 这是已导出类的构造函数。
// 有关类定义的信息,请参阅 DynamicLibrary.h
CDynamicLibrary::CDynamicLibrary()
{
return;
}
//加法计算(类成员函数)
int CDynamicLibrary::MathAdd(int a,int b)
{
return a + b;
}
隐式链接项目创建:
1.给解决方案添加一个新的控制台项目DiskplayLink用于测试静态库,创建完成后设置为启动项目
2.DiskplayLink.cpp添加相关代码
1 // DiskplayLink.cpp : 定义控制台应用程序的入口点。 2 // 3 4 #include "stdafx.h" 5 #include "../DynamicLibrary/DynamicLibrary.h" //动态库头文件 6 7 #pragma comment(lib,"../Debug/DynamicLibrary.lib")//引入动态库.lib文件 8 9 int _tmain(int argc, _TCHAR* argv[]) 10 { 11 //使用动态库中CDynamicLibrary类,调用其中的MathAdd方法 12 CDynamicLibrary dynamicLib; 13 int nResult = dynamicLib.MathAdd(1,2); 14 printf("1 + 2 = %d\r\n",nResult); 15 //调用动态库中的全局函数MathSub 16 nResult = MathSub(5,1); 17 printf("5 - 1 = %d\r\n",nResult); 18 getchar(); 19 return 0; 20 }