动态链接库dll(Windows/C++)

2023-05-16

1、概念

(1)动态链接库广泛用于Windows系统及应用程序,不能单独被执行,在应用程序运行期间被动态调用的模块文件。区别于静态链接库,均属于独立的代码编译模块,但静态链接库在调用方编译期间,被编译到程序里,与程序形成完成的运行(exe)文件。动态链接库只是将对外导出信息(声明)暴露给调用方,调用方在程序中仅依据暴露的信息进行使用,在编译期间,不会与动态链接库发生任何关系,只有在应用程序运行动态链接库所包含的功能时,动态库才被加载到进程空间,实现调用过程。
(2)静态链接库只需要发布调用的主执行程序即可,动态链接库需要将相关的dll文件全部打包发布。windowsAPI就包含在3个主要的动态库中,分别是Kernel32.dll、User32.dll、GDI32.dll

2、优势

可以将完成的某种功能放到动态库中,供给任何需要调用的程序,实现模块化,便于修改维护和后期的功能完善,实现与主程序降低耦合。同时由于dll为编译的二进制文件,可以突破编写语言限制,管理简化。此外,dll文件只在内存加载一次,供不同程序共享,实现资源共享化,减少内存资源浪费,比如dll负责图标、对话框模板等资源加载,则多个应用程序可共享这些资源。

3、创建和使用

在VS下使用VC++创建动态库分为如下几种:
(1)Win32创建的DLL
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
生成文件及dllmain函数如下,windows下指定调用约定是__stdcall,意味着这个函数以标准Pascal的方式进行调用,其中传入dllmain的hMoudle为dll自身的模块句柄,ul_reason_for_call为调用原因说明,为分支下四种情况,一般不需要修改dllmain函数,在清除或者加载dll时可在此处做相关处理
在这里插入图片描述
在当前工程下添加导出信息的声明头文件,将来此文件在隐式链接dll时需要用到,需要在文件内添加导出声明_declspec(dllexport),由于dll工程会预定义宏XX_EXPORTS(可在属性配置中C++预定义下查看添加),所以为了调用方便,可根据宏判断分别定义导入导出声明的宏,这样在调用方就只需要包含此头文件就可使用导出数据,而不需要单独添加导入声明。接下来添加导出类、数据、函数等,需要注意导出声明的位置,类的导出声明放在class后。
此外,全局变量在cpp文件定义(只在所有调用的cpp中定义一次),如果是头文件声明需要在cpp处加external,如果在头文件添加external声明,这样不需要在任何其他文件调用时添加external,而且避免全局变量重复定义后编译错误
在不同语言C/C++编译条件下,导出函数的名称可能会出现变化,C++实现了函数重载,编译期间会对函数名进行修饰,所以在dll的显示加载(后文)时需要用到函数名的时候会出现问题,可以通过命令dumpbin查看dll文件的导出函数名,C函数编译后函数名不会出现变化,所以需要在导出声明时添加extern "C"告诉编译器以C的方式编译,下图是dumpbin运行情况:
在这里插入图片描述
在这里插入图片描述
同时,导出函数在调用时,函数参数的入栈顺序,堆栈清理以及函数名修饰等需要有统一的调用约定,参能实现函数正确调用,windows下一般是_stdcall调用约定,所以在导出函数时还需要添加此声明
实例代码如下,利用导出函数、全局数据、类实现数字相加功能
在这里插入图片描述
导出声明代码:

#pragma once

#ifdef TESTPRODLL_EXPORTS
#define TESTPRODLL2_API __declspec(dllexport)
#else
#define TESTPRODLL2_API __declspec(dllimport)
#endif

//extern "C" 指定以C语言方式调用函数,C语言没有函数重载,所以能保证函数名不变
//
//WINAPI约定调用函数的规则,指参数入栈顺序为_stdcall

extern TESTPRODLL2_API int a;                      //导出变量

extern "C" TESTPRODLL2_API int WINAPI Add(int b);  //导出函数

class TESTPRODLL2_API ADD                          //导出类
{
public:
	 int WINAPI Do(int b);
};

extern "C" TESTPRODLL2_API int WINAPI GetA();           //获取全局变量

extern "C" TESTPRODLL2_API void WINAPI SetA(int b);     //设置全局变量

