DLL的前世今因

网上的有关DLL的文章有很多,也很杂。这里我花了一段时间,查了资料,把各种版本自己动手试了试。以下是我的学习笔记,希望对大家有所帮助。

DLL:动态链接库。

动态链接库就是一种别人已经写好的代码(一般是函数或类),并且已经编译和链接好了,我们只是需要按照一定的规则就可以使用这些代码。

并且动态链接库是可以进行跨语言的。就是说C的DLL可以在Java中使用。

DLL是与exe分开的,当exe执行的时候才去找DLL中函数,而普通的lib文件是链接的时候就已经加载到exe中的。

动态链接库 (DLL) 是作为共享函数库的可执行文件。动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数。函数的可执行代码位于一个DLL 中,该 DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。DLL还有助于共享数据和资源。多个应用程序可同时访问内存中单个 DLL 副本的内容。

动态链接与静态链接的不同之处在于:动态链接允许可执行模块(.dll 文件或 .exe 文件)仅包含在运行时定位 DLL 函数的可执行代码所需的信息。在静态链接中,链接器从静态链接库获取所有被引用的函数,并将库同代码一起放到可执行文件中。

Lib文件是已经编译好的,但没有链接,所以程序链接的时候就会去找lib

Dll文件是已经编译好,且链接好的,所以程序编译和链接的时候都和它没关系,只有执行的时候才去找他。

应用程序和 DLL 之间的区别

应用程序可有多个同时在系统上运行的实例,而 DLL
只能有一个实例。

应用程序可以拥有堆栈、共用内存、文件句柄、消息队列这样的事物,而 DLL
不能。

使用 DLL 的优点

1节省内存和减少交换操作。很多进程可以同时使用一个 DLL,在内存中共享该 DLL
的一个副本。相反,对于每个用静态链接库生成的应用程序,Windows
必须在内存中加载库代码的一个副本。

2节省磁盘空间。许多应用程序可在磁盘上共享 DLL
的一个副本。相反,每个用静态链接库生成的应用程序均具有作为单独的副本链接到其可执行图像中的库代码。

3升级到 DLL
更为容易。当 DLL
中的函数发生更改时,只要函数的参数和返回值没有更改,就不需重新编译或重新链接使用它们的应用程序。相反,静态链接的对象代码要求在函数更改时重新链接应用程序。

4提供售后支持。例如,可修改显示器驱动程序 DLL
以支持当初交付应用程序时不可用的显示器。

5DLL可以被其他DLL调用,而lib库不可以被其他lib调用。

生成DLL文件

这里由于我们是在VS中生成的,所以可以选择生成C语言的DLL、或生成C++的DLL。这里我们是生成C语言的DLL,然后在C++中使用。

我们首先new project->C++->Win 32 Console->DLL-文件名:MYdll

这里我们可以得到MYdll.cpp文件了。(Dllmain.cpp是入口函数不用管)

这里由于后面使用时可能会用到.h文件,所以我们new一个头文件,文件名:MYhead.h

3种方法:

1. 使用__declspec(dllexport) 关键字生成DLL

1. 在MYhead.h文件中声明要导出函数

若要导出函数,__declspec(dllexport) 关键字必须出现在调用约定关键字的左边

若要导出类中的所有公共数据成员和成员函数,关键字必须出现在类名的左边。

例如:

//MYhead.h

extern "C" _declspec(dllexport) int Max(int a, intb);

//extern “C”出现的目的是由于我们此时在VS中,而此时默认的是用C++来编译的,而我们想要的是C语言的DLL,所以要加。如果你想得到C++的DLL,这里就不需要加extern”C”了。

2. 在MYdll.cpp文件中把函数实现

//MYdll.cpp

#include”MYhead.h”

int Max(int a, int b)

{

return (a>b)? a:b;

}

3. 然后buildsolution就会在debug中出现lib和dll文件了。

2. 使用def文件生成DLL

1. 在MYhead.h文件中声明要导出函数

例如:

//MYhead.h

extern "C" int Max(int a, int b);

//加extern “C”的原因上面已经说过。

2. 在MYdll.cpp文件中把函数实现

//MYdll.cpp

#include”MYhead.h”

int Max(int a, int b)

{

return (a>b)? a:b;

}

3. new一个def文件,名字随便。在此文件中写入下列语句:

LIBRARY MYdll(注意,这里是实现函数的文件名,不能是声明文件的文件名。)

EXPORTS

Max(这里是要导出的函数名,如果有多个,换行接着就可以了)

4. 然后buildsolution就会在debug中出现lib和dll文件了。

3. 使用#pragma comment(linker,”/export:_functionname”)

1. 在MYhead.h文件中声明要导出函数

例如:

//MYhead.h

#pragma comment(linker,”/export:_Max”)

//注意:这里是_Max,因为/export:后面的必须是函数编译之后的函数名,而C语言编译后的函数名就是在原函数名前加一个下划线。

注意:这种方法有个弊端,我们必须要预先知道函数编译后的函数名,C语言还可以表示,但是C++就很难表示了。所以这种方法限于生成C语言的DLL。

