WINDOWS静态库与动态库

一 静态库:

编译静态库时,只会产生.lib文件。所有数据都在lib文件中。静态库的使用方式只有一种,即静态载入,在程序编译链接阶段,会将静态库中的所有数据都链接合并到最终生成的exe文件中,链接完成后就不再需要静态库文件,这样方便程序移植,但是也带来程序臃肿过大的弊端。同时,如果静态库中函数有所变化,需要重新编译工程来生成新的exe文件,过程较为麻烦。

二 动态库:

编译动态库时,会产生.lib文件和.dll文件。lib文件只包含函数或者变量的地址信息,而其具体实现都在dll文件中。编译链接阶段只需要.lib文件中的数据,与dll文件无任何关系,程序运行时才会用到dll文件,与lib文件无任何关系。动态库的使用方式包括静态载入和动态载入,静态载入在程序启动时就载入dll文件,动态载入在需要时使用函数LoadLibrary再手动载入dll文件,载入dll文件的时机更加随机而不会都在程序启动时一起载入,对于程序中有使用大量dll文件时尤为有效,减少了程序启动的时间,在特定条件下载入特定的dll文件,分散dll文件的载入时间。同时,如果动态库中函数有所变化,只需要用新的dll文件替换掉旧的dll文件即可,exe文件无需重新编译生成,非常适合库中函数需要经常更新的场合。

三 解决方案TestLibrary

下面结合一个具体的工程来做解释。解决方案为TestLibrary,TestLibary目录中内容如下:

在这里插入图片描述

文件夹TestDll、TestLib、UseDll、UseLib是四个工程的主目录,分别用来生成动态库生成静态库使用动态库使用静态库,各个工程的输出文件路径都设置为Output

1 工程TestLib

有两个文件TestLib.hTestLib.cpp

TestLib.h内容:

// 注意:静态库导出函数无需使用extern "C" __declspec(dllexport)修饰

void TestLibFunc1();
void TestLibFunc2();

TestLib.cpp内容:

#include "TestLib.h"
#include <iostream>

void TestLibFunc1()
{
    
    
	std::cout << "TestLibFunc1" << std::endl;
}

void TestLibFunc2()
{
    
    
	std::cout << "TestLibFunc2" << std::endl;
}

编译后只生成TestLib.lib文件。

2 工程TestDll

有两个文件TestDll.hTestDll.cpp

TestDll.h内容:

// 注意:动态库导出函数一定要使用extern "C" __declspec(dllexport)修饰

// 如果不使用__declspec(dllexport)进行修饰,
// 编译时不会生成.lib文件。

// 如果不使用extern "C"进行修饰,会以C++的语法进行编译,
// 编译后的符号就不直接是原来的函数名而是会再加上了其他符号以支持C++的函数重载。

#define  extern "C" EXPORT __declspec(dllexport)

EXPORT void TestDllFunc1();
EXPORT void TestDllFunc2();

TestDll.cpp内容:

#include "TestDll.h"

#include <iostream>

void TestDllFunc1()
{
    
    
	std::cout << "TestDllFunc1" << std::endl;
}

void TestDllFunc2()
{
    
    
	std::cout << "TestDllFunc2" << std::endl;
}

编译后生成TestDll.libTestDll.dll文件。

3 工程UseLib与UseDll

均只有一个文件main.cpp用来调用库中函数,其具体内容根据的载入方式有所不同。

四 静态载入和动态载入:

静态载入:在程序启动时载入。
动态载入:在程序运行过程中通过调用库函数实现载入库。

五 静态库的载入

因为静态库中所有数据在程序的编译链接阶段全部合并到exe文件中,静态库就只有静态载入一种载入方式。
静态载入包括配置方式和代码方式两种方法。

1 静态载入步骤(配置方式)

  • 头文件所在目录添加到附件包含目录。(可省略)
  • .lib文件所在目录添加到附加库目录
  • .lib文件名添加到附加依赖项
  • 在代码中#include 头文件(如步骤1省略,此时需要加上头文件的路径,相对路径绝对路径都可)

此时main.cpp内容:

#include "TestLib.h" 
// #include "..\\TestLib\\TestLib.h" 未设置附加包含目录时需要给出文件路径

int main()
{
    
    
	TestLibFunc1();
	TestLibFunc2();
	return 0;
}

2 静态载入步骤(代码方式)

  • 将头文件所在目录添加到附件包含目录(可省略)
  • 将.lib文件所在目录添加到附加库目录(可省略)
  • 在代码中#include 头文件(如步骤1省略,此时需要加上头文件的路径,相对路径绝对路径都可)
  • 使用#pragma comment(lib, "lib文件名")载入库。(如步骤2省略,此时需要加上lib文件的路劲,相对路径绝对路径都可)

此时main.cpp内容:

#include "TestLib.h" 
// #include "..\\TestLib\\TestLib.h" 未设置附加包含目录时需要给出文件路径

#pragma comment("lib", "TestLib.lib")
// pragma comment("lib", "..\\Output\\TestLib.lib") 未设置附加库目录时需要给出文件路径

int main()
{
    
    
	TestLibFunc1();
	TestLibFunc2();
	return 0;
}

六 动态库的载入

动态库的实现部分在.dll文件中,因此动态库的载入方式包括静态载入动态载入两种方式。

动态库的静态载入跟静态库的静态载入一样,只有一点需要注意:需要将.dll文件放到.exe文件所在目录下,exe文件运行时只会在所在目录搜寻dll文件(动态载入也一样)**,如果找不到会弹出系统错误提示框:由于找不到***, 无法继续执行代码,重新安装程序可能会解决此问题。

在这里插入图片描述

动态库的动态载入通过在代码中调用相关库函数实现,包括LoadLibrary()GetProcAddress()FreeLibrary()等。动态载入方式只需要使用.dll文件,头文件以及.lib文件均不需要。

1 动态载入步骤

  • 调用LoadLibrary()载入库, 保存其函数返回值,供之后GetProcessAddress()FreeLibrary()使用。
  • 调用GetProcessAddress()搜寻所需函数,将其返回值转换成函数指针,使用该函数指针完成函数调用。
  • 调用FreeLibrary()释放库。

此时main.cpp内容:

#include "TestDll.h"
#include <windows.h>

typedef int(*FuncAddr)();

int main()
{
    
    
	HINSTANCE dllDemo = LoadLibraryA("..\\Output\\TestDll.dll"); // 必须给出文件所在路径
	if (dllDemo)
	{
    
    
		// 通过函数名称获取函数指针,需要注意,动态库的导出函数一定要使用extern "C"进行修饰
		// 否则这里通过函数名找不到对应函数
		
		FuncAddr func1 = (FuncAddr)GetProcAddress(dllDemo, "TestDllFunc1");
		if (func1 != nullptr) func1();
		FuncAddr func2 = (FuncAddr)GetProcAddress(dllDemo, "TestDllFunc2");
		if (func2 != nullptr) func2();
		
		FreeLibrary(dllDemo);
	}
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/xp178171640/article/details/112470852