1、DLL基本理论

在Windows操作系统中,几乎所有的内容都是以DLL的形式存在的。

1、DLL基本概念

语言程序要从目标代码(.obj)外部引用函数,可以通过俩种途径实现——静态链接和动态链接。

1.1、静态链接

该种方式下,链接程序首先对库文件(.lib)进行搜索,直到在某个库中,找到包含函数的对象模块为止。然后,链接程序把这个对象模块复制到可执行文件(.exe)中。链接程序负责维护对该函数的所有引用。

1.2、动态链接

该种方式下,链接程序同样先对库文件(.lib)进行搜索,直到在某个库中,找到所引用函数的输入记录为止。注意,不同于静态链接的对象模块,这里查找的是输入记录,它不直接包含函数的代码或者数据,而是指定一个动态链接库(.dll)。该动态链接库包含函数的函数名(或函数序号)和代码。然后,链接程序把输入记录复制到可执行文件中,产生一次对该函数的动态链接。

2、DLL的分类

微软的Visual C++支持3种DLL,它们分别是Non-MFC DLL、Regular DLL和Extension DLL。

2.1、Non-MFC DLL

这种动态链接库没有采用MFC的类库结构,而是直接用C语言写的DLL。它的导出函数是标准的C接口,能被MFC和非MFC编写的应用程序调用。相比采用MFC类库编写的DLL,它占用很少的磁盘和内存空间。

2.2、Regular DLL

这种动态链接库和下面的Extension DLL一样,是MFC类库编写的。它的一个显著地特点是,在源文件里有一个继承自CWinApp的类,导出的对象可以是C函数、C++函数或者C++成员函数。只要应用程序能调用C函数,它就可以调用常规DLL。

2.3、Extension DLL

这种动态链接库是由MFC的动态链接版本创建,且只能被使用MFC类库编写的应用程序调用。(程序员必须在DLL的DllMain函数中添加初始化代码和结束代码)

3、DLL的工作原理

应用程序打开动态链接库时,把动态链接库的执行代码映射到进程的地址空间中。而动态链接库中的数据,应用程序则不是通过映射方式获取,而是做了一个备份。

3.1、文件映射

当应用程序调用动态链接库时,系统首先要为这个动态链接库建立一个文件映射视图,然后搜寻调用者的地址空间,将文件视图映射到进程的地址空间中。如果同时又有另外一个应用程序使用这个动态链接库,那么只是简单地将这个动态链接库的另一个文件视图映射到这个进程的地址空间。使用这个动态链接库的每一个进程,都是这个动态链接库的一个映射视图。

3.2、引用表

将文件映射到位后,系统要检查调用者和动态链接库的引用表,并把每个DLL函数新分配的虚地址插入调用者的输入库中。如果系统需要调整动态链接库中的引用表,需要复制被修改过的内存页面。由于多个进程调用相同的动态链接库时,这个动态链接库会被映射到不同的虚地址,所以不同进程对这个动态链接库模块进行了不同的地址修改。

3.3、内存分配

在默认情况下,动态链接库为变量分配的所有内存空间,都是由调用这个动态链接库的进程的堆分配的。

2、简单DLL示例

 //AddDLL.h: interface for the AddDLL ckass
#ifndef ADDDLL_H
#define ADDDLL_H
extern "C" int __declspec(dllexport)add(int x,int y); //定义导出函数
#endif //AddDLL.cpp: implementation of the AddDLL class
#include "AddDLL.h"
int add(int x, int y) //定义加法函数
{
return x + y;
} void CTestAddDLLDlg::OnAdd()
{
// TODO: Add your control notification handler code here
UpdateData(true);
typedef int(*lpAddFun)(int, int); //宏定义函数指针类型
HINSTANCE hDll; //DLL句柄
lpAddFun addFun; //函数指针
hDll = LoadLibrary("..\\Debug\\AddDLL.dll"); //载入DLL if(hDll != NULL)
{
addFun = (lpAddFun)GetProcAddress(hDll,"add"); //获得DLL中函数的入口地址
if(addFun != NULL)
{
int result = addFun(m_X,m_Y); //调用函数
CString s;
s.Format("调用AddDLL,运算结果为:%d",result);
MessageBox(s); //显示结果
}
}
FreeLibrary(hDll);
}

