极语言官方网站

原子名单——使用示例

原子表是系统定义的表,用于存储字符串和相应的标识符。

Atom 表中放置一个字符串(称为 原子名称),并接收一个 16 位整数(称为 atom),该整数可用于访问该字符串。

系统提供许多原子表。 每个原子表都有不同的用途。

例如,Dynamic Data Exchange (DDE) 应用程序使用 全局 atom 表 与其他应用程序共享item-name和topic-name字符串。

DDE 应用程序将全局原子传递给其合作伙伴应用程序,而不是传递实际字符串。

伙伴使用原子从 atom 表中获取字符串。应用程序可以使用本地 Atom 表来存储其自己的项名称关联。

系统使用应用程序无法直接访问的 Atom 表。 但是,应用程序在调用各种函数时使用这些原子。

例如,注册的剪贴板格式存储在系统使用的内部 Atom 表中。

应用程序使用 注册剪板 函数将 atom 添加到此 Atom 表。

此外,已注册的类存储在系统使用的内部原子表中。

应用程序使用 注册窗类 或 注册窗体 函数将 atom 添加到此 Atom 表。



全局 Atom 表

全局 Atom 表可供所有应用程序使用。

当应用程序在全局 Atom 表中放置字符串时,系统会生成一个在整个系统中唯一的原子。

具有 atom 的任何应用程序都可以通过查询全局 atom 表来获取它标识的字符串。

定义用于与其他应用程序共享数据的专用 DDE 数据格式的应用程序应将格式名称放在全局 Atom 表中。

此技术可防止与系统或其他应用程序定义的格式的名称发生冲突,并使标识符 (原子) 其他应用程序可用的消息或格式。



用户 Atom 表

除了全局 Atom 表,用户 Atom 表是另一个系统 Atom 表,也是在所有进程之间共享的。

用户原子表用于 win32k 内部的少量方案;例如,Windows 模块名称、win32k 中的已知字符串、OLE 格式等。

尽管应用程序不直接与用户 Atom 表交互,但它们会调用多个 API(如 注册窗类 注册消息 和 注册剪板),

这些 API 可将条目添加到用户 Atom 表。 添加的 注册窗类 条目可由 注销窗类 删除。

但是,在会话结束之前,不会删除 由 注册消息 和 注册剪板 添加的条目。

如果用户 Atom 表没有更多的空间,并且传入的字符串不在表中,则调用将失败。



Atom 表大小

许多关键 API(包括 创建窗口)依赖于用户原子。

因此,用户 Atom 表中的空间耗尽将导致严重问题:例如,所有应用程序都可能无法启动。

下面是一些建议,可确保应用程序有效地利用 Atom 表,并保留应用程序和系统的可靠性和性能:

1。应限制应用对用户 Atom 表的使用。

使用 API(如 注册窗类、注册消息 或 注册剪板 )存储唯一字符串会占用用户 Atom 表中的空间,

其他应用全局使用该空间来使用字符串注册窗口类。

如果可能,应使用 添加名单/删除名单 将字符串存储在本地 Atom 表中,或者使用 添全名单/删全名单 (如果需要原子跨进程)。

2。如果担心应用程序会导致用户 Atom 表问题,

可以通过连接内核调试器并在调用UserAddAtomEx (bae1 win32kbase!UserAddAtomEx /p "kc10;g")

时进入进程来调查根本原因。

在调用堆栈上查找 user32! ,以查看正在调用哪个 API。

该方法类似于识别全局原子表泄漏中介绍 的全局原子表问题检测。 转储用户 Atom 表内容的另一种方法是

在从 0xC000 到 0xFFFF 的可能原子范围内调用 剪切板名 。

如果在应用程序运行时,总原子计数稳步上升,或者在关闭应用时未返回到基线,则存在问题。



本地 Atom 表

应用程序可以使用本地 Atom 表有效地管理仅在应用程序中使用的大量字符串。

这些字符串和关联的原子仅适用于创建表的应用程序。

许多结构中需要相同字符串的应用程序可以通过使用本地 Atom 表来减少内存使用量。

应用程序可以将字符串放置在 atom 表中,并将生成的原子包含在结构中,

而不是将字符串复制到每个结构中。 这样,字符串在内存中只出现一次,但可以在应用程序中多次使用。

在搜索特定字符串时,应用程序还可以使用本地 Atom 表来节省时间。

若要执行搜索,应用程序只需将搜索字符串放在 atom 表中,并将生成的原子与相关结构中的原子进行比较。

比较原子通常比比较字符串快。Atom 表作为哈希表实现。

默认情况下,本地 Atom 表对其哈希表使用 37 个存储桶。

但是,可以通过调用 初始名单 函数更改使用的存储桶数。

但是,如果应用程序调用 初始名单,则必须在调用任何其他 Atom 管理功能之前执行此操作。



