Windows平台下动态链接库的总结

33 篇文章 5 订阅
11 篇文章 0 订阅

1、 动态链接库与静态连接库

       静态连接库与动态链接库都是经过编译器编译之后的,在计算机上可以直接运行的二进制目标文件,就像exe文件一样,但不同于exe文件的是静态链接库和动态链接库不可以独立运行,一般而言,动态链接库和动态链接是在内部实现了一些功能,对外提供了一组接口函数使得外部的程序能够通过这些对外的接口函数来使用其内部的功能。

       静态链接库,是在调用该静态库的可执行文件编译的时候(更确切地说是链接的时候)就将其中的内容整合进目标文件,而动态链接库则是在运行或加载时才将动态库中的内容整合进目标文件。

       静态连接库命名为XXX.lib,它在程序链接时候将被整合进主程序,假如我们要开发一个计算器软件,该软件的主程序为Calc.exe,在开发Calc的时候使用到了两个静态链接库 add.lib和multi.lib ,其中静态库add.lib主要用于对各种类型数据的加、减操作,静态库multi.lib主要用于对各种类型数据的进行高效率的乘法和除法操作。在开发Calc.exe的时候,我们就必须在编译时用到这两个静态链接库 add.lib和multi.lib,最终我们发布的程序只有calc.exe,没有add.lib和multi.lib,因为在生成目标文件calc.exe的时候,编译工具就已经把这两个静态库中的内容整合进了calc.exe中,这时候用户拿到的计算器程序就一个文件:Calc.exe。因此静态库只是提供给开发人员使用,对于最终用户来说是不可见的。

       动态链接库一般命名为XXX.dll,它在程序链接的时候不会被整合进目标文件,因此在发布应用程序的时候,需要一并把所需的动态库文件提供给用户。按照上一个例子,要开发名为Calc.exe的程序,但是在开发的时候用到了两个动态库add.dll和multi.dll,其中add.dll主要用于对各种类型数据进行高效的加减操作,add.dll主要用于对各类数据进行高效的乘除操作,在编译的时候,我们并不需要这两个dll文件,dll文件是在运行的时候需要的,如果采用显示动态加载dll的方式,则只需要一个头文件就可以了,如果要采用隐式加载时链接dll的方式,则还需要对应的lib文件。在Calc的开发过程中,编译器并没有将dll中实现的内容放进Calc.exe中,因此我们程序在发布的时候,就不能只发布Calc.exe,还需把Calc.exe所依赖的动态库add.dll和multi.dll一起提供给用户,这时候用户拿到的计算器程序包含了三个文件:Calc.exe、add.dll和multi.dll,其中Calc.exe是可执行程序,用户只需双击它就可以使用。

2、创建动态链接库

       动态库的开发基本与正常程序开发一样,只是它不需要main函数,当然dll里面也有个DllMain函数,但是我们一般不使用它。创建动态库的一般步骤如下:

(1)  创建头文件,在该头文件中主要包含待导出的接口函数,该头文件需和动态库一起提供给使用者,因为在使用动态库的时候还需要包含此头文件,这样动态库的使用者才知道动态库的导出函数是怎么定义的。在windows下动态库中的函数如果不特殊说明默认是不导出的,因此如果需要导出一个函数,需要在函数的声明时使用__declspec(dllexport)进行告诉编译器声明的函数将被导出。

(2)  创建源文件,在源文件中来实现dll的所有功能。这些源文件不会提供给使用者。

(3)  编译各源文件,将每个源文件都分别编译成obj文件。

(4)  链接obj文件,连接起将所有的obj文件链接起来形成一个dll文件。

(5)  生成lib文件,当动态库中导出的符号(这里的符号是指导出的函数或变量)超出一个时,还会生成一个lib文件,这里的lib文件不像静态库中生成的lib文件(静态库的lib文件中包含了静态库的全部实现内容),它里面没有实现代码,只是包含导出的符号;动态库的实现代码被放在dll中,lib文件中只是简单的包含导出的符号和其他一些链接信息,以供隐式使用动态库所用。

       下面以VS2008为例来创建一个Calc.exe所需要的动态库add.dll

(1)  File =>New =>Project在弹出的对话框中依次进入:     

Visual C++ => Win32 => Win32 ConsoleApplication

在Name中填写dll工程的名字add,在Location中选择add功所要保存的路径,点击OK,在接下来的一个页码中不作修改直接点击Next,进入Application Settings页面中选择DLL(这里默认的是Console application),然后点击Finish,创建一个空的dll工程。此时可以看到VS已经自动生成了dlmain.cpp,在该文件中就有DllMain的默认定义。这里暂时不做修改。

(2)  添加头文件add.h,

#ifndef _ADD_H

#ifdef __cplusplus

  #defineMY_EXPORT extern “C”__declspec(dllexport)

#else

  # defineMY_EXPORT __declspec(dllexport)

#endif //__cplusplus

 

MY_EXPORT int add(int iVal1, int iVal2);

#endif //_ADD_H

       这里extern “C”是C和C++混合一起编程时需要使用的修饰符号。另外,编译器看到__declspec(dllexport)符号时,会为其所修饰的函数、类、变量等生成一些额外的信息,这些额外的信息在使用动态库的时候会用到。

(3)  添加源文件add.cpp,在源文件中添加导出函数的实现。

#include “add.h”

const int DEFAULT_ADD_VALUE = 100;

int add (int iVal1, int iVal2)

{

       return iVal1 + iVal2 + DEFAULT_ADD_VALUE;

}