extern "C" int Max(int a, int b);

//加extern “C”的原因上面已经说过。

2. 在MYdll.cpp文件中把函数实现

//MYdll.cpp

#include”MYhead.h”

int Max(int a, int b)

{

return (a>b)? a:b;

}

3. 然后buildsolution就会在debug中出现lib和dll文件了。

注:这里为何生成了lib文件?

这里的lib文件是DLL文件中函数名称和地址,如果是隐式链接的话,链接的时候程序会链接DLL的导入库文件(就是这个lib文件)。在程序执行时,程序才会根据导入库文件中的函数地址去到DLL中寻找函数体。

DLL<-----àLIB<-----àEXE

注:DLL内部函数的调用是不用加什么_declspec(dllexport)的,想一想DLL的原理就知道原因了。

链接DLL的方法(如何使用DLL)

注:编译时是和库文件没有关系的

有两种类型的链接:隐式链接和显式链接。(隐式链接要把生成DLL的程序会产生DLL文件和导入文件lib(有时还需要.h文件)放到当前工程目录下,显示链接只需要DDL文件就可以了)

隐式链接:

隐式链接就是在程序开始执行时就将DLL文件加载到应用程序当中。为隐式链接到 DLL,可执行文件必须从 DLL
的提供程序获取下列各项:

1包含导出函数和/或 C++
类的声明的头文件(.h
文件)使用__declspec(dllimport)

将函数的定义导入进来

2要链接的导入库(.LIB
files
)。(生成 DLL
时链接器创建导入库。)

3实际的 DLL(.dll
文件)。

例如:

// TestDll.h

#pragma comment(lib,"MyDll.lib")

(!!这一步开发人员也可以不使用#pragma comment(lib,"MyDll.lib")语句,而直接在工程的Setting->属性->Linker->Input->Additional
Dependencies最后加MYDll.lib;注意要加”;”)

extern "C" _declspec(dllimport)int Min(int a,int b); !!如果要导出的是变量:则

extern“C” extern _declspec(dllimport) int a;因为只有加上extern,这才是声明。

这行语句也可以用

extern“ C” {

#include”Mydll.h”

}

来代替,效果是一样的。(注意要把MyDll.h拷到此工程下).推荐这样使用。

这里我们是使用C语言生成的DLL,这里我们要用C++来使用,所以要加extern”C”.如果你使用的是用C++生成的DLL,这里就不需要extern
“C”了。

<span style="font-size:18px;">//TestDll.cpp
#include<iostream>
#include<Windows.h>
#include"Dlltest.h"
void main()
{int a;
a=Min(8,10)
cout<<a<<endl;
}</span>

/////////////////////////////////////////////////////////////

隐式链接体会:

1. Dll和lib不是都已经有了吗,为何还需要#include<.h>或dllimport。

因为dll需要静态导入,所以需要lib导入库,由于lib导入库中的函数名都是c编译好的,函数名都是_funcname形式的,所以需要#include<..h>或dllimport函数名,这样编译时才会生成函数名类似_funcname形式的。然后链接时才会正确链接lib导入库。

2. 为何#include头文件中要加extern “C”或者_declspec(dllimport)前要加extern “C”。

因为lib导入库是用C语言编译好的,函数名形如_funcname.如果用C++来编译头文件或_declspec(dllimport)后面的函数名,由于C++支持多态,所以它编译函数名时要把函数的参数个数、参数类型一起柔和在一起,则函数名会是_func_int_float类似的东西。而这样的函数名显示与lib导入库的函数名匹配不了的。

//////////////////////////////////////////////////////////////

显式链接:

显式链接是应用程序在执行过程中随时可以加载DLL文件,也可以随时卸载DLL文件,这是隐式链接所无法作到的,所以显式链接具有更好的灵活性,对于解释性语言更为合适。

在显式链接下,应用程序必须进行函数调用以在运行时显式加载 DLL。为显式链接到 DLL,应用程序必须:

1调用 LoadLibrary(或相似的函数)以加载
DLL 和获取模块句柄。

2调用 GetProcAddress,以获取指向应用程序要调用的每个导出函数的函数指针。由于应用程序是通过指针调用
DLL 的函数,编译器不生成外部引用,故无需与导入库链接。

3使用完 DLL
后调用 FreeLibrary。

<span style="font-size:18px;">#include<iostream>
#include<Windows.h>
using namespace std;
void main(void)
{
typedef int(*pMax)(int a,int b);
HINSTANCE hDLL;
pMax Max;
hDLL=LoadLibrary("MyDll.dll");//加载动态链接库MyDll.dll文件;
Max=(pMax)GetProcAddress(hDLL,"Max");
int a=Max(5,8);
cout<<a<<endl;
FreeLibrary(hDLL);//卸载MyDll.dll文件;
System(“pause”);
}</span>

!!!!最后这里给出一个非常有用的命令

查看lib文件、dll文件,使用命令行:

>dumpbin /exports xxx.lib/xxx.dll

时间: 2024-10-09 22:06:36

