Windows下静态库和动态库的制作与使用

静态库:
静态库:指在我们的应用中,有一些公共代码是需要反复使用,就把这些代码编译为“库”文件;在链接步骤中,连接器将从库文件取得所需的代码,复制到生成的可执行文件中的这种库。

静态库生成的lib文件:多个obj文件的合集

使用静态库步骤,配置工程属性链接静态库

1拷贝头文件,包含头文件到要使用的工程项目中

2把lib添加到工程,下面2种方式都可以

2.1 添加工程引用的lib文件名:工程---属性---配置属性---链接器---输入---附加依赖项:加上lib文件名(这种方式得把lib文件拷贝到当前目录下)

2.2 #pragma comment(lib, "..\\debug\\staticlib.lib")//和2.1是一个意思,只是用代码方便些(这种方式lib文件在当前目录就不需要写相对路径或绝对路径,我这个例子写的是相对路径)

#include"makestatic.h"
#include<iostream>
#pragma comment(lib,"makestatic.lib")//这个和附加依赖项是一样,指定库目录中的文件(这是用代码添加)
using namespace std;
int main()
{
int ret = test(10, 20);
cout << ret << endl;
system("pause");
return 0;
}
静态库使用缺点
每次使用都要重新编译
多个程序使用同一个静态库,每个程序中都会有一份静态库代码的拷贝(运行之前(编译期间)会加载所有的代码),会造成磁盘的资源的浪费
 

动态库
动态库的优点:使用DLL文件的好处是程序不需要在运行之初加载所有代码,当被多个程序调用时只在内存中生成和使用同一个实例,使用DLL文件还可以减小程序的体积

动态库生成的lib文件:存放是是dll的名字和函数名,这样好通过lib文件用LoadLibrary和GetProcAddress去获取函数地址调用

动态库生成的dll文件:存放可供使用的函数、变量、代码、类

 

生成动态库的两种方式动态库:

1用__declspec(dllexport)声明为导出函数

使用动态库的时候得要__declspec(dllimport)声明导入函数

如果要复用头文件一般如下声明,这样的话在使用的时候就可以生成一份.h文件就可以了

#ifdef DLL_EXPORTS
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif // DLL_EXPORT
例子(宏是自己定义的):

函数声明导出:DLL_API int MySub(int nVal1, int nVal2);

注意类声明为导出语法不一样关键字要写在类后面:class DLL_API CMyTest

如果动态库需要兼容c/c++和重用一份头文件可以这样写

#ifdef DLL_EXPORTS
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif // DLL_EXPORT


#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

DLL_API int MyAdd(int nVal1, int nVal2);
DLL_API extern int g_nValTest;

#ifdef __cplusplus
}
#endif // __cplusplus
2利用def文件导出函数来创建动态库不需要使用关键字(__declspec(dllexport))声明,只需要在选中添加新文件-代码--def文件

注意点:def生成的动态库 隐式加载除了全局变量要使用关键字声明其他的都可以不使用__declspec(dllimport)声明

 def文件中,按照指定格式填写需要导出的函数信息

/*
def导出
def为了解决跨编译器使用, 导出函数的名字为函数本身, 不进行名称粉碎,这样的话使用动态加载函数名称只需要写同一个就可以了,就不需要在不同的编译器下写不同的名称粉碎的名称
多个进程加载同一个dll, 如果dll导出全局变量, 某个进程对全局变量的修改不会影响另一个进程.这里使用了写时拷贝技术 + 引用计数.
def文件导出格式
LIBRARY
EXPORTS
导出的函数或变量名
*/

LIBRARY
EXPORTS
MyAdd = MySuperAdd @5 //也可以不加序号@5 或者不复制 直接写导出名也可以
g_nVal
??0CMyTest@@QAE@XZ //导出类时,需要填写所有类的函数导出名
??1CMyTest@@QAE@XZ
?Test@CMyTest@@QAEXXZ


//特别说明:使用格式

entryname[=internalname] [@ + ordinal [NONAME]] [DATA] [PRIVATE]
导出函数名 实际函数名 指定导出序号 不导出名字 数据格式 只允许动态加载使用
动态库的使用(两种方式):

1隐式加载动态库