1、例程分析

AddDLL.h文件中的 __declspec(dllexport) 语句是对函数add()声明为导出函数。

(1)语句typedef int(*lpAddFun)(int, int)  定义了一个函数指针类型,它与add()函数的参数和返回值类型均相同,随后,定义了lpAddFun的实例addFun。

(2)定义了一个DLL HINSTANCE句柄实例hDll,通过Win32 API函数LoadLibrary()加载DLL模块,并将DLL模块句柄赋值给hDll。

(3)通过调用Win32 API函数GetProcAddress(),得到了DLL中的add()函数地址,并把它赋值给addFun。由函数指针addFun完成对DLL中add()函数的调用。

(4)应用程序使用完DLL后,通过Win32 API函数FreeLibrary()释放已经加载的DLL模块。

总结:(1)DLL中需以某种特定的方式声明导出函数、变量或类。

(2)应用程序需以某种特定的方式调用DLL的导出函数、变量或类。

2、导出函数

DLL文件中包含一个导出函数表,给出了DLL中每个导出函数的名字和标识号。另外,函数表中还包含了DLL中函数的地址。动态链接过程将加载DLL模块时,动态建立一个函数调用与函数地址的对应表。从DLL导出函数主要有俩种方式:

(1)创建模块定义文件(.def),并把导出函数名加入模块定义文件中;

(2)在定义函数时使用__declspec(dllexport)关键字。

2.1、使用模块定义文件导出函数

模块定义文件是一个由多个语句组成的文本文件,一般包含以下语句:

(1)LIBRARY语句。DEF文件的第一条语句必须是LIBRARY语句,用来表明DEF文件将描述一个动态链接库,而非应用程序。LIBRARY后面紧接的事动态链接库的名称,用来在DLL的导入库中标识该动态链接库。

(2)DESCRIPTION语句。LIBRARY语句之后一般是DESCRIPTION语句,用来描述DLL的主要功能。

(3)EXPORT语句。该语句列出动态链接库要输出的函数。用户还可以给每个要输出的函数指定输出序列号,只要在对应的函数名之后加上符合@和一个数字即可。

2.2、使用_declspec(dllexport)关键字输出DLL

用户可以使用_declspec(dllexport) 关键字输出DLL里的数据、函数、类等,这样就不需要DEF文件了。

_declspec(dllexport) 将会把函数名字存在DLL的输出表里,但不存储函数的序列号。MFC为_declspec(dllexport) 定义了宏:AFX_CLASS_EXPORT、AFX_DATA_EXPORT、AFX_API_EXPORT。使用MFC,可以直接调用这些宏。

3、导入函数

DLL中实现了导出函数,而在应用程序中,必须声明相应的导入函数。在应用程序中可以使用_declspec(dllimport)关键字来导入函数。

3、访问动态链接库

在应用程序中访问DLL,实际上就是将应用程序中的导入函数与DLL文件中导出函数进行链接。有俩种链接方式:隐式链接和显式链接。

隐式链接是指通过编译器,给应用程序提供DLL的名称和DLL函数的链接地址;显式链接则由应用程序通过代码加载DLL,编译器不需要知道任何关于DLL的信息。

1、隐式链接

要实现对DLL的隐式链接,应用程序要从DLL提供者处获得以下3个文件:

(1)包含有关DLL输出函数声明的头文件;

(2)DLL的导入库(.LIB)文件;

(3)动态链接库(.DLL)文件。

LIB文件中也包含了对应的DLL文件名,链接程序将其存储在EXE文件内部。当应用程序运行过程中需要加载DLL文件时,Windows根据这些信息发现并加载DLL,然后通过函数名或标识号实现对DLL函数的动态链接。

2、显式链接

显式链接方式要求应用程序以函数的形式实时地装入DLL,其实现过程表示如下:

(1)调用Win32的LoadLibrary()函数,指定DLL的路径作为参数,函数将返回DLL模块的句柄;

(2)调用GetProcAddress()函数,获取应用程序希望访问的DLL函数的入口指针;