(4)  Build 该dll工程,可以在其Debug或Release目录下看到生成了两个文件:add.dll和add.lib

在创建动态链接库的时候需要注意:

(1)  避免导出变量,导出变量将不利于动态库的维护和扩展。

(2)  慎重导出类,因为只有当导出C++类的模块使用的编译器和导入C++类的编译器为同一厂商提供时,才可能保证没有问题。

3、使用动态库连接库

       动态链接库有两种使用方式:“隐式载入时链接”和“显示运行时链接”。“隐式载入时链接”方式在程序启动时就需要把所有依赖的动态库载入,即时在程序中没有执行动态库中的代码也需要将动态库加入,如果所依赖的动态库不存在,则程序无法启动;“显示运行时链接”方式需要在程序中通过函数LoadLibrary来显示的加载动态库,它是运行时才加载所使用的动态库,如果程序中没有执行到加载动态库的操作,则动态库不会被加载进来;例如:

boolbUseLib = false;

if(bUseLib)

{

           HMODULE hdll = LoadLibrary(“add.dll”);

           Typedef int (*TADD)(int,int);

           TADD add = GetProcAddress(hdll, “add”);

           cout<<add(12,13)<<endl;    

}

       此时,在上面的代码中动态库的加载不会被执行,此时即使没有add.dll程序也可以正常执行。

相反如果在隐式动态库中,其实现代码可能为如下形式:

boolbUseLib = false;

if(bUseLib)

{

           cout<<add(12,13)<<endl;    

}

       此时,在程序刚启动时就会加载add.dll,在上述代码中根本就没走add这个分支,但是没有了add.dll程序还是启动不了。

在VS2008中,隐式载入时链接的配置与使用方式,需要注意的是Debug和Release模式需要分别配置,配置文件也要对应,例如工程的Debug需要配置Debug模式的动态库,工程的Release模式需要配置Release模式的动态库:

[1]添加动态库的头文件路径:项目属性=>C/C++=〉General=〉Additional Include Directories,在其中填入动态库头文件的路径。

[2]配置lib文件的路径:项目 属性=>Linker=〉General=〉Additional Library Directories,在其中填入lib文件的路径。

[3]配置所依赖的lib文件名:项目 属性=>Linker=〉Input=〉AdditionalDepedencies,在其中填入所依赖的lib文件名称。

[4]将所依赖的dll文件拷贝到Debug或Release目录下;或者将dll的路径添加到环境变量中;或者将dll拷贝到windows下面system32目录下。

[5]在工程中包含dll的头文件,然后在程序中就可以直接使用dll中的函数了。

4、可执行程序搜索动态库的顺序

       在运行时,可执行程序将按照下面的顺序来搜索动态库,搜索到所需要的动态库之后再进行加载,下面以 Calc.exe、add.dll、multi.dll组成的计算器程序在执行时加载动态库为例(假设这三个文件都放在Calc目录下):

(1)  可执行程序所在的目录,如本例中Calc.exe所在的Calc目录。

(2)  Windows的系统目录,该目录可通过函数GetSystemDirectory得到,在xp系统下就是C:\WINDOWS\system32

(3)  Windows的System32子目录

(4)  Windows目录,此目录可以通过函数GetWindowsDirectory得到。

(5)  进程的当前目录,注意进程的当前目录不一定是启动目录,在使用过程中有可能被更改。

(6)  环境变量path中所列出的目录。

5、创建和使用动态库时需要注意的事项

(1)  跨编译器使用dll时要防止导出符号的改名。假如dll的编译与使用都是同一家厂商的编译器,则不存在问题,如果用MS VC++开发的dll要用到其它非MSVC++的编译器的项目上时,需要采取一些预防措施来防止导出符号被改名,可以通过以下两种方式来防止导出符号被改名:

[1]在编译dll的时候增加一个.def文件,并在该文件中添加EXPORT段,然后将导出的符号列在EXPORT之后即可,例如:

EXPORT

        add

[2]在编译dll的时候,在源文件的开始添加类似如下的代码:

#pragmacomment(linker, ”/export:add=_add@8”)

这里_add@8是MS编译器自动为符号修改的名字,其修改规则是:在函数名前加”_”,在函数名后jia”@”和传给函数的参数的字节数构成。

(2)  开发过程中不要在一个动态库中申请内存然后再这个动态库之外释放,这样不仅容易引起内存泄露,而且还有可能由于申请和释放内存所使用的运行库不一样而引起其它的问题。

(3)  尽量避免导出变量。

(4)  谨慎导出类。

(5)  在显示动态链接的时候可以通过LoadLibrary加载一个exe但是并不执行该exe程序,这样我们就可以像动态库一样使用该exe文件中的资源了,不过此时需要指定动态库加载函数LoadLibrary的第二个参数Flags为:LOAD_LIBRARY_AS_DATAFILE

(6)  Dll由系统维护,可以在各进程之间共享代码,因此,显示动态链接时,每调用LoadLibrary加载一次dll,操作系统就会将对应dll的计数器加1,因此一次LoadLibrary就需要对应一次FreeLibrary来将计数器减1,在动态库加载时可以通过GetModuleHandle来获取判断一个动态库是否被加载,例如:

HMODULEhDll = GetModuleHandle(_T(“add.dll”));

If(NULL==hDll)

        hDll = LoadLibrary(_T(“add.dll”));

GetModule还有另外一个作用:返回应用程序的可执行文件的句柄,此时只需传给它一个NULL参数即可。


  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值