一、动态链接库的加载方式
隐式加载又称载入时加载,指在主程序载入内存时搜索DLL,并将DLL载入内存。使用隐式加载时,使用者需要DLL链接库的.h文件、.lib文件和.dll文件。
(.lib文件包含DLL导出的函数声明和变量的符号名,.dll 文件才包含实际的函数和数据定义)
显式加载又称运行时加载,指主程序在运行过程中需要DLL中的函数时再加载。使用显式加载时,使用者只需要DLL链接库的.h文件和.dll文件
(显式加载时要知道.dll文件中包含的函数列表)
显式加载可以灵活选择DLL的加载时机,减少应用程序启动时开销,具有更好的灵活性,能更加有效的使用内存。在编写大型程序时往往使用显式加载方式,唯一的缺点是需要显式调用Windows API,比较麻烦,例如不调用FreeLibrary的话,会导致内存泄漏。(隐式加载可以通过lazy方式在使用到dll时再加载,避免启动开销)
二、 如何显式加载Windows动态链接库
2.1 将动态链接库映射到调用者进程 LoadLibraryW2.2 获取动态链接库导出函数地址 GetProcAddress2.3 释放动态链接库 FreeLibrary
一般DLL的导出函数只需要三个:mod_init、mod_term、mod_getobject,分别负责动态库模块的初始化、动态库模块的终结、获取库中必要的对象;
以dll_xxx为例,对于dll_xxx的使用者来说,定义dllxxxLoader类,并include dllxxx_i.h文件即可;(C++标准里是没有interface关键字的,实际使用struct替代:#define interface struct,在combaseapi.h头文件中定义)
三、 dllxxx如何保持二进制兼容性
dllxxx以COM(Component Object Model)的形式提供,向客户提供一个接口集(dllxxx_i.h),客户只能通过接口与COM组件打交道。COM组件是以Win32动态链接库(DLLs)或可执行文件(EXEs)的形式发布的可执行代码。COM组件必须满足以下3个条件:1.COM组件是完全与语言无关的;
2.COM组件可以以二进制的形式发布;
3.COM组件可以在不妨碍老客户的情况下被升级;
通过对dlxxx_i.h的分析得出:1.一个COM组件可以提供多个COM接口(IF1, IF2, IF3...);
2.COM接口在C++中是用纯虚函数声明;
以接口IF1中的start声明为例:virtual __declspec(nothrow) HRESULT __stdcall start() = 0virtual ... =0: 代表start函数是纯虚函数,在IF1中只有声明没有定义,必须在子类中定义;__declspec(nothrow):告知编译器被声明的函数以及函数内部调用的其它函数都不会抛出异常HRESULT: 返回值__stdcall: 修饰函数的关键字,主要约定(参考 http://blog.csdn.net/xdrt81y/article/details/11644707)
a.函数参数传递顺序,表示参数从右向左压入堆栈;
b.堆栈由调用函数还是被调用函数清理,表示由被调用函数清理堆栈;c.COM接口都需要继承IUnknown接口;
IUnknown接口包括三个纯虚函数:QueryInterface, AddRef, Release
QueryInterface由客户调用,判断一个组件是否支持对应的接口,若支持,QueryInterface将返回一个指向此接口的指针,否则返回一个错误代码
QueryInterface是在组件层次实现的,不管组件实现了多少个接口,组件都只实现了一个QueryInterface。所有接口指针调用QueryInterface进行查询时,都是调用的同一个QueryInterface
参考:http://www.jb51.net/article/55883.htm3.添加新的接口
COM中接口是不会发生变化的。当组件发布一个接口并被某个客户使用之后,此接口将决不会发生任何变化,而将永远保持不变,这是由接口IID来进行保证的。一般情况下,不会改变接口,而可以建立一个新接口并为之指定一个新的IID。当QueryInterface接收到对老的IID的查询时,它将返回老的接口;而当它收到对新的IID的查询时,它将返回新的接口。于老的接口仍然保持不变,已有客户的运行将不会受到任何影响。而新客户则可以自行决定是使用老接口还是新接口,因它可以自由决定到底是查询哪个接口。
4. COM接口的背后:虚函数表
在对包含虚函数的类定义实例时,编译器会为这个实例分配一个成员变量_vfptr,该变量指向这个虚函数表,这个虚函数表中的每一项都会记录对应的虚函数的地址。所有的, 同一个类, 共用同一份虚函数表.使用COM接口时,要保证调用接口函数的对象中有包含该接口函数的虚函数表,否则会产生链接错误的信息。