☰
跨进程读取文档内容:Windows-classic-samples 中 UIAutomationDocumentClient 的 UI Automation 文本模式实战解析
2026/10/15 3:39:45 网站建设 项目流程
  • 示例工程

【免费下载链接】Windows-classic-samples

This repo contains samples that demonstrate the API used in Windows classic desktop applications.

项目地址:https://gitcode.com/gh_mirrors/wi/Windows-classic-samples
点击查看免费下载

导读

本文以 Windows-classic-samples 仓库中的 UIAutomationDocumentClient 示例 为骨架,系统讲解如何利用微软 UI Automation(UIA)的 Text / TextRange 控件模式,从一个命令行工具中读取其他应用窗口正在显示的文档内容——包括标题、各级标题、批注(annotation)、当前选区、可见文本以及光标(caret)位置等。读完本文,你将掌握 UIA 文本模式的完整调用链、命令行的构建与运行方式,以及如何基于 UiaDocumentClient.cpp 的源码理解并复用这套跨进程文档内容提取方案。

示例是什么:一个“文档内容客户端”

UIAutomationDocumentClient 是一个纯命令行工具(UiaDocumentClient.exe),其核心能力是:通过 Microsoft UI Automation 控件模式,访问另一个应用窗口中正在展示的文档内容。它不要求目标文档应用做任何特殊集成,只要该窗口支持Text(文本)控件模式即可——例如记事本(Notepad)这类文本控件天然支持该模式。

按仓库 README 的表述,示例展示了对窗口内容的多种检索能力,包括:

  • 标题(heading)与文档各段落的样式文本;
  • 批注(annotation,如文档评论及其作者、时间、类型);
  • 当前选区(selection)与可见文本(visible text);
  • 整篇文档范围(document range);
  • 光标位置及其相关的插入符状态信息。

从源码结构看,这个示例同时依赖TextPattern2(IUIAutomationTextPattern2)及其扩展特性:批注范围(RangeFromAnnotation)、光标范围(GetCaretRange),这些是普通 Text 模式不具备的能力,因此在 UiaDocumentClient.cpp 中明确使用GetCurrentPatternAs(UIA_TextPattern2Id, ...)获取文本模式对象。

核心技术:Text 与 TextRange 控件模式

UI Automation 的文本体系由两个层面的接口组成,示例分别使用了它们:

接口 / 概念在本示例中的用途
IUIAutomationTextPattern获取文档范围(DocumentRange)、选区(GetSelection)、可见范围(GetVisibleRanges)等基础文本信息
IUIAutomationTextPattern2在 Text 模式基础上扩展出批注(RangeFromAnnotation)与光标(GetCaretRange)能力,是示例实际获取的模式对象
IUIAutomationTextRange表示一段文本范围,支持GetText、FindAttribute、Move、MoveEndpointByRange、CompareEndpoints、ExpandToEnclosingUnit、Clone等操作
IUIAutomationTextRangeArray返回多段范围时使用,例如多选区或可见文本可能不止一段

示例在 TextPatternExplorer::GetRange 中演示了四类范围的获取方式:

  • 文档范围:_textPattern->get_DocumentRange(&uiaRange);
  • 光标范围:_textPattern->GetCaretRange(&isActive, &uiaRange),同时返回该光标是否处于活动状态;
  • 选区:_textPattern->GetSelection(&uiaRangeArray);
  • 可见文本:_textPattern->GetVisibleRanges(&uiaRangeArray)。

其中选区和可见范围以“范围数组”形式返回。源码注释特别指出,对于复杂文本模式这些属性返回的是数组,示例为保持简洁只取第一个元素(源码 L1041-L1061),实战中若要完整处理多选区/多可见区域,应当遍历整个数组。

源码结构与关键类设计

示例的全部逻辑都位于单个源文件 UiaDocumentClient.cpp(约 1240 行),配套工程文件为 UiaDocumentClient.sln 与 UiaDocumentClient.vcxproj。整个程序围绕三个封装类展开:

Comment:批注元素的包装

Comment 类 包装一个支持批注的元素,负责:

  • 获取AnnotationPattern(GetCurrentPatternAs(UIA_AnnotationPatternId, ...)),用于读取批注的类型名、作者、日期时间;
  • 调用textPattern->RangeFromAnnotation(...)把批注还原为一段文本范围;
  • 打印批注概要(仅名称)或完整信息(类型/作者/时间/内容)。

Range:文本范围的包装