DLL的前世今因的相关文章

Windows下尝试PHP7提示丢失VCRUNTIME140.DLL的问题解决

前天PHP7.0.0正式版发布了,有一些比较好的改进,官方也说速度比php5.6快了两倍,性能上有了很大提升,并且也发布了从php5.x向php7迁移的问题,所以今后php网站迁移后能够大幅度的提升网站性能,所以为了尝鲜我也去php官网下载了7.0的版本,通过命令行进行独立的测试,下载zip包后解压出来,下载后进入目录,将php.ini-development改为php.ini其余的参数暂时不用修改,然后在当前目录下新建test.php,输入简单的代码: 1 <?php 2 echo "H

win7 64位系统 PB连接oracle数据库出现“oracle library oci.dll could not be loaded”问题的解决方法

今天与大家分享一个自己的学习笔记,希望能给遇到同样问题的人带来帮助. 不知道大家在win7 64位系统下用 PB连接oracle数据库时,是否遇到过“oracle library oci.dll could not be loaded”问题. 今天,在win7 64位系统下用 PB连接oracle数据库时,一直出现上述错误,在百度上找了很久,都没有找到一个完整的解决方案,咨询了很多人,(他们都说是我的PB和oracle没装好,但我装的时候没出现任何问题,一切都很顺利,而且PB和oracle都能正

c#解决dll调用的问题

在做一个c#中间程序时,调用第三方的dll安装路径"Program Files" 和"Program Files (x86)"的问题,经过一段时间的研究,找到了下面的解决方案: 思路: 1.配置dll的加载路径 2.监控dll 3.dll加载失败判断操作系统位数(x86.x64) 4.动态加载dll 解决方案: 1.在config中添加dll引用配置: <runtime> <assemblyBinding xmlns="urn:schem

c# 无法加载xxx.dll 找不到指定的模块(如何指定文件夹)

如果直接放在项目运行目录,例如bin/debug可以直接加载,但是这样比较乱. 如果在放debug里面的一个文件夹里面,有可能会报错“无法加载xxx.dll 找不到指定的模块”. 如果路径写成这样就会报错 framework/linphone 解决方法:将/ 改成"\" framework\linphone

PL/SQL developer 连接oracle数据库报错“initialization error could not load oci.dll”

声明:PL/SQL 版本:PL/SQL Developer 9.0.6 (http://files.allroundautomations.com/plsqldev906.exe) 报错提示如图: 原因:PL/SQL只对32位OS进行支持,解决方法是额外加载一个oci.dll文件 解决办法:1.下载OCI.DLL相关库文件.地址: (需注册Oracle账号) http://www.oracle.com/technetwork/topics/winsoft-085727.html ----->  

DLL引用摘录

最近使用DllImport,从网上google后发现,大部分内容都是相同,又从MSDN中搜集下,现将内容汇总,与大家分享. 大家在实际工作学习C#的时候,可能会问:为什么我们要为一些已经存在的功能(比如Windows中的一些功能,C++中已经编写好的一些方法)要重新编写代码,C#有没有方法可以直接都用这些原本已经存在的功能呢?答案是肯定的,大家可以通过C#中的DllImport直接调用这些功能. DllImport是System.Runtime.InteropServices命名空间下的一个属性

Unity使用DLL库

Unity3D 能够很方便的集成一些外部插件,以便调用现有的动态链接库.下面会介绍Unity中如何集成Dll的两种方法.1. 标准引用这里所使用的语言是C#. 1.1 新建C#类库项目,这里就不多介绍了. 1.2 项目建好后正常的编写代码 以上是一个简单的测试代码. 1.3 将项目属性 -> 应用程序 -> 目标框架:改为 .NET Framework 3.5或以下 .这一步很重要,因为Unity3D(当前的Unity3D版本是3.5版) 支持的 .Net 是3.5版. 如果选择的是4.0版会

用VS2010编写的C++程序,在其他电脑上无法运行,提示缺少mfc100.dll的解决办法

问题: 在自己电脑上用VS2010编写的VC++程序(使用MFC库),不能在其他电脑上运行.双击提示: "无法启动此程序,因为计算机中丢失mfc100.dll 尝试重新安装该程序以解决此问题." 解决方案: 方案一 采用静态编译使用VS2010编译的程序在windowsxp中运行时,经常会出现找不到相关的DLL文件,我们可以使用静态编译的方法把这些运行依赖文件打包到*.exe中来,以减少对环境的依赖. 一般可以配置以下两项: 项目 -< **属性 -< 配置属性-<

LIB和DLL的区别与使用(转载)

转载自:http://www.cppblog.com/amazon/archive/2009/09/04/95318.html 共有两种库:一种是LIB包含了函数所在的DLL文件和文件中函数位置的信息(入口),代码由运行时加载在进程空间中的DLL提供,称为动态链接库dynamic link library.一种是LIB包含函数代码本身,在编译时直接将代码加入程序当中,称为静态链接库static link library.共有两种链接方式:动态链接使用动态链接库,允许可执行模块(.dll文件或.e