(3)通过DLL函数指针访问DLL函数;

(4)当DLL使用完后,调用FreeLibrary()函数来卸载DLL。

2.1、获取DLL的句柄

应用程序通过调用LoadLibrary()函数把动态链接库装入内存,其函数定义如下:

HINSTANCE LoadLibrary(LPCTSTR lpLibFileName);

lpLibFileName:字符串变量,表示要被调用的动态链接库的名称。

当应用程序使用LoadLibrary()函数试图把一个动态链接库装入内存时,会先从系统中查找,如果这个动态链接库已经被装入内存,则系统会增加这个动态链接库的使用计数,并返回这个动态链接库的句柄。但如果这个动态链接库并没有被装入内存,则系统会搜索这个文件,并把它装入内存,返回这个动态链接库的句柄。

2.2、获取函数的内存地址

当获得一个动态链接库的句柄后,就可以通过调用GetProcAddress()函数取得这个动态链接库中的一个函数的内存地址。其函数定义如下:

FARPROC GetProcAddress(HMODULE hModule, LPCSTR lpProcName);

hModule:表示函数所在的动态链接库的句柄。

lpProcName:表示函数的名字。

2.3、卸载动态链接库

当这个动态链接库使用完后,需要调用FreeLibrary()函数来卸载动态链接库,其函数定义如下:

BOOL FreeLibrary(HMODULE hLibModule);

hLibModule:表示待卸载的动态链接库的句柄。

如果只有一个进程使用这个动态链接库,则这个函数执行完后,动态链接库的引用计数会减1,当其引用计数减少到0时,该动态链接库会从系统内存中卸载。

2.4、判断动态链接库是否被装入系统

可调用GetModuleHandle()函数判断动态链接库是否被装入系统,其定义如下:

HMODULE GetModuleHandle(LPCTSTR lpModuleName);

lpModuleName:表示待检测的动态链接库的文件名称。

如果这个动态链接库没有被调入内存,那么这个函数会返回NULL,如果已经被进程通过调用LoadLibrary()函数调入内存,并且映射到当前进程的地址空间,则将返回动态链接库的句柄。(并不增加对动态链接库的引用计数)

3、链接方式的选择

采用隐式链接,应用程序EXE文件被加载,会将其调用的所用DLL文件加载到内存中;但采用显式链接,程序员可以决定DLL文件何时加载或不加载。

4、常规DLL和扩展DLL

1、MFC中的常规DLL可分为俩种——静态链接到MFC的常规DLL和动态链接到MFC的常规DLL。二者的区别是:前者使用的是MFC的静态链接库,生成的DLL文件长度大;后者使用MFC的动态链接库,生成的DLL文件长度小。

2、常规DLL会自动生成DEF文件,并编写导出函数,在头文件(.h)中声明导出函数,在源文件(.cpp)文件中编写实现代码。

3、实现隐式链接只要将导入函数的关键字_declspec(dllimport)和函数名写到应用程序对应的头文件中即可。

     #pragma comment(lib, "xxxx.dll")                   //载入DLL

     extern "C" _declspec(dllimport) void MsgBox();     //载入函数

而后就可以直接使用函数MsgBox()。当然也可以在Setting->Link页的Object/library modules栏填入xxxx.dll。

4、MFC中扩展DLL的特点是用来建立MFC的派生类,该DLL只能被用MFC类库所编写的应用程序调用。扩展DLL和常规DLL不一样,它没有一个从CWinApp继承而来的类的对象,编译器创建了一个DLL入口函数——DLLMain(),可以在此函数中实现DLL的初始化。

