诊断 RegisterWindowMessage 泄漏

Diagnosing RegisterWindowsMessage leak

我们发现其中一个应用程序的生产服务器上的原子池资源耗尽。

使用出色的 AtomTableMonitor 工具,我们将问题隔离为通过 RegisterWindowsMessage 调用创建大量原子。他们都有这样的名字:

ControlOfs030D000000000270

末尾的数字发生变化。

我的问题是:我们如何确定是哪个进程在创建这些原子?

一些潜在资源:

https://blogs.msdn.microsoft.com/ntdebugging/2012/01/31/identifying-global-atom-table-leaks/

您可以使用 API Monitor 之类的工具并将其设置为仅跟踪 RegisterWindowsMessage。它将向您显示哪个进程正在使用此函数以及堆栈跟踪(尽管没有符号可能不太有用)。

此外,快速 Google 搜索 ControlOfs 会找到与您的问题相匹配的 https://forums.embarcadero.com/thread.jspa?threadID=47678。他们说这是 VCL 中的错误。如果您有代码,有人发布了此修复包:

http://andy.jgknet.de/blog/bugfix-units/vclfixpack-10/

如果您没有代码,我建议您在生产服务器中查找 Delphi/VCL 应用程序并尝试更新它们或报告问题。

"ControlOfs..."开头的原子是由Borland/Embarcadero的VCL(可视化组件库)框架在Delphi/C++Builder中创建的。这些原子实际上是 "ControlOfs<HInstance><ThreadID>" 的形式,其中 <HInstance><ThreadID> 是十六进制格式(因此,在您的情况下,HInstance = 0x030D0000 = 51183616ThreadID = 0x00000270 = 624)。

还有一个由VCL创建的原子名,形式为"Delphi<ProcessID>",其中<ProcessID>为十六进制格式。

这意味着使用 VCL 的应用程序的每个实例都会创建一个新的唯一 "Delphi..." 原子,并且其主 UI 线程会创建一个新的唯一 "ControlOfs..." 原子(这些原子用于通过 SetProp() 在 VCL 创建的 HWND 中存储 TWinControl 对象指针,供 VCL 的 FindControl()IsDelphiHandle() 实用函数使用)。两个原子都在应用程序启动时注册 GlobalAddAtom(),并在应用程序关闭时注销 GlobalDeleteAtom(),因此没有泄漏。

然而,在 Delphi/C++Builder 6 一直到 RADStudio XE2 中,还有另一个原子使用相同的 "ControlOfs..." 名称。此原子是使用 RegisterWindowMessage() 创建的(对于私人 RM_GetObjectInstance window 消息),无法注销。因此,每当受影响的 VCL 应用 运行 时,都会创建这个唯一的原子并随后泄露。

这最终在 2012 年由 Embarcadero 在 RADStudio XE3 中修复(Andreas Hausladen 为早期的 VCL 版本发布了 patch)。但是,使用旧版本 VCL 编译的现有应用程序会受到影响,如果不修补它们以使用具有 RegisterWindowMessage().

的静态名称,您将无法阻止它们泄漏。

因此,为了回答您的问题,结合使用 AtomTableMonitor 和任务管理器,您应该能够弄清楚您正在 运行 哪些应用程序是 VCL 应用程序,然后您可以单独检查它们泄漏的原子。或者,使用 SysInternals Process MonitorThread Create 过滤器来获取线程 ID 列表及其随时间的创建进程,然后您可以将这些线程 ID 与泄漏的原子名称进行匹配。