extern "C" TESTPRODLL2_API int WINAPI RunADDDo(int a);  //创建ADD对象,通过对外函数访问类

在cpp中实现如下:


#include "stdafx.h"
#include "TestAdd.h"

int a = 1;                   //全局变量定义需要加类型,而且必须定义

int Add(int b)
{
	int c = a + b;
	return c;
}

int ADD::Do(int b)
{
	return a + b;
}
int GetA()       //获取全局变量
{
	return a;
}

void SetA(int b) //设置全局变量
{
	a = b;
}

int RunADDDo(int a)  //创建ADD对象,调用方通过对外函数访问dll的类
{
	ADD add;
	return add.Do(a);
}

编译工程后,win32的dll就创建完毕,目录如下:
在这里插入图片描述
接下来调用该dll,实现dll中的功能,分为如下两种方式:
(a)隐式加载dll
需要dll的导出头文件XX.h和引入库文件XX.lib,在调用cpp中包含XX.h头文件,同时添加lib链接,可以在属性配置中添加,也可用#progma coment命令引入。
隐式加载dll通过lib将导出声明信息编译到执行程序中,在程序启动后会加载lib指定的dll,dll需要放在执行程序目录下,不论dll功能是否正在使用,都会被加载
配置链接lib:
在这里插入图片描述
在这里插入图片描述
命令引入lib:
#pragma comment(lib,“XX.lib”)
以上两种链接lib方式均可,以下为第二种方式实现

