C++中的dll文件

目录

一、DLL是什么?

二、为什么使用DLL?

三、使用动态链接库的好处

四、dll的创建

方法1: 使用 __declspec(dllexport) 创建dll

方法2: 使用模块定义(.def)文件创建dll

五、dll的使用

方法1:隐式链接方式加载dll

方法2:显式链接方式加载dll

一、DLL是什么?

动态链接库(Dynamic Link Library)DLL文件与EXE文件一样也是可执行文件,但是DLL也被称之为库,因为里面封装了各种类啊,函数啊之类的东西,就像是一个库一样,存储着很多东西,主要是用来调用的。

调用方式主要分为两种:隐式(通过lib文件与头文件) 与 显式(只通过DLL文件)。

静态库:函数和数据被编译进一个二进制文件(扩展名通常为.lib),在使用静态库的情况下,在编译链接可执行文件时,链接器从静态库中复制这些函数和数据,并把它们和应用程序的其他模块组合起来创建最终的可执行文件(.exe)。当发布产品时,只需要发布这个可执行文件,并不需要发布被使用的静态库。

动态库:在使用动态库时,往往提供两个文件:一个引入库(.lib,非必须)和一个.dll文件。这里的引入库和静态库文件虽然扩展名都是.lib,但是有着本质上的区别,对于一个动态链接库来说,其引入库文件包含该动态库导出的函数变量的符号名,而.dll文件包含该动态库实际的函数数据

二、为什么使用DLL?

     微软方面一边开源了很多代码,比如MFC的,这些代码是直接暴露在程序员眼皮子底下的,和容易调用,当然也很容易与程序员的代码发生冲突,业内也称之为“白盒复用”,有一定的优势,但是也有弊端。在此基础上,我们再来聊DLL就比较好,DLL将要一系列的代码转化成二进制,直接供程序使用,称之为“黑盒复用”。

动态链接库DLL最大的好处就是节省内存,程序在没有运行到dll里的函数的时候,dll里的函数就没有运行,只有在运行到了dll,才通过dll的接口函数访问dll,这样比直接把函数代码写到主题函数,或静态链接库相比,运行的效率有了很大的提高.其次使用DLL文件编程,其更新也较为方便,不需要与程序链接。

三、使用动态链接库的好处

可以使用多种编程语言编写:比如我们可以用VC++编写dll,然后在VB编写的程序中调用它。
增强产品功能:可以通过开发新的dll取代产品原有的dll,达到增强产品性能的目的。比如我们看到很多产品踢动了界面插件功能,允许用户动态地更换程序的界面,这就可以通过更换界面dll来实现。
提供二次开发的平台:用户可以单独利用dll调用其中实现的功能,来完成其他应用,实现二次开发。
节省内存:如果多个应用程序使用同一个dll,该dll的页面只需要存入内存一次,所有的应用程序都可以共享它的页面,从而节省内存。

四、dll的创建

dll的创建主要有两种方法:

一是使用 __declspec(dllexport) 创建dll,二是使用模块定义(.def)文件创建dll

方法1: 使用 __declspec(dllexport) 创建dll

先创建一个空的DlL的项目

然后为工程添加一个C++源文件:Dll1.cpp,假设我要实现的是加法和减法运算,则代码如下: 
myDl1.cpp

//extern "C"表示动态链接库中文件名称不发生改变 ,解决C和c++之间调用的问题
#define DL1_API extern "C" _declspec(dllexport)
#include "dl1.h"

int add(int a,int b)
{
    return a+b;
}

int substract(int a ,int b )
{
    return a - b ;
}

dl1.h

//定义宏
#ifdef DL1_API
#else
#define DL1_API extern "C" _declspec(dllimport) //交给客户端使用时
#endif


DL1_API int add (int a , int b);
DL1_API int substract(int a, int b);

点击运行,既然已经有了这个dll文件,是不是就可以在其他程序中访问该dll中的add和subtract函数了呢?必须注意的一点是:应用程序如果想要访问某个dll中的函数,那么这个函数必须是已经被导出的函数

为了查看一个dll中有哪些导出函数,Visual Studio提供了一个命令行工具:Dumpbin。

使用Dumpbin命令确认dll的导出函数

首先在命令行中进入到VS的安装目录下,运行一个名为VCVARS32.bat的批处理程序(对于VS2013来说,该bat文件位于\VC\bin目录下),该文件的作用是用来创建VC++使用的环境信息。(注意,当在命令行界面执行VCVARS32.bat文件后,该文件设置的环境信息只在当前命令行窗口生效。) 
然后输入dumpbin命令,即可列出该命令的使用方法。

