Chap 19 动态链接库

动态链接库详解
本文详细介绍了动态链接库(DLL)的工作原理,包括DLL的作用、不同类型DLL的功能介绍、DLL的调用方式(隐式与显式),以及程序加载DLL时的路径顺序等内容。

 

Chap 19 动态链接库

一.只有在其他模块调用动态链接库中的函数时,它才发挥作用。Win32 API 中所有的函数都在dll 中,其中最重要的三个dll

1.     kernel32.dll: 所有与内存操作,线程,进程相关的操作。

2.     user32.dll: 所有与执行用户界面相关的操作。

3.     gdi32.dll: 所有用于画图和显示文本的操作。

 

二.动态链接库分为隐式调用和显式调用:

(1)    隐式调用

a.       DEF 的方法制作 dll ,则不需要加 dllexport dllimport 来声明导出函数,而是在 def 文件中指明导出函数

LIBRARY “dll”

EXPORTS

        Add

        Sub

这里指明的导出函数的名字要和程序里的函数名一致,或者不一致时,可以用

Exportedname = InFuncName 来导出不同于程序中函数名的函数名。

这个方法比较简单,而且不会进行名字改编。

b.       dllexport dllimport 的方法制作 dll

dll.h 文件中:

#ifdef DLL_EXPORTS

#define DLL_API extern “C” _declspec (dllexport )

#else

#define DLL_API extern C _declspec (dllimport)

#endif

       DLL_API int add(int a,int b)

       在制作 dll 的工程中加上预处理器 DLL_EXPORTS ,这样在制作 dll 的工程时,会把 add 函数导出,在使用 dll 的工程中由于没有定义 DLL_EXPORTS ,会把 add 函数当成是导入函数,告诉编译器这个函数是从其他的 dll 中导入的。其实不加 dllimport 也可以编译通过并成功运行,但是加了可以使运行效率更高。

(2)    显式调用

这种方式调用 dll 时,那么只有在程序需要加载 dll 的时候才会将 dll 加入内存,而不会

像第一种方法,在程序开始的时候就加载 dll 到内存中,这样,可以节省内存,提高效率。调用方法:

       HINSTANCE hInst;

       hInst = LoadLibrary (“dll.dll”);

       typedef int (*ADDPROC)(int a,int b); // 定义函数指针,获取函数地址

       ADDPROC add = (ADDPROC)GetProcAddress (hInst, “add”); // 注意 add 为函数的名称,如果不是使用 def 方式得到的 dll ,那么函数名字发生了改编,前后加了些符号,这样就不能得到 add 函数的地址了。

       add(1,2);

       FreeLibaray (hInst);

       当一个 dll 使用 _stdcall 的调用约定时,调用它的时候也要使用 _stdcall 的调用约定,这样就可以将上述一行改为 :

       typedef int (_stdcall *ADDPORC)(int a,int b);

      

三.程序加载 dll 时搜索 dll 的路径顺序:

1.       程序的执行目录 (exe 所在目录 )

2.       当前目录( vcproj 所在目录)

3.       系统目录: c:/windows/system32,c:windows/system,c:/windows

4.       环境变量 path 中所列出的路径

 

四.名字改编和调用约定:

1.       C++ C

C++ C 语言在编译时编译器会做不同的处理:由于 C++ 有函数重载的机制,所以在 C++ 语言中 void f1(int a) 在编译完后进行名字改编为 f1_int, C 语言中同样的函数在编译完后就是 f1, 即不进行名字改编,所以当 C++ 调用 C 语言生成的函数或函数库时链接器会无法正确的链接相应的函数,同样, C 调用 C++ 生成的函数库时链接器也无法解析相应的函数。为了解决这个问题, C++ 提供了限定符 extern “C” 来解决这个问题:

假设 cfile.h( 内含 f1 函数 ),cfile.c C 文件,那么在 cplusplus.cpp 中引用 cfile.h 时,应该:

extern “C”

{

#include “cfile.h”

}

 

int main()

{

extern “C”

{

       f1();

}

}

这样,编译器在编译 cplusplus.cpp 的时候,知道按照 C 的方式去处理 f1 函数,及不进行名字改编,那么就能够识别 C 语言的 f1 函数。

2.       调用约定

VC++ 中默认的调用约定是 C 调用约定 _cdecl ,而标准调用约定是 _stdcall 。在调用约定

改变时,名字改编也不一致。例如,对于C 语言的 add(int,int) 函数,如果用 _cdecl 编译时不进行名字改编,但是用 _stdcall 时,会改变成 _add@8 ,这样,别的用 _stdcall 调用约定的编译器就无法调用 add 函数, DEF 文件能够解决这个问题,用 def 文件导出的 dll ,函数名字不进行改编,也就是说,用 DEF 制作的 dll ,不管是什么调用约定,都不发生名字改编。但是,函数用什么调用约定编译的,调用者就应该使用什么调用方式来调用函数,否则即使函数名字不发生改编,也无法正确调用。也就是说,名字改编与否和调用约定一致与否,这两个条件一起决定了函数能否被其他模块正确调用。

       另外, main 函数只能用 _cdecl 调用约定,这个不可更改。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值