# PostMessage / SendMessage 教程 作者: Rajat

本页解释如何通过 PostMessage 和 SendMessage 向窗口或窗口控件发送消息, 并将回答如下问题:

- "如何按下已最小化窗口中的按钮?""
- "当 WinMenuSelectItem 不起作用时, 我如何选择一个菜单项?!"
- "这是个可更换皮肤的窗口....
- "如何确保我每次发送的命令都有效?"
- "如果是 隐藏 窗口呢?!"

需要: [AutoHotkey v1.0.09+] 和 Winspector Spy(点击打开下载页面)

像第一个例子那样, 请注意 WinMenuSelectItem 对 Outlook Express 的 "New Message" 窗口中的菜单栏无效. 换句话说, 这代码不起作用:

    WinMenuSelectItem, New Message,, &Insert, &Picture...

但 PostMessage 能实现这个操作:

    PostMessage, 0x0111, 40239, 0, , New Message

太神奇了!但那个是什么? 0x0111 是 wm_command 消息的十六进制代码, 而 40239 是这个特殊窗口理解为选择 'Insert Picture' 菜单项的代码. 现在让我告诉您如何找到类似 40239 这样的值:

1. 启动 Winspector Spy, 打开 "New Message" 窗口.
2. 拖动 Winspector Spy 窗口中的十字线到 "New Message" 窗口的标题栏(没有被 Winspector Spy 覆盖的部分).
3. 在左边的列表中右键点击所选窗口并选择 'Messages'.
4. 右键点击空白窗口并选择 'Edit message filter'.
5. 点击 'filter all' 按钮, 然后双击左边列表中的 'wm_command'. 这样您将只监视此消息.
6. 现在转到 "New Message" 窗口并在菜单栏中选择: Insert > Picture.
7. 返回 Winspector Spy 并按下信号灯按钮来停止监视.
8. 展开收集到的 wm_command 消息(忽略其他的).
9. 您想要找的(通常) 是代码为 0 的消息. 有时会有一些描述为 'win activated' 或 'win destroyed' 以及其他..的 wm_command 消息都是不需要的内容. 您会发现描述为 'Control ID: 40239' 的消息 ...就是它了!
10. 现在把它放入上面的命令中您就完成了! 它就是 wParam 值.

在下一个例子中我会使用画图程序, 因为很可能每个人都会有. 现在假设我们要使用 AutoHotkey 从工具栏中选择一个工具; 假设要选择取色工具.

您会怎么做? 很可能是使用鼠标点击工具栏, 对吗? 但是按钮可能被移动且隐藏了!这个画图程序的工具栏也可能被移动/隐藏. 所以如果目标用户这么做了, 那么您的脚本在这个点上会失效. 但是下面的命令仍然有效:

    PostMessage, 0x0111, 639,,, Untitled - Paint

PostMessage 的另一个好处是窗口可以在后台; 与之相比, 发送鼠标点击要求目标窗口必须是活动的.

这里有其他的例子. 请注意: 我使用的是 WinXP Pro(SP1) ... 如果您使用不同的操作系统, 那么您的参数可能要改变(仅适用于像写字板和记事本这类 windows 自带的应用程序; 其他的程序应该不用改变):

    ; 设置写字板字体为青色
    PostMessage, 0x0111, 32788, 0, , Document - WordPad

    ; 打开记事本的关于对话框
    PostMessage, 0x0111, 65, 0, , Untitled - Notepad

    ; 切换记事本的自动换行属性
    PostMessage, 0x0111, 32, 0, , Untitled - Notepad

    ; 在 Windows Media Player 中播放/暂停
    PostMessage, 0x0111, 32808, 0, , Windows Media Player

    ; 挂起正在运行的 AHK 脚本的热键
    DetectHiddenWindows, On
    SetTitleMatchMode, 2
    ; 使用 65306 来 Pause, 而 65303 来 Reload, 从而代替下面的 Suspend. (请参阅 FAQ)
    PostMessage, 0x0111, 65305,,, MyScript.ahk - AutoHotkey

    ; 按下 CapsLock 和 Numpad2 重新加载所有 AutoHotkey 脚本
    CapsLock & Numpad2::
    ReloadAllAhkScripts() {
        DetectHiddenWindows, On
        SetTitleMatchMode, 2
        WinGet, allAhkExe, List, ahk_class AutoHotkey
        Loop, % allAhkExe {
            hwnd := allAhkExe%A_Index%
            if (hwnd = A_ScriptHwnd)  ; 重新加载时忽略当前窗口
            {
                continue
            }
            PostMessage, 0x0111, 65303,,, % "ahk_id" . hwnd
        }
        Reload
    }

上面讲的是 PostMessage. SendMessage 的工作方式相同, 不过还会等待返回值, 这个值可用于一些情况中, 例如获取 Winamp 中当前正在播放的曲目(请参阅自动化 Winamp 的例子).

这里是一些注意事项:

- 上文也提到了操作系统是 XP 且需要注意消息值随着不同的操作系统而改变. 如果您发现一个消息在您的系统上有效(对于一个软件的某个版本), 那么在其他系统上它对于这个软件的相同版本仍然是有效的. 此外, 大多数应用程序甚至会在它们的不同版本中使用相同的消息值(例如 Windows Media Player 和 Winamp).
- 如果您已经在 Winspector Spy 中设置了过滤器来只显示 wm_command 消息, 却仍然得到大量的消息时, 请右键点击那些不需要的消息并选择隐藏(消息名)... 这些消息一般是不是由于您和目标软件交互所产生的消息.
- 在 Winspector Spy 中向右的箭头显示消息值, 而模糊的左箭头显示返回值. 返回值 0(零) 默认情况下可以安全地认为 '没有错误'(与 SendMessage 一起使用, 返回值会存储在 %ErrorLevel%).
- 要传递消息到匹配部分标题的窗口, 请添加此行到脚本: `SetTitleMatchMode, 2`
- 要传递消息到隐藏窗口, 请添加此行到脚本: `DetectHiddenWindows, On`

注: 此技术对某些应用程序无效. 我对 VB 和 Delphi 编写的应用程序只能侥幸. 此技术最适用于 C, C++ 编写的应用程序. 对于 VB 应用程序相同命令的 'LParam' 参数在传递时总是变化的. 对于 Delphi 应用程序... 一些程序的 GUI 甚至不使用 wm_command. 它或许使用鼠标位置和点击吧.

去探索吧.... 并且记得在 AutoHotkey 论坛分享您的经验. 欢迎反馈!

这个指南并不是为新手们准备的(没有冒犯的意思), 因为这些命令被认为是高级功能. 所以如果读完本文, 您还是摸不着头脑, 请忘了它吧.

Rajat

dftg