程序快捷开发(二) 动态库与静态库

应用背景

 在程序开发时,无论项目怎么变化,有些功能是必不可少同时又是不变的,比如数据存储,通讯,或者某个第三发组件。这个时候,我们无需重新造轮子,应该用想办法使之标准化。使用静态库,动态库是重用代码的一种绝佳方式。

具体应用

一 静态库:


编译静态库时,只会产生.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内容:

// 注意:动态库导出函数一定要使用__declspec(dllexport)修饰
// 如果不使用__declspec(dllexport)进行修饰,
// 编译时不会生成.lib文件。

#define 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文件中,静态库就只有静态载入一种载入方式。
静态载入包括使用IDE配置方式和使用代码方式两种。

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 <windows.h>

typedef int(*FuncAddr)();

int main()
{
	HINSTANCE dllDemo = LoadLibraryA("..\\Output\\TestDll.dll"); // 必须给出文件所在路径
	if (dllDemo)
	{
		// 直接通过函数名称获取函数指针,需要注意,动态库的导出函数一定要使用extern "C"进行修饰
		// 因为只有通过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;
}

 补充:  在生成后事件中复制 DLL

  1. 右键单击“解决方案资源管理器”中的“MathClient”节点,然后选择“属性”以打开“属性页”对话框 

  2. 在“配置” 下拉框中,选择“所有配置” (如果尚未选择)。

  3. 在左窗格中,选择“配置属性”>“生成时间”>“后期生成事件” 。

  4. 在属性窗格中,在“命令行”字段中选择编辑控件 。 如果已按照指示将客户端项目置于 DLL 项目的单独解决方案中,则输入以下命令:

    xcopy /y /d "..\..\MathLibrary\$(IntDir)MathLibrary.dll" "$(OutDir)"

参考MSDN:演练:创建和使用自己的动态链接库 (C++) | Microsoft Docs 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值