Range 类 是示例中最核心的封装,它持有IUIAutomationTextRange并提供多种检索能力:

  • Print:输出当前范围文本(概要模式下截断为 40 字符并附字符总数,完整模式下全量输出,读取上限为maxLength = 10000字符);
  • FindAndPrintHeaders/FindAndPrintStyle:通过FindAttribute(UIA_StyleIdAttributeId, ...)逐段查找 Title、Heading1、Heading2、Heading3 样式的文本,并用MoveEndpointByRange推进“剩余范围”的起点继续向后搜索;
  • FindAndPrintComments/FindCommentsInRange/WalkForCommentsInRange:以TextUnit_Format为单位逐段移动范围,读取UIA_AnnotationObjectsAttributeId属性收集批注元素,并通过CompareElements去重,限制最多收集maxComments = 25条;
  • PrintCaretInfo:读取UIA_IsActiveAttributeId、UIA_CaretPositionAttributeId、UIA_CaretBidiModeAttributeId、UIA_SelectionActiveEndAttributeId四个属性,并通过GetBoundingRectangles输出光标范围的屏幕坐标矩形。

值得注意的是,示例专门处理了 UI Automation 中两个保留值:ReservedNotSupportedValue(提供方不支持该属性)和ReservedMixedAttributeValue(范围内属性混合),从源码 WalkForCommentsInRange 可看到它对这两类返回值做了区分处理,这也是在实际开发中处理文本属性时容易踩坑的地方。

TextPatternExplorer:交互主循环

TextPatternExplorer 类 负责把上述能力组织成一个交互式命令行界面:读取用户输入、解析命令、维护“当前活动范围”(currentRange)与“当前活动批注”(currentComment)两个状态,并在每次输入前打印当前范围的概要信息。

构建示例

README 给出的构建前提是:

警告:本示例要求 Microsoft Visual Studio 2013 或更高版本(任意 SKU),且无法在 Visual Studio Express 2013 for Windows 中编译。

按 README 的步骤构建:

  1. 启动 Visual Studio,选择File → Open → Project/Solution;
  2. 进入示例目录下的 C++ 目录,双击解决方案文件 UiaDocumentClient.sln;
  3. 按F7(Visual Studio 2013 中是 F6),或使用Build → Build Solution编译。

仓库配套的 ReadMe.txt 还补充了命令行构建方式:

msbuild UiaDocumentClient.sln

从 UiaDocumentClient.vcxproj 可以确认工程的关键配置:

  • 支持Debug/Release × Win32/x64四种配置;
  • 平台工具集为V110(对应 VS2011/2012 时代工具集,但 README 声明可用 VS2013+ 打开);
  • 字符集为Unicode,子系统为Console(命令行程序);
  • 链接依赖uiautomationcore.lib,编译警告级别为 Level4 且将警告视为错误(TreatWarningAsError=true),因此编译时若遇到警告会直接报错,这也是本项目代码风格严谨的原因之一。

运行示例与两种目标窗口选择方式

README 描述的典型运行流程:

  1. 在一个支持 UI Automation Text 控件模式的应用(如记事本)中打开一篇文档;
  2. 在示例安装目录下运行UiaDocumentClient.exe(如<install_root>\UI Automation Document Content Client Sample\C++\Debug目录);若在 Visual Studio 中运行,按F5(带调试)或Ctrl+F5(不带调试),也可从Debug菜单选择Start Debugging / Start Without Debugging;
  3. 程序启动后3 秒内,把鼠标光标移动到第 1 步打开的文档窗口上——程序会获取鼠标所在位置的元素;
  4. 随后程序列出命令清单,用命令即可检索文档内容。

从 wmain 入口 可以看到更完整的运行方式细节,示例支持零参数或一个参数两种模式:

UiaDocumentClient [hwnd] Explore the text pattern for the specific [hwnd] (in hex) If no [hwnd] is supplied, it will get the element under the mouse pointer.
  • 不带参数:先打印 “Getting element at cursor in 3 seconds...”,Sleep(3000)等待 3 秒后用GetCursorPos获取鼠标坐标,再调用_automation->ElementFromPoint(pt, &element)得到鼠标下的元素;
  • 带一个十六进制 HWND 参数:例如UiaDocumentClient 0x0006093A。程序用swscanf_s(argv[1], L"%x", ...)解析十六进制窗口句柄,先通过IsWindow校验句柄合法性,再调用_automation->ElementFromHandle(hwnd, &element)获取元素。窗口句柄可以通过 Spy++、Inspect 等工具获取。

