重要
本主题中的信息适用于所有版本的 Windows 10 及更高版本。 我们在这里将这些版本称为“Windows”,并在必要时指出任何例外情况。
所有版本的 Windows 都共享称为核心 OS 的操作系统 (OS) 组件(在某些上下文中,此通用基础也称为 OneCore)。 在核心 OS 组件中,Win32 API 组织为称为 API 集的功能组。
API 集的目的是在实现给定 Win32 API 的主机 DLL 与 API 所属的功能协定之间提供体系结构分隔。 API 集在实现和协定之间提供的分离为开发人员提供了许多工程优势。 具体而言,在代码中使用 API 集可以提高与 Windows 设备的兼容性。
API 集合专门适用于以下场景:
尽管电脑上支持 Win32 API 的完整广度,但其他Windows设备(如 HoloLens、XBOX 和其他设备)仅提供了 Win32 API 的子集。 API 集名称为你提供了一个稳定的查询依据,因此你的应用可以在运行时判断当前设备上某项功能是否可用。 查询本身由 IsApiSetImplemented 函数执行。
某些 Win32 API 实现存在于不同 Windows 设备上具有不同名称的 DLL 中。 在检测 API 可用性和对 API 进行延迟加载时,使用 API 集名称而不是 DLL 名称,可以确保无论 API 实际在哪里实现,都能正确定位到其实现。
有关详细信息,请参阅 API 集加载程序操作和检测 API 集可用性。
API 集和 DLL 是否相同?
否 - API 集名称标识 协定,而不是文件。 在运行时,加载程序通过当前设备上的 API 集架构方案解析该契约,并将该引用路由到承载该实现的 DLL。 这是一种实现隐藏技术,作为调用者,你不必确切地知道哪个模块托管信息。
该技术允许在不同 Windows 版本和版次上重构模块(拆分、合并、重命名等)。 你的应用程序仍然能够链接,并且在运行时仍会路由到正确的代码。
那么,为什么 API 集的名称中具有 .dll? 原因在于 DLL 加载程序的实现方式。 加载器是操作系统中负责加载 DLL 和/或解析对 DLL 的引用的部分,它通过导入表中按文件名写法拼写的模块名来确定要加载的内容。 API 集名称遵循相同的命名约定,以便它们能够放在同一位置。
加载程序可识别以 api- 或 ext- 开头的名称,并将其路由到 API 集运行时;它是加载程序的一个扩展,通过架构解析协定。 从那一点起,该名称由 API 集命名规则(而不是文件名)分析,因此 .dll 后缀不是正在解析的协定名称的一部分。
可以将 API 集名称传递给 LoadLibrary,或将其用作延迟加载目标。 当当前设备上的架构将协定映射到可用主机时,该操作会成功;电脑上的任何位置都不一定存在具有该名称的实际文件。 如果当前设备上未映射该契约,则直接调用 LoadLibrary 会失败。 延迟加载的引用表现不同:进程仍然会加载,而缺失会在稍后调用 API 时才显现出来。
无论哪种方式,成功的链接或加载本身都不是证明实现存在的证据。 若要确定这一点,请参阅 “检测 API 集可用性”。
链接伞型库
为了更轻松地将代码限制为核心 OS 中支持的 Win32 API,我们提供了一系列伞型库。 使用伞式库可以链接单个库,而不是为调用的每个 API 标识单个导入库。
有关更多详细信息,并选择与目标匹配的伞库,请参阅Windows伞库。
API 集协定名称
API 集通过协定名称进行标识,该名称遵循库加载器可识别的约定。
所有合约名称均遵循以下命名约定:
- 名称以字符串 api 或 ext-开头。
- 名称的正文可以是字母数字字符,也可以是短划线 (-)。 波浪号(~)仅作为组名称前的分隔符显示。
- 此名称不区分大小写。
两种形式的协定名称正在使用中,你可能会遇到任一形式。
带版本号的协定名称以序列l<n<->n<->n>结尾,其中n由十进制数字组成,例如ext-ms-win-core-samplefeature-l1-1-0。 末尾的数字用于标识该契约的某个特定版本,而这种形式的名称应被视为该版本的不可变标识符。
协定别名不带有任何版本,例如api-win-core-samplefeature。 它标识协定本身,而不是它的一个版本。 当协定将其单独可用的功能组织到命名组中时,通过将组名称追加到协定别名(用波形符分隔)来寻址组。 api-win-core-samplefeature~AdvancedOperations
samplefeature此处使用的名称是虚构Windows组件的说明名称。
api- 和 ext- 前缀
前缀是命名约定。 它最初旨在区分存在于每个符合条件的版本中的契约(api-)与可能不存在的契约(ext-)。 这种区别未一致应用,并且合同的角色可能会随时间而变化,而无需重命名合同。
加载程序没有为前缀分配任何意义;它按相同的规则解析 api 和ext- 名称。 不要从前缀推断可用性。 请改为查询它 - 请参阅 “检测 API 集可用性”。
使用合同名称
两类不同的操作都接受一个契约名称。
加载器操作(例如 LoadLibrary 或 P/Invoke)会在通常填写 DLL 模块名称的位置使用协定名称。 追加 .dll 项在该上下文中是常规的,但它不是 API 集名称解析所必需的,不是协定名称的一部分。 使用协定名称而不是物理 DLL 模块名称,以确保无论该 API 在当前设备上的何处实际实现,都能正确路由到相应的实现。 磁盘上不需要有具有该协定名称的文件。
可用性查询示例 通常省略 .dll 后缀,并使用与 API 的寻址方式匹配的表单:
| API 接口面 | 查询表单 | Example |
|---|---|---|
| 命名组 | <contract>~<group> |
api-win-core-samplefeature~AdvancedOperations |
| 默认组 | 不带 ~Default 的协定别名 |
api-win-core-samplefeature |
| 版本化契约 | 完整的带版本号的契约名称 | ext-ms-win-core-samplefeature-l1-1-0 |
组名称不能与带版本号的协定名称结合使用。
标识 Win32 API 的 API 集
若要确定特定 Win32 API 是否属于 API 集,请查看 API 参考文档中的要求表。 如果 API 属于 API 集,则文章中的要求表列出了 API 集名称和首次引入 API 集的 Windows 版本。 有关属于 API 集的 API 的示例,请参阅以下文章:
如果 API 的标头提供 Is<APIName>Present 帮助程序函数,在测试可用性时首选该帮助程序。 它已包含承载 API 的 API 集或组的正确名称。 有关详细信息,请参阅 检测 API 集可用性。