分类: C/C++
2010-04-21 20:09:54
DLL(动态链接库)专 题
0.
Windows API中所有的函数都包含在dll中, 其中有3个最重要的DLL。
(1) Kernel32.dll
它包含那些用于管理内存、进程和线程的函数,例如CreateThread函 数;
(2) User32.dll
它包含那些用于执行用户界面任务(如窗口的创建和消息的传送)的 函数,例如CreateWindow函数;
(3) GDI32.dll
它包含那些用于画图和显示文本的函数。
1. 静态库和动态库
(1) 静态库
函数和数据被编译进一个二进制文件(通常扩展名为.LIB)。 在使用静态库的情况下,在编译链接可执行文件时,链接器从库中复制这些函数和数据并把它们和应用程序的其他模块组合起来创建最终的可执行文件(.Exe文 件).当发布产品时,只需要发布这个可执行文件,并不需要发布被使用的静态库。
(2) 动态库
在使用动态库的时候,往往提供两个文件:一个引入库(.lib)文 件和一个DLL(.dll)文件。虽然引入库的后缀名也是”lib”, 但是动态库的引入库文件和静态库文件有着本质上的区别,对一个DLL来说,其引入库文件(.lib)包 含该DLL导出的函数和变量的符号名,而.dll文 件包含该DLL实际的函数和数据。在使用动态库的情况下,在编译链接可执行文件时,只需要链接该DLL的 引入库文件,该DLL中的函数代码和数据并不复制到可执行文件中,直到可执行程序运行时,才去加载 所需的DLL,将该DLL映射到进程的地址 空间外,然后访问DLL中导出的函数。这时,发布产品时,除了发布可执行文件以外,同时还要发布该 程序将要调用的动态链接库。
2. 在导出库头文件中的标准写法:
#ifdef LIBDAQ_EXPORTS
#define LIBDAQ_API __declspec(dllexport)
#else
#define LIBDAQ_API __declspec(dllimport)
#endif
将该头文件添加到某客户代码中时,会自动展开。如果客户代码没有定义LIBDAQ_EXPORTS, 那么LIBDAQ_EXPORTS会被定义为__declspec(dllimport)表 示有LIBDAQ_EXPORTS头的函数都是从该DLL中 导入的。
3. 名字改编和”extern “C””
C++编译器在生成DLL时,会 对导出的函数进行名字改编,并且不同的编译器使用的改变规则不一样,因此改编后的名字会不一样。这样,如果利用不同的编译器分别生成DLL和 访问该DLL的客户端代码程序的话,后者在访问该DLL的 导出函数时会出现问题。为了实现通用性,需要加上限定符:extern “C”。
但是利用限定符extern “C”可以解决C++和C之 间相互调用时函数命名的问题,但是这种方法有一个缺陷,就是不能用于导出一个类的成员函数,只能用于导出全局函数。
4. 显示加载方式加载DLL
使用动态方式来加载动态链接库时,需要用到LoadLibrary函 数。该函数的作用就是将指定的可执行模块映射到调用进程的地址空间。调用原型为:
HMODULE LoadLibrary(LPCTSTR lpFileName);
LoadLibrary函数不仅可以加载DLL, 还可以加载可执行模块(Exe)。当加载可执行模块时,主要是为了访问该模块内的一些资源,例如对 话框资源、位图资源或图标资源等。LoadLibrary函数有一个字符串类型(LPCTSTR)的 参数,该参数指定了可执行模块的名称,既可以是一个dll文件,也可以是一个exe文 件。如果调用成功,LoadLibrary函数将返回所加载的那个模块的句柄。返回类型HMODULE和HINSTANCE可 以通用。
当加载到动态链接库模块的句柄后,接下来就要想办法获取该动态链接库中导出函数的地址,这可以通过调用GetProcAddress函 数来实现。该函数用来获取DLL导出函数的地址,其原型声明如下所示:
FARPROC GetProcAddress(HMODULE hModule, LPCSTR lpProcName);
参数hModule:指定动态链接库模块的句柄,即LoadLibrary函 数的返回值。
参数lpProcName:一个指向常量的字符指针,指定DLL导 出函数的名字或函数的序号。如果是序号,则序号必须在低位字节中,高位字节必须是0。
如果调用成功,GetProcAddress函数将返回指定导出函数的地址;否 则返回NULL。
例如:
HINSTANCE hInst;
hInst = LoadLibrary(“DllTest.dll”);
typedef int (*ADDPROC)(int a, int b);
ADDPROC add = (ADDPROC)GetProcAddress(hInst, “add”);
if (!add)
print(“Failure”);
else
process next events
FreeLibrary(hInst);
调用语法:
BOOL FreeLibrary(HMODULE hModule);
5. 加载DLL的 两种方式优缺点:
采用动态加载方式,那么可以在需要时才加载DLL,而隐式链接方 式实现起来比较简单,在编写客户端代码时就可以把链接工作做好,在程序中可以随时调用DLL导出的 函数。但是如果程序需要访问十多个DLL时,如果都采用隐式链接方式加载它们的话,那么在该程序启 动时,这些DLL都需要被加载到内存中,并映射到调用进程的地址空间,这样将加大程序的启动时间。 而且一般来说,在程序运行过程中只是在某个条件满足时才需要访问某个DLL中的某个函数,其它情况 下都不需要访问这些DLL中的函数。但是这时所有的DLL都 已经被加载到内存中,资源浪费是比较严重的。这个时候就需要采用显示加载的方式来访问DLL,在需 要时才加载所需的DLL。也就是说在需要时才被加载到内存中,并被映射到调用进程的地址控件中。需 要说明的是,隐式链接方式访问DLL时,在程序启动时也是通过LoadLibrary函 数加载该进程需要的动态链接库的。
6. DllMain函 数
如果提供了DllMain函数(该 函数是可以选择存在的),那么在此函数中不要进行太复杂的调用。因为在加载该动态链接库时,可能还 有一些核心动态链接库没有被加载。例如Use32.dll或GDI32.dll。 我们自己编写的DLL会比较靠前地被加载。
chulia200020012010-04-21 20:41:17
GCC编译出来的DLL,在VC6.0中调用为什么会有不同的结果,请高手详解 http://social.microsoft.com/Forums/zh-CN/visualcpluszhchs/thread/57a6ef6c-6d97-4e82-80d9-3527adc24dc4 问题很简单,可就是不明白! 具体情况描述如下: 1.GCC的ffmpeg工程原本是生成.exe的,我把它修改后,现在能够生成.dll文件 2.把生成后的dll文件搁到VC6.0中,如果我用console项目显式调用,没有任何问题;如果我用MFC的exe项目中也采用相同的调用方式就会出错,“0xC0000005:Access Violation”。 个人认为是MFC的问题,希望高手明示! MFC 调用DLL的代码如下: FFmpegConvertCodeParam ccpMy; ccpMy.input_file_fullpath="d:\\1.avi"; ccpMy.fmt="flv"; ccpMy.bitrate="512000"; ccpMy.rfps="14.985"; cc
chulia200020012010-04-21 20:34:30
用VC和MinGW导出 dll的def和lib(a)文件 http://zhangyafeikimi.javaeye.com/blog/404580 关键字: vc mingw 导出 动态链接库 dll def lib a 有了dll文件需要导出def文件: pexports zlib1.dll > zlib1.def 有了dll和def文件,需要导出MinGW的.a文件: dlltool --dllname zlib1.dll --def zlib1.def --output-lib libz.dll.a 有了dll和def文件,需要导出一个VC的lib文件: lib /machine:i386 /def:zlib1.def 注意: C++的dll不具有2进制级别的共享性,也就是说VC的MinGW的C++ dll不能混用。 而C的可以。 pexports和dlltool的MinGW的工具,lib是VC的工具。
chulia200020012010-04-21 20:22:52
如何从DLL文件中导出LIB文件? http://zzqsc.spaces.live.com/blog/cns!D831FDBD876F27A2!223.entry 由于我们经常要调用一些第三方厂商或其他编译器编写的动态链接库,但是一般都不提供源文件或.lib文件,而作为VC隐式链接到DLL (implicitly link to the DLL)调用,这些却是必需的。本文将主要讨论在没有源文件及.lib输入库文件或欲调用Windows未公开函数的情况下重建.Lib文件的方法。在建立之前,我们首先要了解一下DLL输出函数的几种方式。 一、从DLL中输出函数的方式(calling conventions) _cdecl是C和C++程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式。VC将函数编译后会在函数名前面加上下划线前缀。 _stdcall是Pascal程序的缺省调用方式,通常用于Win32 Api中,函数采用从右
chulia200020012010-04-21 20:14:03
【话题】只有.h文件和.dll文件,没有lib文件,如何调用dll文件中的函数? http://group.gimoo.net/review/9563 问题: 只有.h文件和.dll文件,没有lib文件,如何调用dll文件中的函数? 现实情况: 现有文件:RFIDAPIEXPORT.h 和RFIDAPI.dll,其中的一个函数为例: 函数SAAT_TCPInit在头文件中的声明: extern "C" bool RFID_API __stdcall SAAT_TCPInit(void** pHandle,char *pHostName,int nsocketPort); 尝试办法1: 在网上查了通过dumpbin生成lib文件的方法,但是,生成的def文件中,大多数函数为XCTCPInit形式,生成lib文件后,链接时出错: Linking... rfid.obj : error LNK2001: unresolved external symbol __imp__SAAT_TCPInit@12 Debug/RFID.exe : fatal error
chulia200020012010-04-21 20:12:00
请问exe文件在调用dll时,是如何知道哪个头文件对应哪个dll 的? http://topic.csdn.net/u/20100318/17/cb50e339-fff9-4817-92fc-f26bb1926a23.html 发表于:2010-03-18 17:51:50 我自己写了一个dll文件,然后将dll、lib、.h三个文件拷贝到了exe文件相关路径下,运行发现没问题。后来我修改了dll文件,新生成的dll文件名,lib文件名与原来的不一样了,但.h文件名还是一样的。后来我将这三个文件又拷贝到exe相关路径下,可运行发现exe启动不了了。提示**.dll文件无法加载,这个**.dll文件是我未修改前的那个dll,它为什么还会去找前一个dll呢,为什么不找现在这个新的呢? 我的exe调用dll时,是采用静态链接方式,我在exe的文件中设置了#pragma comment(lib,"**.lib"),以便链接相关的dll文件。我现在想知道,当exe静态链接dll时,是怎么知道哪个头文件对应哪个lib 文件的,对应哪个dll的,是靠文件名吗?lib文件名与dll文件