得到目标元素后,程序调用 FindTextPatternElement:用CreatePropertyCondition(UIA_IsTextPattern2AvailablePropertyId, VARIANT_TRUE, ...)构造“支持 TextPattern2”的属性条件,再对元素及其全部后代执行FindFirst(TreeScope_Subtree, ...)深度搜索,找到第一个支持 TextPattern2 的元素。这正是“记事本窗口顶层元素不直接支持文本模式、但内部编辑控件支持”这类场景能正常工作的原因。

命令行交互命令详解

程序启动后即进入TextPatternExplorer的交互循环,输入任意命令(不区分大小写)即可操作。README 与源码 Welcome 帮助输出 中的完整命令如下:

命令功能对应实现
help显示帮助信息Welcome()
print打印当前活动范围的完整文本Range::Print(false)
document把活动范围设置为整篇文档get_DocumentRange
selection把活动范围设置为当前选区GetSelection
visible把活动范围设置为当前可见文本GetVisibleRanges
headers在活动范围内查找并打印 Title、Heading 1/2/3 样式文本Range::FindAndPrintHeaders
comments列出活动范围内所有批注(编号 + 名称概要)Range::FindAndPrintComments
comment {n}把活动批注设置为活动范围内第 n 条批注SelectComment/Range::SelectComment
commentrange把活动范围设置为活动批注对应的文本范围RangeFromComment/RangeFromAnnotation
printcomment打印活动批注的完整信息(类型、作者、日期时间、内容)Comment::Print(false)
caret获取光标范围并打印光标专属信息Range::PrintCaretInfo
exit退出程序—

几个值得注意的交互细节:

  • 命令解析通过 GetCommand 完成:将输入小写化后与预置命令前缀逐条wcsncmp比较,因此comment 3这类带参数命令也能正确匹配;
  • caret命令在执行前会Sleep(2000)暂停 2 秒并提示用户“如需聚焦文本控件请在此期间操作”,随后获取的光标范围还会打印其活动状态、位于行首/行尾/行中、双向文本(BiDi)方向、选区活动端以及光标外接矩形坐标;
  • comments输出的批注编号可以直接配合comment {n}使用,进而通过commentrange把活动范围跳到该批注对应的正文位置——这是文本模式“批注 ↔ 文本范围”双向映射的完整演示。

底层运行环境与约束

  • 初始化顺序:示例在wmain中先CoInitialize(NULL)初始化 COM,再CoCreateInstance(__uuidof(CUIAutomation8), ..., CLSCTX_INPROC_SERVER, ...)创建 UIA 自动化对象(源码 L1162-L1175)。注意这里使用的是CUIAutomation8(Windows 8 版本自动化对象),而不是更早的CUIAutomation,这与示例依赖 TextPattern2 等 Windows 8 新增能力一致。
  • 系统要求:README 明确标注 Client 为Windows 8.1、Server 为Windows Server 2012 R2;仓库说明同时指出,示例为 Windows 8.1 / Windows Server 2012 R2 + Visual Studio 2013 创建,多数情况下可在后续版本上不加修改地运行。
  • 工具链限制:必须使用 VS2013 或更高版本(任意 SKU),Express 版不可编译。
  • 可移植性提示:源码中的CUIAutomation8、UIA_TextPattern2Id、UIA_AnnotationPatternId、UIA_CaretPositionAttributeId等属于较新的 UIA API,若要在旧系统上运行,需要确认目标平台提供对应支持。

总结与延伸

UIAutomationDocumentClient 是理解 UI Automation文本模式家族最直接的样例:它把IUIAutomationTextPattern2的文档范围、选区、可见范围、批注、光标等全部关键入口,浓缩进一个无需任何 UI 依赖的命令行工具中,并给出了完整的错误处理(每个 COM 调用失败都会打印 HRESULT)与资源释放(所有权式封装、SysFreeString、VariantClear、CoTaskMemFree等)范本。

如果希望进一步深入,可以对照同一仓库中的 UIAutomationDocumentProvider(文档提供方)与 UIAutomationFragmentProvider(片段提供方)示例,从“客户端消费”与“服务端提供”两个视角交叉理解文本模式的双向契约;本示例源码 UiaDocumentClient.cpp 与工程文件 UiaDocumentClient.vcxproj 均可直接作为后续二次开发的起点。

  • 示例工程

【免费下载链接】Windows-classic-samples

This repo contains samples that demonstrate the API used in Windows classic desktop applications.

项目地址:https://gitcode.com/gh_mirrors/wi/Windows-classic-samples
点击查看免费下载
上一篇:Web-Dev-For-Beginners 实战:基于 CO2 Signal API 构建 Carbon Trigger 浏览器扩展
下一篇:OpenClaw 接入 Inworld 流式语音合成:安装、配置与 MP3/OGG_OPUS/PCM 输出实战

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询