Atom 类型

应用程序可以创建两种类型的原子:字符串原子和整数原子。

整数原子和字符串原子的值不重叠,因此这两种类型的原子都可以在同一代码块中使用。

多个函数接受字符串或原子作为参数。

将原子传递给这些函数时,应用程序可以使用 MAKEINTATOM 宏将原子转换为函数可以使用的形式。

以下部分介绍原子类型。



字符串 Atoms

当应用程序将以 null 结尾的字符串传递给 添全名单、添加名单、查全名单 和 查找名单 函数时,

它们接收 字符串原子 (16 位整数) 返回。 字符串原子具有以下属性:

字符串原子的值在 0xC000 到0xFFFF 范围内。
在 Atom 表中搜索原子名称时,大小写并不重要。 
此外,整个字符串必须在搜索操作中匹配;不执行子字符串匹配。
与字符串 Atom 关联的字符串的大小不能超过 255 个字节。 此限制适用于所有原子函数。
引用计数与每个原子名称相关联。
每次将原子名称添加到表中时,计数都会递增,并在每次从表中删除原子名称时递减。
这可以防止同一字符串 Atom 的不同用户销毁彼此的原子名称。
当原子名称的引用计数等于零时,系统会从表中删除原子和原子名称。


整数原子

整数原子与字符串原子的不同之处在于:
整数原子的值在0x0001 到 0xBFFF 的范围内。
整数原子的字符串表示形式为 #dddd,其中 dddd 表示的值是十进制数字。 忽略前导零。
没有与整数原子关联的引用计数或存储开销。

Atom 创建和使用情况计数

应用程序通过调用 添加名单 函数创建本地原子;它通过调用 添全名单 函数创建全局原子。

这两个函数都需要指向字符串的指针。 系统会在相应的 atom 表中搜索字符串,

并将相应的原子返回到应用程序。 对于字符串原子,

如果字符串已驻留在 atom 表中,系统会在此过程中递增字符串的引用计数。

重复调用以添加相同的原子名称将返回相同的原子。

如果在调用 添加名单 时表中不存在原子名称,则将原子名称添加到表中,并返回新的原子。

如果它是字符串原子,则其引用计数也设置为 1。

当应用程序不再需要使用本地原子时,应调用 删除名单 函数;当它不再需要全局原子时,它应调用 删全名单 函数。

对于字符串原子,其中任一函数会将相应原子的引用计数减少 1。

当引用计数达到零时,系统会从表中删除原子名称。

只要字符串原子的引用计数大于零,字符串原子的原子名称就保留在全局原子表中,

即使在将它放入表中的应用程序终止之后也是如此。

当关联的应用程序终止时,将销毁本地原子表,而不考虑表中原子的引用计数。



Atom-Table查询

应用程序可以使用 查找名单 或 查全名单 函数来确定特定字符串是否已在原子表中。

这些函数在原子表中搜索指定的字符串,如果字符串存在,则返回相应的原子。

应用程序可以使用 获取名单 或 取全名单 函数从原子表检索原子名称字符串,

前提是应用程序具有与所搜索的字符串对应的原子。

这两个函数将指定原子的 atom-name 字符串复制到缓冲区,并返回复制的字符串的长度。

获取名单 从本地原子表检索原子名称字符串, 取全名单 从全局原子表检索原子名称字符串。



Atom 字符串格式

添加名单、添全名单、查找名单 和 查全名单 函数采用指向以 0 结尾的字符串的指针。

应用程序可以通过以下方式之一指定此指针。

字符串格式说明
#dddd指定为十进制字符串的整数。 用于创建或查找整数原子。
字符串 atom 名称字符串原子名称。 用于向原子表添加字符串原子名称,并接收原子作为返回。

原子名单 ——相关函数
中文名称英文名称示例作用
添加名单AddAtomA编号=添加名单("张三先生")将一个字符串[255]添加到本地原子表(程序内)中,并返回一个唯一标识
删除名单DeleteAtom删除名单(编号)从本地原子表中删除关联的字符串
查找名单FindAtomA编号=查找名单("李四")从本地原子表中查找字符串,得到标识
获取名单GetAtomNameA获取名单(编号,名称,长度)获取本地原子表中编号相对应的字符串
初始名单InitAtomTable初始名单(数量)初始化本地原子表并将其设置为指定的大小
添全名单GlobalAddAtomA编号=添全名单("张三先生")将一个字符串[255]添加到全局原子表(系统内)中,并返回一个唯一标识
删全名单GlobalDeleteAtom删全名单(编号)从全局原子表中删除关联的字符串
查全名单GlobalFindAtomA编号=查全名单("李四")从全局原子表中查找字符串,得到标识
取全名单GlobalGetAtomNameA取全名单(编号,名称,长度)获取全局原子表中编号相对应的字符串