news 2026/5/6 9:27:20

C# 自动化神器10分钟上手 UI Automation,操控任何 Windows 软件

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C# 自动化神器10分钟上手 UI Automation,操控任何 Windows 软件

前言

在 C# 开发中常常面临一类棘手问题:如何让程序自动操作那些没有开放 API 的 Windows 应用?

比如批量处理记事本文件、自动化填写老旧桌面软件表单,或对封闭系统进行回归测试。传统方法要么依赖第三方接口(很多软件根本不提供),要么靠人工重复点击——效率低、易出错、难维护。

其实,Windows 早已内置了一套强大的"秘密武器":UI Automation。可以允许你的 C# 程序像真人一样识别窗口、定位按钮、输入文字、点击保存,真正实现"所见即所得"的自动化。

本文将通过一个完整的记事本自动化实战案例,带你从零掌握这项被低估却极其实用的技术。

正文

UI Automation 是微软官方提供的可访问性技术,最初为辅助功能设计,但因其通用性和稳定性,逐渐成为桌面自动化的首选方案。

它不依赖应用是否开放接口,只要界面元素能被 Windows 识别(几乎所有标准 Win32、WPF、UWP 应用都支持),就能被程序操控。

核心思路

将每个 UI 元素(如窗口、按钮、文本框)视为一个带有属性和模式的对象,通过条件筛选找到目标控件,再调用其支持的操作(如点击、输入、获取文本)。

整个过程无需图像识别,性能高、可靠性强。

以下是一个典型的项目配置,确保能调用 UI Automation COM 组件:

<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net8.0</TargetFramework> <UseWindowsForms>true</UseWindowsForms> </PropertyGroup> <ItemGroup> <COMReference Include="UIAutomationClient"> <WrapperTool>tlbimp</WrapperTool> <Guid>944de083-8fb8-45cf-bcb7-c477acb2f897</Guid> </COMReference> </ItemGroup> </Project>

关键在于封装一套健壮的查找与操作工具。例如ElementFinder类,提供了带超时重试的控件查找逻辑,避免因界面加载延迟导致失败:

public staticclassElementFinder { privatestaticreadonly IUIAutomation _automation = new CUIAutomation(); public static IUIAutomationElement GetDesktop() { return _automation.GetRootElement(); } publicstatic IUIAutomationElement? FindElementSafely( IUIAutomationElement parent, IUIAutomationCondition condition, TreeScope scope, int timeoutMs = 5000) { var endTime = DateTime.Now.AddMilliseconds(timeoutMs); while (DateTime.Now < endTime) { try { var element = parent.FindFirst(scope, condition); if (element != null) return element; } catch (COMException) { // UI可能正在变化,继续重试 } Thread.Sleep(100); } returnnull; } publicstatic IUIAutomationElement? FindFirstByControlType( IUIAutomationElement parent, int controlTypeId, int timeoutMs = 3000) { var condition = _automation.CreatePropertyCondition( UIA_PropertyIds.UIA_ControlTypePropertyId, controlTypeId); return FindElementSafely(parent, condition, TreeScope.TreeScope_Subtree, timeoutMs); } }

对于文本输入,尤其在 Windows 11 新版记事本中,传统SendKeys可能失效。因此采用更底层的keybd_eventAPI 模拟键盘事件,确保中英文、大小写、快捷键都能准确发送:

public staticclassKeyboardHelper { [DllImport("user32.dll")] private static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo); [DllImport("user32.dll")] private static extern short VkKeyScan(char ch); privateconstuint KEYEVENTF_KEYUP = 0x0002; privateconstbyte VK_CONTROL = 0x11; public static void SendText(string text) { foreach (char c in text) { if (c == '\r') continue; SendChar(c); } } public static void SendChar(char character) { short vkKey = VkKeyScan(character); byte virtualKey = (byte)(vkKey & 0xFF); bool needShift = (vkKey & 0x0100) != 0; if (needShift) keybd_event(0x10, 0, 0, UIntPtr.Zero); // Shift down keybd_event(virtualKey, 0, 0, UIntPtr.Zero); // Key down keybd_event(virtualKey, 0, KEYEVENTF_KEYUP, UIntPtr.Zero); // Key up if (needShift) keybd_event(0x10, 0, KEYEVENTF_KEYUP, UIntPtr.Zero); // Shift up Thread.Sleep(10); } public static void SendCtrlS() { keybd_event(VK_CONTROL, 0, 0, UIntPtr.Zero); SendChar('s'); keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, UIntPtr.Zero); } }

主业务逻辑NotepadAutomation整合了打开、输入、保存、关闭全流程,并针对不同版本记事本做了兼容处理——例如 Windows 11 使用RichEditD2DPT类名而非传统 Edit 控件:

public classNotepadAutomation { private Process? _notepadProcess; private IUIAutomationElement? _notepadWindow; public bool RunTest() { try { if (!OpenNotepad()) returnfalse; if (!InputRandomText()) returnfalse; if (!SaveFile()) returnfalse; if (!CloseNotepad()) returnfalse; Console.WriteLine("✅ 自动化任务完成!"); returntrue; } catch (Exception ex) { Console.WriteLine($"❌ 执行失败: {ex.Message}"); returnfalse; } finally { CleanUp(); } } private bool InputRandomText() { if (_notepadWindow == null) returnfalse; var editControl = ElementFinder.FindFirstByControlType( _notepadWindow, UIA_ControlTypeIds.UIA_EditControlTypeId, 2000); if (editControl == null) { editControl = ElementFinder.FindByClassName(_notepadWindow, "RichEditD2DPT", 3000); } if (editControl == null) { Console.WriteLine("⚠️ 未找到编辑控件,使用直接输入模式"); return InputTextDirectlyToWindow(); } editControl.SetFocus(); Thread.Sleep(500); var textLines = GenerateRandomTextLines(10); var fullText = string.Join(Environment.NewLine, textLines); return TryInputText(editControl, fullText); } private bool SaveFile() { _notepadWindow?.SetFocus(); KeyboardHelper.SendCtrlS(); Thread.Sleep(3000); var desktop = ElementFinder.GetDesktop(); var saveDialog = FindSaveDialog(desktop); if (saveDialog == null) { Console.WriteLine("❌ 未找到保存对话框"); returnfalse; } var fileName = $"AutoTest_{DateTime.Now:yyyyMMddHHmmss}.txt"; var fileNameEdit = FindFileNameEditBox(saveDialog); if (fileNameEdit != null && !IsSearchBox(fileNameEdit)) { fileNameEdit.SetFocus(); Thread.Sleep(300); KeyboardHelper.SendCtrlA(); KeyboardHelper.SendText(fileName); } var saveButton = ElementFinder.FindButton(saveDialog, "Save", 2000) ?? ElementFinder.FindByAutomationId(saveDialog, "1", 2000); if (saveButton != null) { ClickElement(saveButton); Thread.Sleep(2000); var desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); return File.Exists(Path.Combine(desktopPath, fileName)); } returnfalse; } private bool IsSearchBox(IUIAutomationElement element) { var name = element.CurrentName ?? ""; var automationId = element.CurrentAutomationId ?? ""; return name.Contains("Search") || automationId.Contains("Search"); } }

除了记事本,这套方案可广泛应用于:

  • 办公自动化:批量处理 Excel、Word 文档;

  • 回归测试:对无 API 的桌面软件进行功能验证;

  • 数据采集:从老旧工控软件中提取运行状态;

  • 智能运维:定时执行配置备份、日志导出等操作。

开发中需注意三大要点:

一是设置合理超时避免死等;

二是缓存控件引用减少重复查找;

三是采用多策略兼容不同应用版本。

异常处理也至关重要——当 InvokePattern 失败时,可降级为键盘模拟,确保流程不中断。

总结

UI Automation 是解决真实痛点的工程神器。它让 C# 开发摆脱对第三方接口的依赖,在封闭系统中也能实现高效自动化。

本文提供的代码模板经过实战打磨,具备良好的健壮性与扩展性,可直接用于工业软件、测试工具或个人效率脚本。掌握这项技能,意味着可以拥有了"操作任何 Windows 软件"的能力——这在自动化日益发展的时代,是一项不错的技术能力。

关键词

C#、UI Automation、#桌面自动化、#Windows、#记事本、#控件查找、#键盘模拟、#COM组件、#工业软件、#自动化测试

最后

如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎加入微信公众号社区,与其他热爱技术的同行一起交流心得,共同成长!

作者:技术老小子

出处:mp.weixin.qq.com/s/THTZjf_rzVLmsyBqHZ0AGA

声明:网络内容,仅供学习,尊重版权,侵权速删,歉意致谢!

- EOF -

技术群:添加小编微信dotnet999

公众号:dotnet讲堂

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/6 7:15:07

如何快速部署Argos Translate:离线翻译的终极完整指南

如何快速部署Argos Translate&#xff1a;离线翻译的终极完整指南 【免费下载链接】argos-translate Open-source offline translation library written in Python 项目地址: https://gitcode.com/GitHub_Trending/ar/argos-translate 在当今数字化时代&#xff0c;离线…

作者头像 李华
网站建设 2026/5/3 7:01:14

16、Unix系统负载监控:命令与脚本详解

Unix系统负载监控:命令与脚本详解 1. 引言 在Unix系统中,准确监控系统负载对于保障系统的稳定运行至关重要。不同的命令如 iostat 、 sar 、 vmstat 和 uptime 等,都能从不同角度提供系统负载的相关信息。本文将详细介绍这些命令的语法、输出特点以及如何通过脚本…

作者头像 李华
网站建设 2026/5/4 2:11:04

3分钟掌握ant-design-x-vue:构建智能对话界面的完整指南

3分钟掌握ant-design-x-vue&#xff1a;构建智能对话界面的完整指南 【免费下载链接】ant-design-x-vue Ant Design X For Vue.&#xff08;WIP&#xff09; 疯狂研发中&#x1f525; 项目地址: https://gitcode.com/gh_mirrors/an/ant-design-x-vue 还在为开发AI对话界…

作者头像 李华
网站建设 2026/5/5 10:40:24

⭐力扣刷题:最长递增子序列

题目&#xff1a; 给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元素而不改变其余元素的顺序。例如&#xff0c;[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列…

作者头像 李华
网站建设 2026/5/5 10:40:24

家政系统源码解析:一体化解决方案如何重塑同城保洁+维修服务生态!

在同城生活服务数字化转型的浪潮中&#xff0c;家政服务行业正面临 “服务分散、流程繁琐、管理低效” 的瓶颈。亿坊家政系统源码作为一体化解决方案的标杆&#xff0c;凭借对保洁、维修、预约上门等核心场景的深度适配&#xff0c;打通 “用户 - 服务人员 - 商户 - 平台” 全链…

作者头像 李华
网站建设 2026/5/5 17:59:32

新能源知识库(162)高镍三元锂电池介绍

一、定义 “高镍三元锂电池”仍属于镍钴锰&#xff08;NCM&#xff09;或镍钴铝&#xff08;NCA&#xff09;体系&#xff0c;只是将正极中镍的摩尔分数提高到 ≥60%&#xff0c;典型代表有 NCM-622、NCM-811、NCA-90 等&#xff1b;普通三元锂电池多指 NCM-523 及以下镍含量的…

作者头像 李华