您现在的位置: IT专家网 > WinSystem子站 > 技巧
XP两种工具在代码中检测并堵塞 GDI 泄漏
Windows 需要一种不太相同的 GDI 泄漏方法,他构建并说明了两种工具,这两种工具旨在检测并消除在 Windows XP、Windows 2000 和 Windows NT 上运行的应用程序中的 GDI 泄漏。
魔法在其他地方存在。当堆栈由 DoStackAddressDump 转储,而地址存储于返回的数组中时,该方法还可以利用 CSymbolEngine 中定义的 SymGetModuleInfo、SymGetSymFromAddr 和 SymGetLineFromAddr(有关实现细节,请参见代码下载中 StackManager.cpp 中的 ConvertAddress)找到对应于地址的符号。为什么现在进行转换?答案很简单:在这个特定的时刻,您确信相应的 DLL 被加载,但稍后调用 DumpStackAllocation 时情况可能会不一样。
如果频繁创建 GDI 对象,就会产生许多堆栈转储,并保存在 m_HandleMap 中。但保存在该映射中的 CHandleInfo 对象保留的是地址数组,而不是转换后的字符串数组。技巧是利用一个映射成员(如 m_AddressToName)来跟踪转换。这就避免了在堆栈转储中存储长字符串来代替每个地址的 DWORD 类型,因此减少了对内存的消耗。另一个好处是,堆栈转储运行速度会更快,因为利用 m_AddressToName 来作为缓冲区,从而避免了对符号引擎进行查询。
即使您知道如何挂钩一系列 GDI 函数,您仍需要了解在哪些模块中调用这些 GDI。我们说,在共享 DLL 中使用 MFC 的应用程序正在创建一个 Cpen 对象,从而操作与 Windows 画笔相关的 API。真正调用 CreatePen(它返回画笔的句柄)是在 MFC DLL 中(而不是在调用应用程序代码中)完成的。如果只挂钩由可执行文件调用的 API 函数,就会丢失来自由应用程序使用的所有 DLL 的调用。
通过调试来确定需要修补的 DLL
在 Windows XP 中,获得在给定时间由某个进程加载的所有 DLL 的列表非常简单,这要感谢 PSAPI 函数 EnumProcessModules,正如 Matt Pietrek 在“Under the Hood”一文中所述,该文发表在 1996 年 8 月发行的 MSJ 中。但是,对于动态加载的库,这个问题需要一点窍门。除了挂钩系统函数,还必须挂钩主程序发出(ANSI 与 UNICODE 版本)的 LoadLibrary 调用,从而检测何时加载了一个新 DLL,并递归地对其进行相同的挂钩处理。
需要回答最后的两个问题。第一,如何知道在侦探的进程中哪些 DLL 需要修补?第二,如何确保这些代码在另一个进程中执行?如果有这样的解决方案,那么还有可能用它来显示任何 GDI 对象句柄的图形化表示。在 2002 年 8 月发表的有关调试的文章中,一个调试过的进程通过使用 Win32 调试 API 来动态或静态地检测加载的 DLL。这种方法的主要缺点是需要启动和调试应用程序。与 Windows 9x 版本的 GDIUsage 不同,被检测的 GDI 对象必须是由调试过的应用程序分配的对象,这可以在图 1 中看到。
Win32 调试 API 允许您编写代码来启动某个应用程序(调试对象),并且当发生事件时(如加载一个新的 DLL)获得通知。这正是您所需要的。要轻松地编写调试器,您只需重载在调试事件发生时要调用的虚方法。CGDIDebugger 类派生于 CapplicationDebugger,在我的 2002 年 8 月的文章中介绍过 CapplicationDebugger。显示了 CGDIDebugger 所重载方法的名称,并解释了每个方法的作用。稍后我将讨论调试器和调试对象之间的通信机制。

图 5 搜索字符串
除了这些方法,已经重载了 OnOutputDebugStringDebugEvent,从而将调试对象(前缀为 >)留下的踪迹重定向到专用的列表框中。还有可能将一个选择复制到剪贴板,或者搜索一个字符串,如图 5 所示。当利用 TRACE 或 OutputDebugString 添加跟踪时,它就会出现在该列表框中。这是一种调试代码的有效机制,并可以标出调试对象(前缀为 >)和调试器的输出结果之间的差异。
- 本文关键词:
- API
- IT技术
- Windows
- Windows XP
- 操作系统

