进行DLL的编程主要涉及到两个方面的问题,一个是要保证DLL中要导出的函数名不被编译器不可控地更改(在C++中由于重载机制的存在,会造成程序被编译时函数名被改变),其实就是要保证DLL导出的函数名与使用DLL时引用的函数名一致;另一个是怎样在应用程序中使用DLL的问题,实质就是怎样生成一个引用DLL的可执行程序的问题。解决这两个问题都有两种方法。下面就这两个问题进行详细讨论。

一、DLL导出函数的两种方法

DLL是包含若干个函数的库文件,其中包括内部函数和外部函数两种。前者用于DLL内部实现的调用,外部应用程序无法引用;后者则是提供给外部应用程序引用的函数,从而体现DLL的作用。应用程序在使用DLL中的函数之前,应该先导出欲发布的函数(即形成外部函数),这样才应用程序才能使用。要导出这些函数有两种方法,一是在定义函数时使用导出关键字__declspec(dllexport);另外一种方法是在创建DLL文件时使用模块定义文件.Def。下面通过两个例子来说明如何使用这两种方法创建DLL文件。

方法1:使用导出函数关键字__declspec(dllexport)创建MyDll.dll,该动态链接库中有一个函数,实现对两个数做加法。在MyDll.h和MyDLL.cpp文件中代码如下:

//MyDLL.h
#ifndef MYDLL
#define MYDLL extern "C" __declspec(dllimport)
#endif
MYDLL int Add(int a,int b);
//MyDll.cpp
#define MYDLL extern "C" __declspec(dllexport) #include"MyDll.h"
int Add(int a, int b)
{
return a+b;
}

该动态链接库编译成功后,在MyDll工程中的debug目录下,可以看到MyDll.dll、MyDll.lib两个文件。LIB文件中包含DLL文件名和DLL文件中的函数名等,该LIB文件只是对应该DLL文件的"映像文件",与DLL文件相比,LIB文件的长度要小的多,在采用隐式链接DLL时要用到它。对于MyDll.h中的extern"C"修改符。只有当你编写C++代码而不是直接编写C代码时,才能使用这个修改符。通常来说,C++编译器可能会改变函数和变量的名字,从而导致严重的连接程序问题。如果使用extern"C",就可以告诉编译器不要改变变量名或函数名,这样,变量和函数就可以供使用C、C++或其他任何编程语言编写的可执行模块来访问。

方法2用.def文件创建工程MyDll。.def文件为链接器提供了有关被链接程序的导出、属性及其他方面的信息。使用.def文件创建DLL,就不需要再在上个例子创建的工程中的MyDll.h文件中包括extern"C"_declspec(dllexport)声明部分了。在该工程中加入一个文本文件,命名为MyDll.def,再在该文件中加入如下代码:

LIBRARY MyDll
EXPORTS
Add @

其中LIBRARY语句说明该def文件是属于相应DLL的,EXPORTS语句下列出要导出的函数名称。我们可以在.def文件中的导出函数后加@n,如Add@1,表示要导出的函数顺序号,在进行显式连时可以用到它。该DLL编译成功后,打开工程中的Debug目录,同样也会看到MyDll.dll和MyDll.lib文件。

二、使用DLL的两种方法(DLL的链接)

应用程序使用DLL可以采用两种方式:一种是隐式链接,另一种是显式链接。在使用DLL之前首先要知道DLL中函数的结构信息。VisualC++6.0在VC\bin目录下提供了一个名为Dumpbin.exe的小程序,用它可以查看DLL文件中的函数结构。另外,Windows系统将遵循下面的搜索顺序来定位DLL:

1.包含EXE文件的目录

2.进程的当前工作目录

3.Windows系统目录

4.Windows目录

5.列在Path环境变量中的一系列目录。

方法1: 隐式链接

隐式链接就是在程序开始执行时就将DLL文件加载到应用程序当中。实现隐式链接很容易,简单地说,只要将导入函数关键字__declspec(dllimport)函数名写到应用程序相应的头文件中就可以了(如果DLL是按照上面的方式一导出函数,并且在头文件已经考虑了dllimport声明的情况,自然就不需要再在应用程序中加此关键字了)。具体操作步骤如下:

(1)当创建可执行源代码文件时,必须加上DLL的头文件。如果没有头文件,输入的符号将不会被定义,这样编译器编译就会无法通过。其实这样做,编译器只是想确保你用正确的方法访问这些输入的符号。——DLL的头文件,供编译程序使用;

