Chinaunix首页 | 论坛 | 博客
  • 博客访问: 847503
  • 博文数量: 168
  • 博客积分: 5431
  • 博客等级: 大校
  • 技术积分: 1560
  • 用 户 组: 普通用户
  • 注册时间: 2007-10-22 11:56
文章存档

2015年(2)

2014年(1)

2013年(12)

2012年(12)

2011年(15)

2010年(5)

2009年(16)

2008年(41)

2007年(64)

分类: C/C++

2008-09-25 23:21:21

DLL(动态链接库)编程

dll是现在常见的文件,它集成了程序的很多功能在里面。一般情况下,它不能直接被执行,常见的使用方法是用其他的*.exe调用其执行,以使其内部功能表现出来。还有*.ocx文件也与之类似,也就是人们常说的com
1.简要
         Windows API中所有的函数都包含在dll中,其中有3个最重要的DLL。
        (1)
Kernel32.dll
        它包含那些用于管理内存、进程和线程的函数,例如CreateThread函数;
        (2)
User32.dll
       它包含那些用于执行用户界面任务(如窗口的创建和消息的传送)的函数,例如CreateWindow函数;
        (3)
GDI32.dll
       它包含那些用于画图和显示文本的函数。
2.
静态库和动态库
(1)
静态库
           函数和数据被编译进一个二进制文件(通常扩展名为.LIB)。在使用静态库的情况下,在编译链接可执行文件时,链接器从库中复制这些函数和数据并把 它们和应用程序的其他模块组合起来创建最终的可执行文件(.Exe文件).当发布产品时,只需要发布这个可执行文件,并不需要发布被使用的静态库。(2)
动态库
        在使用动态库的时候,往往提供两个文件:一个引入库(.lib)文件和一个DLL(.dll)文件。虽然引入库的后缀名也是”lib”,但是动态库 的引入库文件和静态库文件有着本质上的区别,对一个DLL来说,其引入库文件(.lib)包含该DLL导出的函数和变量的符号名,而.dll文件包含该 DLL实际的函数和数据。在使用动态库的情况下,在编译链接可执行文件时,只需要链接该DLL的引入库文件,该DLL中的函数代码和数据并不复制到可执行 文件中,直到可执行程序运行时,才去加载所需的DLL,将该DLL映射到进程的地址空间外,然后访问DLL中导出的函数。这时,发布产品时,除了发布可执 行文件以外,同时还要发布该程序将要调用的动态链接库。
3.
在导出库头文件中的标准写法:
#ifdef LIBDAQ_EXPORTS
#define LIBDAQ_API __declspec(dllexport)
#else
#define LIBDAQ_API __declspec(dllimport)
#endif
     将该头文件添加到某客户代码中时,会自动展开。如果客户代码没有定义LIBDAQ_EXPORTS,那么LIBDAQ_EXPORTS会被定义为__declspec(dllimport)表示有LIBDAQ_EXPORTS头的函数都是从该DLL中导入的。
4.
名字改编和”extern “C””
           C++编译器在生成DLL时,会对导出的函数进行名字改编,并且不同的编译器使用的改变规则不一样,因此改编后的名字会不一样。这样,如果利用不同 的编译器分别生成DLL和访问该DLL的客户端代码程序的话,后者在访问该DLL的导出函数时会出现问题。为了实现通用性,需要加上限定符:extern “C”。
           但是利用限定符extern “C”可以解决C++和C之间相互调用时函数命名的问题,但是这种方法有一个缺陷,就是不能用于导出一个类的成员函数,只能用于导出全局函数。
5.
显示加载方式加载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);


6.      加载DLL的两种方式优缺点:
       采用动态加载方式,那么可以在需要时才加载DLL,而隐式链接方式实现起来比较简单,在编写客户端代码时就可以把链接工作做好,在程序中可以随时调用 DLL导出的函数。但是如果程序需要访问十多个DLL时,如果都采用隐式链接方式加载它们的话,那么在该程序启动时,这些DLL都需要被加载到内存中,并 映射到调用进程的地址空间,这样将加大程序的启动时间。而且一般来说,在程序运行过程中只是在某个条件满足时才需要访问某个DLL中的某个函数,其它情况 下都不需要访问这些DLL中的函数。但是这时所有的DLL都已经被加载到内存中,资源浪费是比较严重的。这个时候就需要采用显示加载的方式来访问DLL, 在需要时才加载所需的DLL。也就是说在需要时才被加载到内存中,并被映射到调用进程的地址控件中。需要说明的是,隐式链接方式访问DLL时,在程序启动 时也是通过LoadLibrary函数加载该进程需要的动态链接库的。
7.      DllMain函数
           如果提供了DllMain函数(该函数是可以选择存在的),那么在此函数中不要进行太复杂的调用。因为在加载该动态链接库时,可能还有一些核心动态 链接库没有被加载。例如Use32.dll或GDI32.dll。我们自己编写的DLL会比较靠前地被加载。
阅读(1919) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2008-09-26 13:37:12

雪praado.com 20:31:40 普拉多 Praado: http://www.praado.cn ( http://praado.vicp.net ) 专业的网页收藏夹 每天我们上网都会碰到一些有用的、值得珍藏的网页链接地址(简称:网址, link, URL),于是我们都用浏览器里的收藏夹来收藏。但是现在的电脑病毒相当的猖獗,电脑遭病毒之后,浏览器里收藏的网址几乎就是无法挽救,损失巨大!同时,现在的浏览器市场有多种多样的浏览器,在一台电脑上的浏览器里收藏了某一网址,可是到另外一台电脑上或另外一个浏览器时却又不能使用了,普拉多网页收藏夹可以帮助解决此问题。只要您注册个用户帐号,把自己喜欢的网页链接地址(简称:网址)保存在您的普拉多收藏夹里 http://www.praado.cn ( http://praado.vicp.net ) ,这样,无论您在任何一台电脑上,您都能轻松访问您的收藏夹,再厉害的病毒也无法侵害您收藏的网页链接地址。 普拉多 http://www.praado.cn ( http://praado.vicp.net )收藏夹支持多级管理和模糊搜索