把.dll和.lib加上.h文件复制出来,拷贝到你要链接的那个工程中,把头文件加载到你的工程项目中,记得头文件的声明要改为__declspec(dllimport) 声明,(最好是定义一个宏去替代导出和导入,这样只需要用同一个头文件就可以了
,之后在.c文件中就可以调用动态库的函数了

最后一步就是配置动态库了,点击属性——配置属性——链接器——附加在项目——编辑——添加你生成的.lib文件名,把.lib文件放到.c文件目录下(或者用#pragma comment(lib, "..\\debug\\staticlib.lib"))

如果没有把.lib文件放入到.c目录下,会报无法打开.lib文件错误

运行一下如果没有成功就把生成的.dll文件放到自己的工程文件中,如果是直接双击exe程序的话那当前exe程序的目录就是当前目录

2显示加载动态库

通过LoadLibrary和GetProcAddress两个api加载动态库

代码例子如下

#include <windows.h>

typedef int(*PFN_MYADD)(int, int);

int main()
{

//DLL模块加载到内存中,dll映射到当前进程使用的地址空间
//返回值:句柄,即dll在内存中的首地址
HMODULE hMod = LoadLibrary(TEXT("..\\Debug\\Dll.dll")); //参数指定要载入的动态链接库的名称


//获取已经加载的DLL的句柄,如果dll没有加载到内存中获取会失败,这个可以获取系统提供dll模块句柄,
//因为系统的dll模块会默认调用LoadLibrary加载进进程中,例如获取kernel32.dll模块句柄
HMODULE hMod1 = GetModuleHandle(TEXT("..\\Debug\\Dll.dll"));//此时获取的句柄LoadLibrary
//是一样的,此时用GetProcAddress填的第一个参数是hMod 和hMod1都可以
if (hMod0 == NULL)
{
printf("GetModuleHandle fail\n");
}
//获取的是主模块句柄(调用这个函数的进程句柄),如果这个函数放在dll中,获取的也是加载dll的进程句
//柄
HMODULE hMod2 = GetModuleHandle(NULL);//此时参数为null,获取的是主模块句柄


/*
函数原型:
FARPROC GetProcAddress(
HMODULE hModule, // DLL模块句柄
LPCSTR lpProcName // 函数名或者用序号
);
*/
PFN_MYADD pfnMyAdd = (PFN_MYADD)GetProcAddress(hMod, "?MyAdd@@YAHHH@Z");
int nNum = pfnMyAdd(1, 2);

FreeLibrary(hMod);

return 0;
}
调用已经加载的DLL模块的未导出函数
未导出函数的偏移量获取方式:在工程目录下的.map文件中查找
.map文件生成:项目-属性-连接器-调试-生成映射文件
HMODULE hMod = LoadLibrary(TEXT("Dll.dll"));

PFN_TEST pfnTest = (PFN_TEST)((int)hMod + 0x11700);
//可根据加载的DLL模块句柄 + 函数在DLL中的偏移量来获取
 

隐式加载动态库
  优点:实现起来比较简单,只需要用#pragma comment包含一下动态库生成的.lib符号表,就会在程序运行的时候自动会把dll所有的函数加载到内存中,在程序中随时调用dll中的任意一个函数,不要把这个想成静态库了,当多个程序都隐式加载的时候,dll一样还是只有一份实例
  缺点:这样就会有和静态库同样的缺点,假如你的exe需要加载多个dll的话,程序在启动时把这些dll都加载到内存中会加大程序的启动时间,并且一般情况只会在不同的条件下才会访问dll中的其中一个函数,这样的话用隐式调用就会造成资源的浪费
  而且隐式加载也是使用显示加载动态库的方式,利用LoadLibrary和GetProcAddress两个api加载所有的dll函数

显示加载动态库
  优点:自己需要那些dll函数就用LoadLibrary和GetProcAddress两个api获取相对应的函数,这样既提高程序启动效率又不会浪费资源,显示加载具有更好的灵活性,能更加有效的使用内存,在编写大型程序时往往使用显示加载方式。
  缺点:代码写起来比较繁琐

.dll加载的目录顺序
  1) 当前目录
  2) 系统目录
  3) 环境变量
————————————————
版权声明:本文为CSDN博主「code_greenhand」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_35426012/article/details/72444536

Windows下静态库和动态库的制作与使用

上一篇:Ubuntu下Memcache的安装与基本使用


下一篇:win10自带ssh server使用