CTOCIO IT专家网

天极传媒 比特网 | 天极网 | IT专家网 | IT商网 | 52PK游戏网 | 手机天极 | IT分众 |
IT专家网搜索

您现在的位置: IT专家网 > WinSystem子站 > 技巧

XP两种工具在代码中检测并堵塞 GDI 泄漏

作者: Christophe Nasarre ,  出处:微软, 责任编辑: 韩博颖, 
2008-05-20 13:13
  Windows 需要一种不太相同的 GDI 泄漏方法,他构建并说明了两种工具,这两种工具旨在检测并消除在 Windows XP、Windows 2000 和 Windows NT 上运行的应用程序中的 GDI 泄漏。

  从调试器到调试对象的通信

  调试器使用 s_dwInfiltratedThreadID 共享变量来发送一个请求(利用 PostThreadMessage 通过一个简单的 Windows 消息),该请求将由调试对象中的渗透线程来处理。当调试对象通知调试器这样的一个请求已经完成时,需要另一个 s_dwCallingThreadID 共享变量。例如,当用户单击“Take Snapshot!”按钮时,GDIUsage 需要从调试对象收集已分配的 GDI 对象。

  GDIUsage 发送一条 TM_GET_LIST 消息给调试对象中的渗透线程,调试对象的值保存在 s_dwInfiltratedThreadID 中。它将执行连同参数 UM_SNAPSHOT_READY 一起发送给 OnGetList 函数,该参数将被用作回调消息被 GDIUsage 主对话框接收。为什么不简单地使用同样的 TM_GET_LIST?答案与共享代码有关。“Take Snapshot!”与“Compare”按钮都需要获得相同的已分配 GDI 对象列表(虽然使用该列表的方法不同),以更新 GDIUsage 用户界面的相应部分。

  为了总结一下在前面段落中我已经论述的内容,TM_GET_LIST 线程消息触发对调试对象端 GDI 对象的处理。另外,根据用户定义的两条消息,有两种方法可以更新 GDIUsage 主对话框:UM_SNAPSHOT_READY 与 UM_COMPARE_READY。

  渗透线程唤醒并要求 OnGetList 来处理请求。该 CGDITraceApp 方法通过修补的存根枚举出调试对象中检测到的 GDI 分配,并将每个对象的说明(句柄值和类型)按照下面的 GDI_LIST 格式复制到由 m_lpvMem z指向的内存映射文件共享的缓冲区中:

  

      typedef struct
  {
  DWORD dwType;
  HGDIOBJ hObject;
  } GDI_ITEM;
  typedef struct
  {
  DWORD dwCount; // count of meaningful GDI_ITEM slots in Items
  GDI_ITEM Items[];
  } GDI_LIST;
  为了通知调试器列表已经准备好,一条相同的 TM_GET_LIST 消息被发送回由 s_dwCallingThreadID 识别的线程。该消息由负责调试事件的线程在 GDIUsage 上下文中接收并发送给 CGDIDebugger::OnThreadMessage。该方法通过用一个指向共享内存的指针向主对话框发送正确的用户消息(本示例为 UM_SNAPSHOT_READY)来通知 UI 线程,共享内存由内存映射文件定义,而调试对象将 GDI 对象保存在该文件中。通过使用 CGdiResources 的 CreateFromList 方法,这个自动封送处理的序列化列表用来实例化 CGdiResources。该类用来包装一个 GDI 对象列表,同时还提供诸如枚举与图形显示等服务。

  死锁与计时问题

  在深入了解远程 GDI 对象的图形显示之前,您应当了解一下可能发生的死锁问题。以前探讨的收集 GDI 对象的技术都是异步的,因为它要依赖调试器和调试对象之间交换的 Windows 消息。如果需要进行强同步通信,可以使用 Win32 事件。例如,渗透线程等待一个有特定名称的事件,当一条消息发送到它的队列中或者事件获得信号通知时,它就调用 MsgWaitForMultipleObjectsEx 来唤醒。在该实现中,用事件来要求线程结束它的生存期,因此不是真正的同步。

  另一种需要真正同步行为的情况是,当调试对象加载一个 DLL 时修补 GDI 调用。为了截获 GDI 调用,调试器必须尽快通知渗透线程。否则,调试对象可能在安装截获存根之前就开始分配 GDI 对象。这里是一个不错的实现方案:

  1.调试器线程获得通知,通过 WaitForDebugEvent 返回的LOAD_DLL_DEBUG_EVENT 已在调试对象中加载了一个 DLL。

  2.OnLoadDLLDebugEvent 重写方法接收 DLL 对应的 hModule,将它保存在一个新的共享变量中,并通过为事件发送信号来请求调试对象为该特定的 DLL 修补 GDI 调用。

  3.如果调试对象加载另一个 DLL,为了避免重新进入,OnLoadDLLDebugEvent 等待另一个在调试对象完成其修补工作后的信号通知事件。

  4.MsgWaitForMultipleObjectsEx 唤醒调试对象-渗透线程,因为它等待的一个事件已经由信号通知。

  5.CGDITraceApp::OnNewDLL 方法为在由共享变量定义的地址处加载的 DLL 重定向 GDI 调用,该共享变量由调试器用 DLL hModule 填充。

  6.调试器等待的事件由渗透线程发信号通知。

  7.渗透线程调用 MsgWaitForMultipleObjectsEx 等待完成另一个请求。

  8.调试器线程继续进行,因为它等待的事件已经由信号通知。

  注意,最后两步的顺序号相同,因为它们的代码在由 Windows 调度的两个不同线程中运行。不要指望一个会在另一个之前执行。

?10?? 9 3 4 5 6 7 8 9 10 :

网友评论

笔名 
请您注意:遵守国家有关法律、法规,尊重网上道德,承担一切因您的行为而直接或间接引起的法律责任。    IT专家网友拥有管理笔名和留言的一切权利。
  • 周排行榜
  • 月排行榜

邮件订阅

天极服务 | 关于我们 | 网站律师 | 加入我们 | 联系我们 | 广告业务 | 友情链接 | 我要挑错
All Rights Reserved, Copyright 2004-2008, Ctocio.com.cn
渝ICP证B2-20030003号 如有意见请与我们联系 powered by 天极内容管理平台CMS4i