#include "stdafx.h"
#include "windows.h"
#include "TestAdd.h"
#pragma comment(lib,".\\x64\\Debug\\TestPro.DLL.lib") //注意表示当前目录下需要加‘.\’
int main()
{
	a = 2;
	int c = Add(3); //5
	ADD A;
	int d = A.Do(4);//6
	int m = RunADDDo(5); //7
	return 0;

(b)显式链接dll
显式链接dll只需要dll文件本身即可,前提是调用者需要清楚dll内部导出细节,比如函数名等信息,需要将dll复制到调用执行文件的目录下
显式链接dll的好处是程序在执行dll相关功能时动态加载dll文件,启动后不会立马加载dll文件,所以不会影响启动效果,也能够节省内存空间
加载方式如下:

#include "stdafx.h"
#include "windows.h"

int main()
{
	HINSTANCE hInstLibrary = ::LoadLibrary(L"TestPro.DLL.dll");  //加载文件
	if (hInstLibrary == NULL)
	{
		::FreeLibrary(hInstLibrary);
		return 0;
	}
	//
	//*******dll中导出全局数据访问**********//
	//
	//通过函数指针进行访问
	typedef int(__stdcall *FuncGetA)();
	FuncGetA testGetA = (FuncGetA)::GetProcAddress(hInstLibrary, "GetA");       //根据函数名进行访问
	int n = GetLastError();
	if (nullptr == testGetA || 0 != n)
	{
		::FreeLibrary(hInstLibrary);
		return n;
	}
	int a = testGetA();  //1

	typedef void(__stdcall *FuncSetA)(int);
	FuncSetA testSetA = (FuncSetA)::GetProcAddress(hInstLibrary, "SetA");       //根据函数名进行访问
	n = GetLastError();
	if (nullptr == testSetA || 0 != n)
	{
		::FreeLibrary(hInstLibrary);
		return n;
	}
	testSetA(4);       
	a = testGetA();      //4

	//
	//*******dll中导出函数访问**********//
	//
	typedef int(__stdcall *FuncAdd)(int);
	FuncAdd testAdd = (FuncAdd)::GetProcAddress(hInstLibrary, "Add");                   //根据函数名进行访问
	n = GetLastError();
	if (nullptr == testAdd || 0 != n)
	{
		::FreeLibrary(hInstLibrary);
		return n;
	}
	int sum = testAdd(5);   //9
	

	//
	//*******dll中导出类访问**********// 备注:此功能在dll添加导出函数中实现dll中类访问
	//
	typedef int( *FuncCreateObj)(int);
	FuncCreateObj testRun = (FuncCreateObj)::GetProcAddress(hInstLibrary, "RunADDDo");       //根据函数名进行访问
	n = GetLastError();
	if (nullptr == testRun || 0 != n)
	{
		::FreeLibrary(hInstLibrary);
		return n;
	}

	sum = testRun(1); //5

	::FreeLibrary(hInstLibrary);                                 //卸载文件

    return 0;
}

注意:显示加载要使用全局变量,需要在dll的头文件添加get/set函数进行操作,使用dll中的类需要添加对外导出函数进行间接调用
此外,对外声明可以通过另一种方式进行实现,需要使用def文件导出,def文件是模块定义文件,可以将需要导出的函数在此文件声明,编译后就将导出信息编译到lib文件,def文件使用如下
在这里插入图片描述
在这里插入图片描述
添加导出说明如下
在这里插入图片描述


;注释部分
LIBRARY TestPro.DLL.lib
EXPORTS 
GetA     @ 1
SetA     @ 2
Add      @ 3
RunADDDo @ 4

;导出函数名称,@序号为导出函数序号

同时修改导出头文件声明如下:

#pragma once


//extern "C" 指定以C语言方式调用函数,C语言没有函数重载,所以能保证函数名不变
//
//WINAPI约定调用函数的规则,指参数入栈顺序为_stdcall

extern  int a;                      //导出变量

extern "C"  int WINAPI Add(int b);  //导出函数

class ADD                          //导出类
{
public:
	 int WINAPI Do(int b);
};

extern "C"  int WINAPI GetA();           //获取全局变量

extern "C"  void WINAPI SetA(int b);     //设置全局变量

extern "C"  int WINAPI RunADDDo(int a);  //创建ADD对象,通过对外函数访问类

编译调用,可实现同样功能
以上代码均在VS2015下调试运行通过
(2)Win32创建支持MFC的DLL
创建如下:
山地车
在这里插入图片描述
如图,比win32下创建dll多了对外导出头文件和相关声明,同时主函数发生了变化,不是dllmian函数
而是main函数,增加了MFC支持,可以使用MFC相关类进行dll编程,dll实现包括调用等与win32基本一致,不多做描述
(3)MFC创建DLL
创建如下:
在这里插入图片描述
在这里插入图片描述
MFC使用了CWinApp类,添加了消息响应机制
在这里插入图片描述
在这里插入图片描述
以上是MFC创建dll时VS给出的所有内容,其他导出实现操作与win32基本一致,需要注意MFC下32位和64位的编译时不同,32位下会重定义错误,需要将WINAPI加到cpp中
以上是分享的dll相关的所有内容,欢迎转载,请注明出处!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

动态链接库dll(Windows/C++) 的相关文章

随机推荐

  • C#语法小知识(三)枚举类型enum

    枚举类型声明一系列常数 xff0c 用于表示这个类型的变量可能会在这些常数里变化 我们在这篇文章里讲一下枚举类型的几个用法 一个简单的枚举类型的定义 xff1a enum TestEnum x y z 而使用也很简单 xff1a TestE
  • 微信开放平台之第三方平台代公众号发起网页授权

    正式讲解之前我想问一个问题 xff1a 微信开放平台第三方平台为什么会出现 xff1f 或者说微信的开发人员为什么弄出个开放平台的第三方平台出来 xff1f 我的理解是 xff1a 原本公众号开发时只能给一家公司开发 xff0c 因为配置的
  • Undefined symbol RTC_DateStruct (referred from main.o).

    被自己蠢哭了 我是两个工程文件合在一起用的 一个工程中的 c文件变量定义之后是在另一个 c文件中共用的所以用了 extern RTC TimeTypeDef RTC TimeStruct extern RTC DateTypeDef RTC
  • 关于Win10下安装Linux ubuntu子系统遇到的几个问题

    1 首先是ubuntu下载 xff0c 在Win10自带的应用商店Microsoft Store搜索 ubuntu 即可找到 2 安装完成后启动 ubuntu 后 Installing this may take a few minutes
  • 嵌入式学习-STM32F103ZE中断配置

    目录 一 中断概念 二 中断类型 三 NVIC 四 中断优先级 五 中断编程顺序 1 使能中断请求 2 中断优先级配置 3 初始化NVIC InitTypeDef结构体 4 中断服务函数 六 总结 一 中断概念 中断是指计算机运行过程中 x
  • haproxy的统计报告功能

    HAProxy的统计报告 简介 HAProxy有统计报告功能 可以让使用者通过web页面概览后端服务器的概况 甚至更改它们的状态 配置 vim etc haproxy haproxy cfg listen statistics bind 9
  • win10 Remote Host 调试 ubuntu18.04 中有libXXX.so库,报/usr/lib/ld 找不到-lxxx

    按照下图操作 xff0c 找到自己的交叉编译环境中的g 43 43 和gcc工具可以解决
  • CDN和Akamai

    最近在看分布式相关的东西 xff0c 在看到HTTP Caching的时候 xff0c 提到CDN和Akamai 以前对这些东西都是一无所知啊 记录一下吧 http zh wikipedia org wiki E5 85 A7 E5 AE
  • 在Centos7环境安装GitLab

    https about gitlab com install centos 7 1 Install and configure the necessary dependencies On CentOS 7 and RedHat Oracle
  • 免费的天气api

    这是最近网上查询到关于天气的api xff0c 大部分的接口都是收费 xff0c 有部分接口虽然免费 xff0c 但查询到的信息量特别不全 但好在有几个免费接口倒是不错 xff0c 倒是可以使用 免费的天气api 高德地图 天气查询免费ap
  • Enlightenment官网介绍

    Enlightenment和EFL的官方网站 xff1a http www enlightenment org Enlightenment xff1a Enlightenment 是一个旗舰项目 它曾经是一个不起眼的 X11 窗口管理器 W
  • Enlightenment 是窗口管理器,Enlightenment 是桌面外壳,Enlightenment是创建漂亮应用程序的材料

    Enlightenment 是窗口管理器 xff0c Enlightenment 是桌面外壳 xff0c Enlightenment是创建漂亮应用程序的材料 xff0c Enlightenment xff0c 或者简单的一个 e xff0c
  • ubuntu安装nvidia显卡驱动报错:”The CC version check failed”

    参考过不少博主回答的问题 xff0c 但都存在很多问题 xff0c 或者比较麻烦 xff0c 给大家推荐一下我自己尝试解决后比较好的一个方案 xff1a 出现这个问题的原因是因为驱动可能比较新 xff0c 系统内核的gcc版本和编译器的默认
  • codeforces1169C 二分答案+思维

    1169C 1700的题 xff0c 然而比赛的时候没有做出来 题意 xff1a 给你一个n表示序列长度为n xff0c 还有一个m表示这个序列的最大值小于m 然后对这个数组进行多次操作 xff0c 一次操作为 对ai xff0c aj x
  • python用pip装第三方库numpy时报错:UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 7: ordi

    python用pip装第三方库numpy时一直报错 xff1a UnicodeDecodeError 39 ascii 39 codec can 39 t decode byte 0xc3 in position 7 ordinal not
  • debian8 jessie 更换为国内源

    编辑 etc apt sources list文件 xff1a 用 注释掉老的源 添加新的源 xff0c deb http mirrors 163 com debian jessie main non free contrib deb ht
  • 09、Flutter FFI Dart Native API

    Flutter FFI 学习笔记系列 Flutter FFI 最简示例 Flutter FFI 基础数据类型 Flutter FFI 函数 Flutter FFI 字符串 Flutter FFI 结构体 Flutter FFI 类 Flut
  • Copilot 自动编程AI工具

    OpenAI与GitHub联合构建的AI自动编程工具Copilot xff0c Copilot基于自然语言处理模型GPT 3搭建而成 xff0c Copilot预览版已经正式上线Visual Studio Code平台 OpenAI的GPT
  • SQLite的SQL语法

    SQLite库可以解析大部分标准SQL语言 但它也省去了一些特性 并且加入了一些自己的新特性 这篇文档就是试图描述那些SQLite支持 不支持的SQL语法的 查看关键字列表 如下语法表格中 xff0c 纯文本用蓝色粗体显示 非终极符号为斜体
  • 动态链接库dll(Windows/C++)

    1 概念 xff08 1 xff09 动态链接库广泛用于Windows系统及应用程序 xff0c 不能单独被执行 xff0c 在应用程序运行期间被动态调用的模块文件 区别于静态链接库 xff0c 均属于独立的代码编译模块 xff0c 但静态