1、简介:
dll和.lib都是程序集合,便于代码重用。都是二进制的文件。.dll也叫动态链接库,与程序链接的方式为运行时链接(run-time linked),为PE(portable executable)格式,也就是程完整的程序。.exe、.dll、.fon、.mod、.drv、.ocx等等都是动态链接库。如.exe为系统调用的函数集合。.dll不存在同名引用,且有导出表,与导入表。
.lib也叫静态链接库,在编译时与程序链接(link-time linked),将“嵌入”到程序中。会有冗余(程序文件代码的冗余和运行时内存存储的冗余),当两个lib相链接时地址会重新建立同。在使用.lib之前,要在程序源代码中引用lib对应的头文件.h,这些头文件告诉编译器.lib中有什么。
在生成.dll时,通常会生成一个.lib。这个.lib将被编译到程序文件中,在程序运行的时候,告诉操作系统将要加载的.dll。这个.lib包括对应.dll的文件名、顺序表(ordinal table包含.dll暴露出的函数的进入点),在程序运行的时候,通过顺序表实现函数的跳转。
如果不想使用或者找不到该.lib,可以用LoadLibrary () Win32 API和GetLibrary () Win32 API。
VC IDE为了实现程序调试,会生成.PDB(程序数据库,二进制),里面包含源文件调用的文件信息和行信息。这样就可以逐行调试了。
打开.lib,查看其ascii码,可以看到如@@My_Function1123的函数名,这些名称在编译时被编译器运用mangling mechanism进行了名称的mangling。
在程序的编译过程中,如果出现如下错误“unresolved symbol _some_funtion@1234”,通常是因为找不到引用过的外部函数对应的.lib文件,或者是.c、.cpp源文件。
2、使用:
用DLL,首先需要将DLL文件映像到用户进程的地址空间中,然后才能进行函数调用,这个函数和进程内部一般函数的调用方法相同。Windows提供了两种将DLL映像到进程地址空间的方法:1、隐式的加载时链接
这种方法需要DLL工程经编译产生的LIB文件,此文件中包含了DLL允许应用程序调用的所有函数的列表,当链接器发现应用程序调用了LIB文件列出的某个函数,就会在应用程序的可执行文件的文件映像中加入一些信息,这些信息指出了包含这个函数的DLL文件的名字。当这个应用程序运行时,也就是它的可执行文件被操作系统产生映像文件时,系统会查看这个映像文件中关于DLL的信息,然后将这个DLL文件映像到进程的地址空间。系统通过DLL文件的名称,试图加载这个文件到进程地址空间时,它寻找DLL 文件的路径按照先后顺序如下:
·程序运行时的目录,即可执行文件所在的目录;
·当前程序工作目录
·系统目录:对于Windows95/98来说,可以调用GetSystemDirectory函数来得到,对于WindowsNT/2000来说,指的是32位Windows的系统目录,也可以调用GetSystemDirectory函数来得到,得到的值为SYSTEM32。
·Windows目录
·列在PATH环境变量中的所有目录
①LIB文件直接加入到工程文件列表中
在VC中打开File View一页,选中工程名,单击鼠标右键,然后选中"Add Files to Project"菜单,在弹出的文件对话框中选中要加入DLL的LIB文件即可。
②设置工程的 Project Settings来加载DLL的LIB文件
打开工程的 Project Settings菜单,选中Link,然后在Object/library modules下的文本框中输入DLL的LIB文件。
③通过程序代码的方式
加入预编译指令#pragma comment (lib,"*.lib"),这种方法优点是可以利用条件预编译指令链接不同版本的LIB文件。因为,在Debug方式下,产生的LIB文件是Debug 版本,如Regd.lib;在Release方式下,产生的LIB文件是Release版本,如Regr.lib。
当应用程序对DLL的LIB文件加载后,还需要把DLL对应的头文件(*.h)包含到其中,在这个头文件中给出了DLL中定义的函数原型,然后声明。
1.把你的youApp.DLL拷到你目标工程(需调用youApp.DLL的工程)的Debug目录下;
2.把你的youApp.lib拷到你目标工程(需调用youApp.DLL的工程)目录下;
3.把你的youApp.h(包含输出函数的定义)拷到你目标工程(需调用youApp.DLL的工程)目录下;
4.打开你的目标工程选中工程,选择Visual C++的Project主菜单的Settings菜单;
5.执行第4步后,VC将会弹出一个对话框,在对话框的多页显示控件中选择Link页。然后在Object/library modules输入框中输入:youApp.lib
6.选择你的目标工程Head Files加入:youApp.h文件;
7.最后在你目标工程(*.cpp,需要调用DLL中的函数)中包含你的:#include "youApp.h"
注:youApp是你DLL的工程名
01 //Dlltest.h
02 #pragma comment(lib,"MyDll.lib")
03 extern "C"_declspec(dllimport)intMax(inta,int b);
04 extern "C"_declspec(dllimport)intMin(inta,int b);
05 //TestDll.cpp
06 #include
07 #include"Dlltest.h"
08 voidmain()
09 {
10 int a;
11 a=min(8,10)
12 printf("比较的结果为%d\n",a);
13 }
2、显式的运行时链接
隐式链接虽然实现较简单,但除了必须的*.dll文件外还需要DLL的*.h文件和*.lib文件,在那些只提供*.dll文件的场合就无法使用,而只能采用显式链接的方式。这种方式通过调用API函数来完成对DLL的加载与卸载,其能更加有效地使用内存,在编写大型应用程序时往往采用此方式。这种方法编程具体实现步骤如下:①使用Windows API函数Load Library或者MFC提供的AfxLoadLibrary将DLL模块映像到进程的内存空间,对DLL模块进行动态加载。
②使用GetProcAddress函数得到要调用DLL中的函数的指针。
③不用DLL时,用Free Library函数或者AfxFreeLibrary函数从进程的地址空间显式卸载DLL。
01{
02 HINSTANCE hDllInst = LoadLibrary("youApp.DLL");
03 if(hDllInst)
04 {
05 typedef DWORD (WINAPI*MYFUNC)(DWORD,DWORD);
06 MYFUNCyouFuntionNameAlias=NULL;
07 // youFuntionNameAlias 函数别名
08 youFuntionNameAlias= (MYFUNC)GetProcAddress(hDllInst,"youFuntionName");
09 // youFuntionName 在DLL中声明的函数名
10 if(youFuntionNameAlias)
11 {
12 youFuntionNameAlias(param1,param2);
13 }
14 FreeLibrary(hDllInst);
15 }
16 }
查看DLL里有哪些函数的工具:VC自带的DUMPBIN.EXE,运行方法:开始-vs-tools-开发人员命令提示,在命令行里输入 "dumpbin [选项] [dll的路径]"
用法: DUMPBIN [选项] [文件]
选项:
/ALL
显示除”code disassembly”以外的所有信息。可以使用/DISASM选项来显示”code
disassembly”。使用/RAWDATA:NONE和/ALL去删除每个段中的二进制数据。
/ARCHIVEMEMBERS
显示库中成员的基本信息。
/CLRHEADER file
公共语言运行时。可参考微软官网的相关信息。
/DEPENDENTS
显示出所依赖的DLLs的名字,但不显示导入函数的名字。
/DIRECTIVES
显示编译器产生的.drective(链接时的参数)段中的信息。
/DISASM
显示代码段反汇编的结果。/DISASM should only be used on native (not managed)
images.(大概意思:不能使用在微软的C++“标准“中)。
/ERRORREPORT[NONE | PROMPT | QUEUE | SEND ]
如果dumpbin.exe在运行时失败,你能使用”/ERRORREPORT”这个参数去发送错误信息到
微软。
/EXPORTS
显示可执行文件或DLL中导出的所有信息。
/FPO
显示FPO( frame pointer optimization)记录。
/HEADERS
显示文件和每个段的头部信息。当使用一个库文件时,它显示的是每个成员对象的头部信息。
/IMPORTS[:file]
显示可执行文件或DLL文件中导入的DLL文件和它的函数列表。
/LINENUMBERS
如果编译时使用了/Zi,/C7/Zd选项,行编号才存在于目标文件中,或者在连接时产生了调试信息是才能够显示行编号。
/LINKERMEMBER[:{1|2}]
这个选项显示定义在库中的全局符号。如果”1”被指定将以对象的顺序显示对象以及他们相应的偏移量,如果”2”被指定将显示偏移和对象成员的索引,然后以字母顺序排序。
/LINKERMEMBER[:{1|2}]
这个选项显示定义在库中的全局符号。如果”1”被指定将以对象的顺序显示对象以及他们相应的偏移量,如果”2”被指定将显示偏移和对象成员的索引,然后以字母顺序排序。
/LOADCONFIG
这个选项会显示”IMAGE_LOAD_CONFIG_DIRECTORY”结构体中的相关信息,这个结构体
是在文件加载时加载选项。
/OUT:filename
这个选项将信息输出到指定的文件中。
/PDATA
这个参数只对RISC处理器才有用。显示.pdata中的相关信息。
/RAWDATA [: { 1 | 2 | 4 | 8 | NONE [, number ] ]
显示原始数据(比如.text段中的指令数据)。你可以使用下列参数来格式化输出。
/RELOCATIONS
显示对象中的重定位信息。
/SECTION:section
显示由”section”指定段中的信息。你可以使用”/HEADERS”选项去显示每个段的头部信息。
/SUMMARY
显示每个段的基本信息(段名和大小),如果其他选项没有指定这个选项是默认的。
/SYMBOLS
这个参数会显示COFF文件的符号表(用于连接时从定位的)。在所有的对象中都存在符号表。一个COFF文件中的符号表只有在连接时使用了”/DEBUG”时才显示。前两句话感觉有点矛盾原文是这样的: “Symbol tables exist in all object files. A COFF symbol table appears
in an image file only if it is linked with /DEBUG”。
/TLS
显示一个来自可执行文件中的“IMAGE_TLS_DIRECTORY”结构体的信息,此结构定义在
Winnt.h中,也显示回调函数的地址。如果你的程序中没有使用线程私有储存空间(”thread local storage”,)这个镜像文件中是不会包含“IMAGE_TLS_DIRECTORY”的。
/UNWINDINFO
这个选项只能使用于 Itanium处理器。
注:上面的每个选项在使用时都有这么一个选项——只用在编译时使用”/GL”选项生成的文件才能够使用”/HEADERS”这个选项