初解DLL基本知识的更多相关文章

  1. Delphi下DLL编程知识(转)

    一.  DLL和系统变量     在 System 单元声明的变量中,有几个对DLL编程有特殊影响.IsLibrary 可以检测代码是执行在应用程序中还是执行在DLL中,在应用程序中 IsLibrar ...

  2. DLL基本知识

    一.生成方式: 使用DEF文件定义导出接口或使用__declspec(dllexport)描述接口,编译链接后生成dll+lib,其中lib是导入库,里面只有对导出接口的描述,而没有具体实现. 二.链 ...

  3. 可能是最简单的把C++Lib包装成C#可用dll的方法

    (想直接看结果的直接翻到最后) 之前对C++接触不多,最近工作需要,第三方给了一个C++的lib库,我们需要把它封装一下在C#中调用.对方要是直接给Dll就省事了... 研究了一下,基本有三个方向: ...

  4. MFC DLL 导出函数的定义方式

    一直在鼓捣DLL,每天的工作都是调试一个一个的DLL,往DLL里面添加自己的代码,但是对于DLL一直不太了解啊!今天一查资料,才发现自己对于DLL编写的一些基本知识也不了解.要学习,这篇文章先总结DL ...

  5. [原]调试实战——使用windbg调试DLL卸载时的死锁

    原调试debugwindbg死锁deadlock 前言 最近我们的程序在退出时会卡住,调查发现是在卸载dll时死锁了.大概流程是这样的:我们的dll在加载的时候会创建一个工作线程,在卸载的时候,会设置 ...

  6. 原创 C++应用程序在Windows下的编译、链接:第二部分COFF/PE文件结构

    2.1概述 在windows操作系统下,可执行文件的存储格式是PE格式:在Linux操作系统下,可执行文件的存储格式的WLF格式.它们都是COFF格式文件的变种,都是从COFF格式的文件演化而来的. ...

  7. 深入理解 Win32 PE 文件格式

    深入理解 Win32 PE 文件格式 Matt Pietrek 这篇文章假定你熟悉C++和Win32. 概述 理解可移植可执行文件格式(PE)可以更好地了解操作系统.如果你知道DLL和EXE中都有些什 ...

  8. 【转帖】C# DllImport 系统调用使用详解 托管代码的介绍 EntryPoint的使用

    1      DLLImport的使用 using System; using System.Runtime.InteropServices; //命名空间 class Example { //用Dl ...

  9. COFF/PE文件结构

    COFF/PE文件结构 原创 C++应用程序在Windows下的编译.链接(二)COFF/PE文件结构 2.1概述 在windows操作系统下,可执行文件的存储格式是PE格式:在Linux操作系统下, ...

随机推荐

  1. 微软测试版Visual Studio for Mac下载

    地址:https://www.visualstudio.com/thank-downloading-visual-studio-mac/?sku=vsmac#

  2. 继承Animation

    package cativity.cyq.alphaanimal; import android.view.animation.Animation; import android.view.anima ...

  3. POJ1064

    #include <iostream> #include <iomanip> #include <cmath> using namespace std; int N ...

  4. Part 12 DateTime functions in SQL Server

    DateTime functions in SQL Server IsDate, Day, Month, Year and DateName DateTime functions in SQL Ser ...

  5. Part 72 to 81 Talking about Dictionary and List collection in C#

    Part 72   What is dictionary in c# Part 73   What is dictionary in c# continued Part 74   List colle ...

  6. jquery设置元素readonly和disabled(checkbox只读)

    jquery api中提供了对元素应用disabled和readonly属性的方法:1,readonly 代码示例: $('input').attr("readonly",&quo ...

  7. BoneCP主要配置参数

    二.BoneCP主要配置参数 1.jdbcUrl 设置数据库URL 2.username 设置数据库用户名 3.password 设置数据库密码 4.partitionCount 设置分区个数.这个参 ...

  8. Matlab摄像头标定得出的参数保存为xml

    最近在做双摄像头的立体匹配,发现OpenCV定标效果不如MatLab的效果,于是用MatLab标定箱做标定,将得到的结果保存为xml,然后,提供给opencv使用.MatLab标定箱做标定得到的结果如 ...

  9. 移动Web轮播图IOS卡顿的问题

    晚饭前,被测试吐槽说,banner轮播手动左右滑的时候会卡顿.我一看不科学啊,大水果手机怎么会卡顿.我一看测试手中拿的是iPod,我觉得大概是这小玩意性能不强悍,后来又拿来5S,依然会卡顿,有趣的是, ...

  10. MySQL之左连接与右连接

    左连接: select 列1,列2,列N from tableA left join tableB on tableA.列 = tableB.列(正常是一个外键列) [此处表连接成一张大表,完全当成一 ...