那么想要查看一个dll提供的导出函数,在Dll1.dll文件所在目录下,在命令行中输入下述命令:

dumpbin -exports Dll1.dll

其中以下代码是为了在编译链接时,C++会按照自己的规则篡改函数的名称,这一过程称为“名字改编”。这会导致不同的编译器、不同的语言下调用dll发生问题。因此我们希望动态链接库文件在编译时,导出函数的名称不要发生变化。 
为了实现这一目的,可以再定义导出函数时加上限定符:extern “C”,如

extern "C" __declspec(dllexport) int add(int a, int b){
//...
}

但是这种方式只能解决C++和C语言之间相互调用时函数命名的问题。为了彻底解决这个问题,可以通过模块定义(.def)文件实现。

方法2: 使用模块定义(.def)文件创建dll

1.创建.def结尾的文件 
在项目目录创建.def结尾的文件,并导入到项目中 

dll2.def

LIBERARY DLL2

EXPORTS
add
substract 

表示DLL2.dll导出add和substract函数,LIBRARY语句用于指定动态链接库的名称,该名称与生成的动态链接库名称一定要匹配

DLL2.cpp

int add(int a,int b)
{
    return a+b;
}

int substract(int a ,int b )
{
    return a - b ;
}

以上就是创建dll的两种方法,个人比较提倡使用模块定义(.def)文件创建dll,代码简洁的同时还没有名字改编的问题。

五、dll的使用

dll的使用也有两种方法,一是隐式链接的方式加载dll,二是显示加载方式加载dll。

  • 方法1:隐式加载(需要.lib和.dll) 
  • 方法2:显式加载(需要.dll)

方法1:隐式链接方式加载dll

但截至目前我们已经制作了一个DLL的Demo程序了,我们可以先来看下效果: 
新建一个新的Win32程序,添加一个dllTest.cpp用于测试我们生成dll

准备工作: 
将生成的DL1.dll放到测试程序会生成的.exe文件的地方(Debug或Release,···\DllTest\Debug)

软件的DLL搜索目录: 
.lib 文件搜索顺序: 
.exe软件算在目录(Debug,Release) 
当前目录 
System32 
System 
Path中的目录

DL1.lib文件放入项目文件中(···\DllTest\)

.lib 表示输入库文件,没有实际代码,包含重定位表

dllTest.cpp

//第一种引用的方法
//extern int add ( int a ,int b);
//extern int substract (int a, int b);

//第二种引用的方法
/*
告诉编译器函数是从.lib文件中导入的,编译器会据此生成效率更高的代码
*/
//_declspec(dllimport) int add (int a , int b);
//_declspec(dllimport) int substract(int a, int b);

//第三种方法
//导入头文件
#include "I:\SDK_settle\DL_Test\DL1\DL1\dl1.h"

#include <iostream> 


using namespace std;

int main()
{
    cout<< add(3,4);
    cout<< substract(3,4);
}

可以看出,我们使用了三种引用的方法,这三种方法都是可以的。

方法2:显式链接方式加载dll

DLL显式加载时指在程序运行过程中,需要用到dll里的函数时,再动态加载dll到内存中,这种加载方式因为是在程序运行后再加载的,所以可以让程序启动更快,而且dll的维护更容易,使得程序如果需要更新,很多时候直接更新dll,而不用重新安装程序.只是这种加载方式,函数调用稍微复杂一点

//第一种引用的方法
//extern int add ( int a ,int b);
//extern int substract (int a, int b);

//第二种引用的方法
/*
告诉编译器函数是从.lib文件中导入的,编译器会据此生成效率更高的代码
*/
//_declspec(dllimport) int add (int a , int b);
//_declspec(dllimport) int substract(int a, int b);

//第三种方法
//导入头文件
//#include "I:\SDK_settle\DL_Test\DL1\DL1\dl1.h"

#include <iostream> 
#include <stdio.h>
#include <windows.h>

using namespace std;

int main()
{
    //动态加载dll
    HINSTANCE hInst;
    hInst = LoadLibrary(L"DL1.dll");
    //根据函数名获取dll地址
    typedef int(*SUBPROC)(int a, int b);
    SUBPROC Sub = (SUBPROC)GetProcAddress(hInst, "substract");
    //调用dll的导出函数
    cout<< Sub(3,4);
    FreeLibrary(hInst);       //LoadLibrary后要记得FreeLibrary
}

以上就是两种生成dll的方法和两种加载dll的方法,建议使用第二种生成方法和第二种加载方法结合使用。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值