(2)接下来,链接程序将所有的.obj模块组合起来,生成可执行模块。该链接程序必须确定哪些DLL包含代码所引用的所有输入符号。这由DLL相应的.lib提供。因此必须将DLL的.lib文件传递给链接程序。——DLL的.lib文件,供链接程序使用;

下面的例子通过隐式链接调用MyDll.dll库中的Add函数。

//TestDll.cpp

#include
#include"MyDll.h"//上面定义的DLL对应的头文件
#pragmacomment(lib,"MyDll.lib") //保证应用程序在链接时能链接成功
void main()
{
printf("%d\n",Add(,));
}

在创建DllTest.exe文件之前,要先将MyDll.h和MyDll.lib拷贝到当前工程所在的目录下面,这样才能编译生成DllTest.ext;而在生成DllTest.exe文件后,无论是隐式还是显示链接,都只需将MyDll.dll文件拷贝到DllTest.exe所在目录,DllTest.exe就能加载到DLL文件,从而正常执行。当然,按照前面提到的应用程序搜索定位DLL的顺序,也可以将DLL放到其他目录如windows的System下。

如果DLL使用的是def文件,要删除TestDll.h文件中关键字extern "C"。TestDll.h文件中的关键字Progamcommit是要Visual C+的编译器在link时,链接到MyDll.lib文件,当然,开发人员也可以不使用#pragmacomment(lib,"MyDll.lib")语句,而直接在工程的Setting->Link页的Object/Moduls栏加入MyDll.lib既可。

方法2: 显式链接

显式链接是应用程序在执行过程中随时可以加载DLL文件,也可以随时卸载DLL文件,这是隐式链接所无法作到的,所以显式链接具有更好的灵活性,对于解释性语言更为合适。在应用程序中用LoadLibrary或MFC提供的AfxLoadLibrary显式的将需要的动态链接库调进来,此后再用GetProcAddress()获取想要引入的函数的地址。自此,你就可以像使用在应用程序自定义的函数一样来调用此引入函数了。在应用程序退出之前,应该用FreeLibrary或MFC提供的AfxFreeLibrary释放动态链接库。下面是通过显式链接调用DLL中的Add函数的例子。

#include
#include
void main(void)
{
typedef int(*pAdd)(int a,int b);
HINSTANCE hDLL;
pAdd Add;
hDLL=LoadLibrary("add.dll");//加载动态链接库MyDll.dll文件;
Add=(pAdd)GetProcAddress(hDLL,"add");
printf("%d\n",Add(,));
FreeLibrary(hDLL);//卸载MyDll.dll文件;
}

在上例中使用类型定义关键字typedef,定义指向和DLL中相同的函数原型指针,然后通过LoadLibray()将DLL加载到当前的应用程序中并返回当前DLL文件的句柄,然后通过GetProcAddress()函数获取导入到应用程序中的函数指针,函数调用完毕后,使用FreeLibrary()卸载DLL文件。同样,在运行程序时,需要先将DLL文件拷贝到系统能够搜到DLL文件的路径下。

使用显式链接的应用程序在编译生成时不需要使用前面提到的任何Dlltest.h头文件以及Lib文件。另外,使用GetProcAddress()函数时,可以利用MAKEINTRESOURCE()函数直接使用DLL中函数出现的顺序号,如将GetProcAddress(hDLL,"Add")改为GetProcAddress(hDLL,MAKEINTRESOURCE(1))(函数Add()在DLL中的顺序号是1),这样调用DLL中的函数速度很快,但是要记住函数的使用序号,否则会发生错误。

三、一点补充说明 

编译生成dll文件后,我们同时得到lib文件。这个lib和静态链接库的lib文件是完全不一样的:

静态链接库的lib文件包含实现的二进制码,链接后就不要lib的支持了,lib中的代码会合并到exe文件中。

dll输出的lib,链接后还要dll的支持。因为它仅含导出函数的地址和一些位址信息,而不包括实现信息,这可以帮助Link程序完成连接(在此时安排调用入口地址及函数回调信息),调用模块可以模拟这个lib去修改相应的import项。这样,在运行时才能将DLL中真正的代码调入执行,实现动态连接。

参考文献:Windows核心编程

