C++ 动态链接库

原创文章,转载请注明出处。

如果Windows系统有缺失的DLL文件,可以去这个链接下载。点击去这里寻找

1>认识DLL(动态链接库)

动态链接库DLL(Dynamic-Link Library)通常包含程序员自定义的变量和函数, 可以在运行时动态链接到可执行文件(我们的exe程序)中。

2 >格式后缀

Windows上:(.dll)
Linux上:(.so)
Android上:(.so)
IOS上:(.dylib)

3 >DLL优点

1)模块化,耦合小:大规模软件开发中,开发过程独立,耦合度小,比如UE4里面的模块(每一个.build.cs)都是一个DLL。

2)扩展性:DLL文件与EXE文件是独立的,只要接口不变,升级程序只需更新DLL文件不需要重新编译应用程序。并且我们的EXE文件较小。

3)复用性:DLL的编制与具体的编程语言以及编译器无关,不同语言编写的程序只要按照函数调用约定就可以调用同一个DLL函数。

4)节省内存:当应用程序使用动态链接时,多个应用程序可以共享磁盘上单个DLL副本。

5)隐私性:可以当做黑盒使用,可以将我们的具体实现代码隐藏起来,比如我们想将算法的具体实现隐藏起来,不让别人看到我是怎么做的。一般SDK接入时候,里面的具体算法实现都是不会公开cpp的。

4 >DLL缺点

如果是动态loaddll的话,会牺牲部分性能吧。微乎其微的。

5 >加载DLL

1>动态加载(运行时加载)通过LoadLibary加载, GetProcAddress调用。

如果加载失败,通过GetLastError()获取失败原因。

下面是LoadLibary的示例代码
第一步,在.h中声明了static HMODULE hDLL;

#include<Windows.h> //加载的头文件

class QIDCardReader : public QMainWindow
{
    Q_OBJECT

public:
    QIDCardReader(QWidget* parent = 0);
private:
	static HMODULE hDLL;
};

第二步,定义要调用的dll中的方法。并通过LoadLibrary给hDLL赋值

#include "QIDCardReader.h"

//CPP中预声明一下我要调用dll里面的方法
typedef int(*MyRouton_RepeatRead)(bool);
typedef int(*MyInitComm)(int);
typedef int(*MyAuthenticate)();
typedef int(*MyReadBaseInfosPhoto)(char * , char * , char * ,
	char *, char * , char * ,
	char *, char * , char* , char * );
typedef int(*MyCloseComm)();

HMODULE QIDCardReader::hDLL;

QIDCardReader::QIDCardReader(QWidget* parent) : QMainWindow(parent)
	,ui_(new Ui::QIDCardReaderClass)
{
	hDLL = LoadLibrary(L"D:\\bimvr-vrlauncher\\test\\Sdtapi.dll"); //加载dll文件 
	//如果加载失败的话, 通过GetLastError()进行获取,看失败原因。
	auto code5 = GetLastError();
}

第三步,调用dll中的方法 GetProcAddress(hDLL, “实际的dll端函数导出名称”)

//调用1
if (hDLL)
{
	MyRouton_RepeatRead MyRouton_RepeatReadFunc = (MyRouton_RepeatRead)GetProcAddress(hDLL, ("Routon_RepeatRead"));//直接使用原工程函数名 
	if (MyRouton_RepeatReadFunc)
	{
		MyRouton_RepeatReadFunc(true);
	}

}

//调用2
int iPort = 1001;
//int ret = InitComm(iPort);
int ret = -1000;
if (hDLL)
{
	MyInitComm MyInitCommFunc = (MyInitComm)GetProcAddress(hDLL, ("InitComm"));//直接使用原工程函数名 
	if (MyInitCommFunc)
	{
		ret = MyInitCommFunc(iPort);
	}
}
if (ret)
{
	//ret = Authenticate();
	if (hDLL)
	{
		MyAuthenticate MyAuthenticateFunc = (MyAuthenticate)GetProcAddress(hDLL, ("Authenticate"));//直接使用原工程函数名 
		if (MyAuthenticateFunc)
		{
			ret = MyAuthenticateFunc();
		}
	}
	if (ret)
	{
		char user_name[31] = { 0 };
		char user_gender[3] = { 0 };
		char user_folk[11] = { 0 };
		char user_birthday[9] = { 0 };
		char user_code[19] = { 0 };
		char user_address[71] = { 0 };
		char user_agency[31] = { 0 };
		char expire_start[9] = { 0 };
		char expire_end[9] = { 0 };

		QByteArray data_path = QString::fromStdWString(AppUtils::GetDataPath()).toLatin1();
		ret = ReadBaseInfosPhoto(user_name, user_gender, user_folk, user_birthday, user_code,
			user_address, user_agency, expire_start, expire_end, str/*data_path.data()*/);
		if (hDLL)
		{
			MyReadBaseInfosPhoto MyReadBaseInfosPhotoFunc = (MyReadBaseInfosPhoto)GetProcAddress(hDLL, ("ReadBaseInfosPhoto"));//直接使用原工程函数名 
			if (MyReadBaseInfosPhotoFunc)
			{
				ret = MyReadBaseInfosPhotoFunc(user_name, user_gender, user_folk, user_birthday, user_code,
						user_address, user_agency, expire_start, expire_end, str/*data_path.data()*/);
			}
		}
	}
}

第四步,可以选择性调用FreeLibrary(hDLL)卸载Dll,这个是下面的静态三件套加载方式做不到的。
也就是说我们可以动态控制Dll的加载与卸载。

2>静态加载方式(.h .lib .dll三件套加载)

1>项目->属性->配置属性->VC++ 目录-> 在“包含目录”里添加头文件Sdtapi.h所在的目录
2>项目->属性->配置属性->VC++ 目录-> 在“库目录”里添加头文件Sdtapi.lib所在的目录
3>项目->属性->配置属性->链接器->输入-> 在“附加依赖项”里添加“Sdtapi.lib”(若有多个 lib 则以空格隔开)
也可以使用#pragma comment(lib, “Sdtapi.lib”)代替上面的1、2、3步骤。

在这里插入图片描述
QIDCardReader.cpp 包含.h,将.h添加到项目中

#include "sdtapi.h"

void QIDCardReader::onNotify()
{
	Routon_RepeatRead(true);

	int iPort = 1001;
	int ret = InitComm(iPort);
	if (ret)
	{
		ret = Authenticate();
		if (ret)
		{
			char user_name[31] = {0};
			char user_gender[3] = {0};
			char user_folk[11] = {0};
			char user_birthday[9] = {0};
			char user_code[19] = {0};
			char user_address[71] = {0};
			char user_agency[31] = {0};
			char expire_start[9] = {0};
			char expire_end[9] = {0};

			QByteArray data_path = QString::fromStdWString(AppUtils::GetDataPath()).toLatin1();
			ret = ReadBaseInfosPhoto(user_name, user_gender, user_folk, user_birthday, user_code,
				user_address, user_agency, expire_start, expire_end, data_path.data());
		}
	}
}

现在是可以通过编译了,但是运行会报错 提示LinkError
因为程序分为编译和链接两步骤,这就是为什么报错误的原因。
我们解决这个报错就是将.dll放到和.exe同级目录就好了

如果最终exe找不到dll的话,会提示下面的错误。解决方法就是将dll放到和exe同级目录就好了。
在这里插入图片描述
谢谢,创作不易,大侠请留步… 动起可爱的双手,来个赞再走呗 <( ̄︶ ̄)>

  • 42
    点赞
  • 117
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WhiteTian

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值