WIN32汇编语言教程:第11章 动态链接库和钩子 · 11.1 动态链接库(4)

第二个实验:修改上一节中的Counter.asm,将入口函数中的返回值改为FALSE,也就是说模拟dll初始化失败的情况。修改完毕后重新编译链接生成新的Counter.dll文件,并将文件拷贝到UseDll1.exe所在目录后运行,系统将弹出如图11.3所示的错误提示。也就是说,任何一个dll文件因为初始化失败而无法装入时,可执行文件也是无法被装入执行的。


图11.3  Dll初始化失败时的错误信息

第三个实验:模拟软件升级或dll文件版本不对时的情况,这种情况在Windows系统中经常发生,因为当某些应用软件包被安装的时候,它可能会用自己附带的某个版本的dll文件替换掉Windows目录中已存在的dll文件,当程序卸载的时候,它有可能会根据备份恢复原来的版本,但更多的情况是根本没有恢复,经过多次安装和卸载不同的应用软件包后,最终的结果就是Windows系统目录中各个dll文件的版本参差不齐。不同版本的dll文件中可能增加了一些函数,也可能废弃了一些函数,有时其他使用这个dll文件的程序可能刚好用到不存在的函数,而这个函数在原来版本的dll文件中本来是存在的。

现在就修改程序来模拟这种情况,将Counter.def文件中的_DecCount一行去掉,这样dll文件的导出表中就不会有这个函数,相当于函数不存在了,然后重新编译dll文件并将它拷贝到UseDll1.exe所在目录,执行UseDll1.exe,这时系统显示的是如图11.4所示的错误提示,UseDll1.exe程序仍然不能被正常装入执行。


图11.4  找不到导出函数的错误信息

现在读者一定明白这个最熟悉不过的错误信息的由来了,通常对付这种莫名其妙的错误的最好方法就是重新安装Windows,这将使所有dll文件的版本被重新安装为统一的版本,错误也就自然消失了。读者也可能会说:把出错的dll替换掉不就行了吗,为什么要整个重装呢?问题是你知道原来的版本应该是多少吗?

如果使用这种标准的方法调用动态链接库中的函数,链接器会根据导入库中的信息将使用的dll文件名和函数名存放在可执行文件头的导入表中,这样Windows要执行文件的时候,在装入的过程中会根据导入表中的dll列表寻找每个dll文件,并根据函数名在每个dll中寻找导出函数,如果这中间出现任何错误,如上面演示的dll文件丢失、dll文件装入失败或dll中的函数名无法找到等情况,应用程序都无法被装载执行。

2. 方法二:动态装入

方法一的优点就是使用方便,应用程序可以像使用自己内部的函数一样使用DLL中的函数,缺点也显而易见,如果装入DLL的过程中有任何错误,应用程序没有任何机会完成应变的措施,因为它根本没有被装入执行。

编程中有时候会有下面的需求:

● 程序需要使用系统中的保留函数。这个函数确实存在于动态链接库的导出表中,可以被其他程序引用,但是软件开发包提供的lib文件中并不包含它。

● 不同版本Windows中的函数集不同(如Windows NT中的很多与安全有关的函数在Windows 9x中不存在),同一版本Windows中不同版本dll文件的函数集也可能不同,程序需要根据函数是否存在做不同的处理。

● 程序使用的某些库并不重要(如仅用来显示程序版本的库),如果丢失这个库,程序希望能继续运行,而不是像上面演示的那样出现根本无法装入的情况。

对于这些需求,解决的办法就是不能将动态链接库的导入信息保存在可执行文件的导入表中,也就是说不要让Windows系统来做动态链接库的装入工作,这些工作由应用程序自己的代码来完成。有3个函数可以用来完成这样的功能:LoadLibrary(装入动态链接库),FreeLibrary(释放动态链接库)和GetProcAddress(获取导出函数地址)。

例子UseDll2.asm程序使用这种动态装入的方法来实现UseDll1程序同样的功能,来看看UseDll2.asm的内容:

               .386
               .model flat, stdcall
              option casemap :none
;####################################################################
; Include 文件定义
;####################################################################
include        windows.inc
include        user32.inc
includelib     user32.lib
include        kernel32.inc
includelib     kernel32.lib
;####################################################################
; Equ 等值定义
;####################################################################
ICO_MAIN          equ    1000
DLG_MAIN         equ    1000
IDC_COUNTER    equ    1001
IDC_INC        equ    1002
IDC_DEC        equ    1003
 
_PROCVAR2      typedef proto :dword,:dword
PROCVAR2         typedef ptr _PROCVAR2
;####################################################################
; 数据段
;####################################################################
                 .data?
hDllInstance     dd            ?
lpIncCount   PROCVAR2       ?
lpDecCount   PROCVAR2       ?
 
                  .const
szError        db    'Counter.dll文件丢失或装载失败,计数功能无法实现',0
szDll         db     'Counter.dll',0
szIncCount    db     '_IncCount',0
szDecCount    db     '_DecCount',0
;####################################################################
; 代码段
;####################################################################
 
                  .code
;####################################################################
_ProcDlgMain      proc       uses ebx edi esi hWnd,wMsg,wParam,lParam
 
                 mov    eax,wMsg
;********************************************************************
                  .if    eax == WM_CLOSE
                          .if    hDllInstance
                                 invoke FreeLibrary,hDllInstance
                         .endif
                        invoke EndDialog,hWnd,NULL
;********************************************************************
                  .elseif eax == WM_INITDIALOG
                          invoke LoadLibrary,addr szDll
                         .if    eax
                                 mov    hDllInstance,eax
                               invoke GetProcAddress,hDllInstance,\
                                       addr szIncCount
                               mov lpIncCount,eax
                                invoke GetProcAddress,hDllInstance,\
                                       addr szDecCount
                                mov lpDecCount,eax
                          .else
                                  invoke MessageBox,hWnd,addr szError,NULL,\
                                         MB_OK or MB_ICONWARNING

上页:第11章 动态链接库和钩子 · 11.1 动态链接库(3) 下页:第11章 动态链接库和钩子 · 11.1 动态链接库(5)

第11章 动态链接库和钩子

版权所有 © 云南伯恩科技 证书:粤ICP备09170368号