理解操作系统对程序的反馈:异常(Exception)和通知(Debug Event)【2】

2.3.3 通知(Debug Event)是操作系统跟调试器交流的一种方法

通知,也叫做调试信息(Debug Events),是操作系统在某些事件发生的时候,通知调试器的一个手段。跟异常处理相似,操作系统在某些事件发生的时候,会检查当前进程是否有调试器加载。如果有,就会给调试器发送对应的消息,以便使用调试器进行观察。跟异常不一样的地方就是,只有调试器才会得到通知,应用程序本身是得不到的。同时调试器得到通知后不需要做什么处理,没有1st /2nd chance的差别。在Windbg帮助文件的Controlling Exceptions and Events主题里面,可以看到关于通知的所有代号。常见的通知有:DLL的加载、卸载,线程的创建、退出等。

案例分析:VB6的版本问题

客户用VB6开发的程序,在VB6 IDE调试的时候无法访问Access 2003创建的数据库,访问Access 97的数据库却是好的。如果换一台开发机,测试就一切正常。

这个问题的思路非常简单,既然只有一台机器有问题,说明是环境的原因。既然访问Access 97没问题,或许跟Access客户端文件,也就是DAO的版本有关。通过工具Windbg目录下的tlist工具检查进程中加载的DLL,发现有问题的机器加载的是dao350.dll,没有问题的机器加载的是dao360.dll。下一步就需要知道为什么加载的是dao350.dll?

DAO是一个COM对象,很有可能是通过COM对象加载的方法完成的。那么,可以采取1.2节中ShellExecute同时打开两个文件的处理方法,从创建COM的API: CoCreateInstanceEx开始,用wt命令跟踪整个函数的执行,保存下来后比较两种不同情况的异同。通过这个方法肯定是可以找出原因的,不过要想用wt命令一直跟踪到LoadLibrary函数加载这个DLL,可能需要执行一整天。所以,应该找一个可操作性更强一点的方法来检查。既然最后要追踪到LoadLibrary为止,那何不在这个函数上设置断点,观察检查DAO350.DLL加载起来的情况?

在LoadLibrary上设定断点并不是一个很好的方法。因为:

1.         加载DLL不一定要调用LoadLibrary的。可以直接调用Native API,比如ntdll!LdrLoadDll。

2.         假设有几十个DLL要加载,如果每次LoadLibrary都断下来,操作起来也是很麻烦的事情。虽然可以通过条件断点判断LoadLibrary的参数来决定是否断下来,但是设定条件断点也是很麻烦的。

最好的方法,就是使用通知,在moudle load的时候,系统给调试器发送通知。由于Windbg在收到moudle load通知的时候,可以使用通配符来判断 DLL的名字,操作起来就简单多了。首先,在Windbg中用sxe ld:dao*.dll设置截获Moudle Load的通知,当文件名是dao*.dll的时候,Windbg就会停下来。(关于Windbg的详细信息,以及这里使用到的命令,后面都有章节详细介绍)。看到的结果就是:

0:008> sxe ld:dao*.dll

ModLoad: 1b740000 1b7c8000   C:/Program Files/Common Files/Microsoft Shared/DAO/DAO360.DLL

eax=00000001 ebx=00000000 ecx=0013e301 edx=00000000 esi=7ffdf000 edi=20000000

eip=7c82ed54 esp=0013e300 ebp=0013e344 iopl=0         nv up ei pl zr na po nc

cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000             efl=00000246

ntdll!KiFastSystemCallRet:

7c82ed54 c3               ret

ntdll!KiFastSystemCallRet                            

ntdll!NtMapViewOfSection                             

ntdll!LdrpMapViewOfDllSection                        

ntdll!LdrpMapDll                                     

ntdll!LdrpLoadDll                                     

ntdll!LdrLoadDll                                     

0013e9c4 776ab4d0 0013ea40 00000000 00000008 kernel32!LoadLibraryExW

ole32!CClassCache::CDllPathEntry::LoadDll            

ole32!CClassCache::CDllPathEntry::Create_rl          

ole32!CClassCache::CClassEntry::CreateDllClassEntry_rl

ole32!CClassCache::GetClassObjectActivator           

ole32!CClassCache::GetClassObject                    

ole32!CServerContextActivator::GetClassObject        

ole32!ActivationPropertiesIn::DelegateGetClassObject

ole32!CApartmentActivator::GetClassObject            

ole32!CProcessActivator::GCOCallback                 

ole32!CProcessActivator::AttemptActivation           

ole32!CProcessActivator::ActivateByContext           

ole32!CProcessActivator::GetClassObject              

ole32!ActivationPropertiesIn::DelegateGetClassObject

ole32!CClientContextActivator::GetClassObject        

ole32!ActivationPropertiesIn::DelegateGetClassObject

ole32!ICoGetClassObject                              

ole32!CComActivator::DoGetClassObject                

ole32!CoGetClassObject                               

VB6!VBCoGetClassObject                               

VB6!_DBErrCreateDao36DBEngine                        

通过检查LoadLibraryExW的参数,可以看到:

0:000> du 0013ea40

0013ea40 "C:/Program Files/Common Files/Mi"

0013ea80 "crosoft Shared/DAO/DAO360.DLL"

从上面的信息可以看到:

1.         DAO360不是通过CoCreateInstanceEx加载进来的,而是另外一个COM API: CoGetClassObject。所以如果对CoCreateInstanceEx做想当然的跟踪,就浪费时间了。

2.         COM调用的发起者是VB6!_DBErrCreateDao36DBEngine这个函数。应该仔细检查这个函数。

有了前面DLL HELL 案例的教训,在检查这个函数前,首先检查VB6.EXE的版本。发现正常情况下的版本是6.00.9782,有问题的机器上的版本是6.00.8176。在有问题的机器上安装Visual Studio 6,SP6升级VB6版本后,问题解决。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值