窗口自动化不一定都要靠点击坐标。对于带传统菜单栏的程序,AHK v1 有一个很直接的命令:WinMenuSelectItem。它可以按菜单路径选择某个菜单项,比如“文件 → 保存”“选项 → 设置”。

这个命令不是万能的。它主要适合经典菜单栏,对 Ribbon、网页界面、自绘菜单、很多现代应用都不一定有效。但只要目标程序兼容,它比鼠标坐标稳定得多,也比一串 Send 方向键更清楚。

先用自己的 GUI 测试

为了避免不同系统语言影响示例,先用一个 AHK 自己创建的菜单来演示。

#Requires AutoHotkey v1.1

Menu, FileMenu, Add, 测试动作, DoAction
Menu, FileMenu, Add, 退出, GuiClose
Menu, MyMenuBar, Add, 文件, :FileMenu

Gui, Menu, MyMenuBar
Gui, Add, Text, w300 h80, 按 F1 会通过 WinMenuSelectItem 触发菜单。
Gui, Show,, 菜单测试窗口
return

F1::
WinMenuSelectItem, 菜单测试窗口,, 文件, 测试动作
if ErrorLevel
    MsgBox, 菜单选择失败
return

DoAction:
MsgBox, 菜单动作已经触发。
return

GuiClose:
ExitApp

这个例子里,热键没有点击窗口,也没有发送方向键,而是直接指定窗口标题、菜单名、菜单项名。只要菜单路径对得上,就能触发对应动作。

菜单文字要写准确

WinMenuSelectItem 最容易踩的坑,是菜单文字不一致。不同语言的软件,菜单名可能不同;有些菜单项带快捷键标记,比如 &File文件(&F);有些菜单是动态生成的,只有打开后才出现。

写脚本时,不要凭印象猜菜单名。先看目标程序的实际菜单文字,再一点点测试。建议先写最短路径,能触发后再加到正式脚本里。

SetTitleMatchMode, 2
WinMenuSelectItem, 记事本,, 格式, 自动换行
if ErrorLevel
    MsgBox, 没有找到这个菜单项,可能是菜单文字或程序版本不一致。

上面这个例子只表达写法,不保证所有 Windows 版本都能直接运行。现在的记事本版本变化比较多,菜单结构也可能不同。真实项目里,要以你当前电脑上的目标程序为准。

它适合哪些程序

我会优先在这些场景尝试:

  • 老式桌面软件,有标准菜单栏。
  • 自己写的 AHK GUI 菜单栏。
  • Notepad++、IrfanView 这类仍保留传统菜单的工具。
  • 需要切换设置,但不想依赖坐标点击的场景。

如果程序是 Office Ribbon、浏览器网页、Electron 应用、自绘界面,就不要先押宝这个命令。那类程序更适合用控件、快捷键、COM、浏览器接口或图像识别。

后台窗口也可以尝试

这个命令的一个优点是:目标窗口不一定非要在最前面。只要窗口存在,并且菜单结构可访问,它就有机会在后台触发菜单项。

SetTitleMatchMode, 2

if WinExist("IrfanView")
{
    WinMenuSelectItem, IrfanView,, Options, Capture
    if !ErrorLevel
    {
        WinWait, Capture Setup,, 3
        if !ErrorLevel
            WinActivate, Capture Setup
    }
}

这类写法很适合“打开某个设置窗口”。菜单动作触发后,再用 WinWait 等待弹出的设置窗口,比盲目 Sleep 稳一些。

和 Send 菜单快捷键相比

传统写法可能是先激活窗口,再发送 Alt 菜单快捷键:

WinActivate, 目标窗口
SendInput, !fo

这种写法短,但不够直观。几个月后再看 !fo,你可能忘记它到底按了哪几个菜单。WinMenuSelectItem 的优势是可读性强,脚本里直接写“哪个窗口、哪个菜单、哪个菜单项”。

失败时怎么排查

  • 先确认窗口标题能匹配,必要时用 SetTitleMatchMode, 2
  • 确认目标程序是传统菜单栏,不是 Ribbon 或自绘菜单。
  • 确认菜单文字和层级没有写错。
  • 如果菜单项会打开新窗口,后面加 WinWait
  • 如果命令失败,检查 ErrorLevel,不要假装成功。

窗口自动化最怕“看起来能跑,但换个窗口状态就失效”。能用菜单命令时,优先用菜单命令;不能用时,再考虑快捷键、控件、消息、图像等办法。思路清楚了,脚本才不会越补越乱。

声明:站内资源为整理优化好的代码上传分享与学习研究,如果是开源代码基本都会标明出处,方便大家扩展学习路径。请不要恶意搬运,破坏站长辛苦整理维护的劳动成果。本站为爱好者分享站点,所有内容不作为商业行为。如若本站上传内容侵犯了原著者的合法权益,请联系我们进行删除下架。