DLL编程总结的更多相关文章

  1. VC++动态链接库(DLL)编程深入浅出(zz)

    VC++动态链接库(DLL)编程深入浅出(zz) 1.概论 先来阐述一下DLL(Dynamic Linkable Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用 ...

  2. 基于Visual C++6.0的DLL编程实现

    整理自基于Visual C++6.0的DLL编程实现 本文通过通俗易懂的方式,全面介绍了动态链接库的概念.动态链接库的创建和动态链接库的链接,并给出个简单明了的例子,相信读者看了本文后,能够创建自己的 ...

  3. MFC下DLL编程(图解)

    MFC下DLL编程(图解) DLL(Dynamic Link Library,动态链接库)是微软公司为Windows和OS/2操作系统设计一种供应用程序在运行时调用的共享函数库.DLL是应用程序的一种 ...

  4. VC++动态链接库(DLL)编程深入浅出

    1.概论 先来阐述一下DLL(Dynamic Linkable Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用的变量.函数或类.在仓库的发展史上经历了“无库-静 ...

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

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

  6. 重要:VC DLL编程

    VC DLL编程 静态链接:每个应用程序使用函数库,必须拥有一份库的备份.多个应用程序运行时,内存中就有多份函数库代码的备份. 动态连接库:多个应用程序可以共享一份函数库的备份. DLL的调用方式:即 ...

  7. 深入Delphi下的DLL编程

    深入Delphi下的DLL编程 作者:岑心 引 言 相信有些计算机知识的朋友都应该听说过“DLL”.尤其是那些使用过windows操作系统的人,都应该有过多次重装系统的“悲惨”经历——无论再怎样小心, ...

  8. 在DLL编程中,导出函数为什么需要extern "C"

    转自:http://blog.csdn.net/zhongjling/article/details/8088664 一般来讲,在DLL编程过程中,对于导出的函数前 都需要加入 extern “C”, ...

  9. VC++动态链接库(DLL)编程深入浅出(四)

    这是<VC++动态链接库(DLL)编程深入浅出>的第四部分,阅读本文前,请先阅读前三部分:(一).(二).(三). MFC扩展DLL的内涵为MFC的扩展,用户使用MFC扩展DLL就像使用M ...

  10. VC++动态链接库(DLL)编程深入浅出(一)

    1.概论 先来阐述一下DLL(Dynamic Linkable Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用的变量.函数或类.在仓库的发展史上经历了“无库-静 ...

随机推荐

  1. #define SYSTEMSERVICE(_func) KeServiceDescriptorTable.ServiceTableBase[ *(PULONG)((PUCHAR)_func+1) 这

    这个跟KeServiceDescriptorTable的结构有关 下面是KeServiceDescriptorTable的结构定义 KeServiceDescriptorTabletypedef st ...

  2. 命令学习_ping

    PING: ping是一个所有操作系统都支持的简单工具.我么可以利用ping来解析DNS 的A record和PTRrecord. A记录是将域名映射到IP地址,这个是ping的缺省功能, ping同 ...

  3. latex ctex 的section不能写中文, /href

    问题描述:再使用超链接 /href 后发现section{}不能写入中文,以前是好使的,经过查询验证,需要在引导区里加入 \hypersetup{CJKbookmarks=true} 即可恢复正常.

  4. let能否完全替代IIFE

    let是什么 http://es6.ruanyifeng.com/#docs/let 最近,我写了一篇关于syntax of Java’s IIFE pattern的文章,来解释为什么我们用现在的方式 ...

  5. IO初步,字节输入流和字节输出流

    字节输出流 OutputStream(基类,抽象) 特点:写任意的文件 方法:写出数据的方法:write write(int b) 写出1个字节 -128~127之间,写的是一个ASCLL码的值 wr ...

  6. Vim操作 -- 多段复位粘贴

    Vim可以多段复制.粘贴.即,内容X复制到寄存器“1”,内容Y复制到寄存器“2”:粘贴时可以选择从“1”还是“2”粘贴. (1) Vim有13个粘贴板,分别是0.1.2.....9.a.“.+:用:r ...

  7. half adder vs. full adder

    1, half adder 2-input, 2-output input: A, B; output out, carry; 2, full adder 3-input, 2-output inpu ...

  8. Excel生成Oracle数据库表sql工具类

    1.解决问题: 开发文档中字段比较多的时候,建表sql(Oracle下划线命名规范)比较麻烦,容易出错~~ (主要是懒) 特意手写一个工具,根据excel字段,生成建表的sql语句. ~~~末尾附Gi ...

  9. BZOJ 2281 消失之物

    ftiasch 有 N 个物品, 体积分别是 W1, W2, -, WN. 由于她的疏忽, 第 i 个物品丢失了. "要使用剩下的 N – 1 物品装满容积为 x 的背包,有几种方法呢?&q ...

  10. 模板——AC自动机

    传送门:QAQQAQ 定义nxt[u]=v表示从u开始不断沿着失配边跳到的第一个是标记点的端点v,那么我们再匹配时沿着last跳,每跳到一个last,它就一定对应一个模式串,所以效率是非常高的. 和K ...