键盘事件未使用 pywin32 发送到窗口

het*_*jav 5 python windows pywin32 python-2.7

我写了一个代码,可以从我想要的任何程序中获取 HWND。如果你问的话,这就是我得到 hwnd 的方式。

以下代码应调出设备管理器并将向下箭头发送到程序。

但它确实。它确实会调出设备管理器,但它不会向程序发送向下箭头键,至少没有任何反应。

如果我使用记事本窗口的 hwnd 代码更改 hwndMain 编号,该代码会起作用并发送向下箭头键

import win32api
import win32con
import win32gui
import time

hwndMain = 133082
hwndChild = win32gui.GetWindow(hwndMain, win32con.GW_CHILD)
win32gui.SetForegroundWindow(hwndMain)
time.sleep(1)

win32api.SendMessage(hwndChild, win32con.WM_CHAR, 0x28, 0)
Run Code Online (Sandbox Code Playgroud)

编辑

我试过了

win32api.SendMessage(hwndChild, win32con.WM_CHAR, win32con.WM_KEYDOWN, 0)
Run Code Online (Sandbox Code Playgroud)

代替

win32api.SendMessage(hwndChild, win32con.WM_CHAR, 0x28, 0)
Run Code Online (Sandbox Code Playgroud)

但这也行不通。

我在 python 2.7

Cri*_*ati 5

每个Win窗口可以有0 个或多个子窗口,每个子窗口也可以有 0 个或多个自己的子窗口,依此类推……所以每个窗口可能有一个完整的子窗口。

有更多关于窗户的东西,而不是眼睛。用户可能看着一个(顶部)窗口并想象它的树以某种方式看起来,而实际上这棵树看起来可能完全不同(更复杂),因为可能有一些窗口不可见。

当将消息发送到窗口并期望发生某种行为时,必须将消息发送到确切的窗口(或其设计为转发它的祖先之一),否则该消息将被简单地忽略(因为错误的窗口不处理这种消息)。
在我们的例子中,这意味着WM_KEYDOWN(或WM_CHAR)消息应该发送到:

  • 保存记事本文的(编辑)窗口
  • 保存设备管理器设备列表的 ( TreeView ) 窗口

您正在使用[ActiveState.Docs]: win32gui.GetWindow,它包装了[MS.Docs]: GetWindow 函数,该函数声明(对于GW_CHILD):

检索到的句柄标识Z顺序顶部的子窗口,如果指定的窗口是父窗口;否则,检索到的句柄为NULL。该函数仅检查指定窗口的子窗口。它不检查后代窗口。

巧合的是,对于记事本将消息发送给它的第一个孩子作品,因为那个孩子变成了我上面提到的编辑窗口(除了那个孩子,记事本只有另一个是StatusBar,就是这样,这些都不是windows 有任何自己的孩子)。

另一方面,对于设备管理器,事情就没有那么简单了。如您所见,它的结构更为复杂(例如,ToolBar窗口是可见的)。按照建议,为了使用 Windows,我正在使用[MS.Docs]: EnumChildWindows function

代码.py

#!/usr/bin/env python3

import sys
import pywintypes
import win32gui
import win32con


def enum_child_proc(wnd, param):
    print("    Handling child 0x{:08X} - [{:}] - 0x{:08X}".format(wnd, win32gui.GetWindowText(wnd), win32gui.GetParent(wnd)))
    if param[0] >= 0:
        if param[1] == param[0]:
            win32gui.SendMessage(wnd, win32con.WM_KEYDOWN, win32con.VK_DOWN, 0)
            return 0
        param[1] += 1


def handle_window(wnd, child_index=-1):
    print("Handling 0x{:08X} - [{:}]".format(wnd, win32gui.GetWindowText(wnd)))
    cur_child = 0
    param = [child_index, cur_child]
    try:
        win32gui.EnumChildWindows(wnd, enum_child_proc, param)
    except pywintypes.error as e:
        if child_index < 0 or e.args[0]:
            raise e


def main():
    np_wnd = 0x01DB1EE2  # Notepad handle
    dm_wnd = 0x000E2042  # Device Manager handle

    handle_window(np_wnd, child_index=0)
    handle_window(dm_wnd, child_index=6)


if __name__ == "__main__":
    print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
    main()
Run Code Online (Sandbox Code Playgroud)

注意事项

  • 我硬编码了 2 个窗口句柄(np_wnddm_wnd)。显然,它们将无效(因为我关闭了窗户,它们在我的机器上也不再有效),并且它们的值需要更改
  • 为了找到窗口的句柄(及其一些子窗口),我使用了Spy++[MS.Docs]: How to: Start Spy++),它是VStudio 的一部分,但我确信还有很多其他类似的应用

输出

#!/usr/bin/env python3

import sys
import pywintypes
import win32gui
import win32con


def enum_child_proc(wnd, param):
    print("    Handling child 0x{:08X} - [{:}] - 0x{:08X}".format(wnd, win32gui.GetWindowText(wnd), win32gui.GetParent(wnd)))
    if param[0] >= 0:
        if param[1] == param[0]:
            win32gui.SendMessage(wnd, win32con.WM_KEYDOWN, win32con.VK_DOWN, 0)
            return 0
        param[1] += 1


def handle_window(wnd, child_index=-1):
    print("Handling 0x{:08X} - [{:}]".format(wnd, win32gui.GetWindowText(wnd)))
    cur_child = 0
    param = [child_index, cur_child]
    try:
        win32gui.EnumChildWindows(wnd, enum_child_proc, param)
    except pywintypes.error as e:
        if child_index < 0 or e.args[0]:
            raise e


def main():
    np_wnd = 0x01DB1EE2  # Notepad handle
    dm_wnd = 0x000E2042  # Device Manager handle

    handle_window(np_wnd, child_index=0)
    handle_window(dm_wnd, child_index=6)


if __name__ == "__main__":
    print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
    main()
Run Code Online (Sandbox Code Playgroud)

如从输出看出,树视图窗口是7孩子(7的的孩子:))设备管理器窗口,意味着有6中介(和不可见)之间的窗口(其忽略消息)。

尽管代码对相关窗口起到了作用,但目前没有适用于任何窗口的方法(或者如果有,我不知道)。我必须提到,我已经尝试通过查看树来确定感兴趣的子窗口:

  • 姓名
  • 班级
  • 风格(MS doc 在这方面很差)
    • 扩展样式
  • 位置(相对于其父级)
  • SendMessage的返回码

但我找不到任何可以将它与其他窗户区分开来的东西。我注意到的唯一的事情是,记事本,所需的窗口是1孩子列举,而设备管理器它是7,所以我没滤波基于这个事实(在CHILD_INDEX),但我认为这是完全不可靠的.

作为替代方案,可以根本不进行过滤,并将消息发送到树中的所有子窗口,但这可能会产生不需要的影响,因为可能有其他窗口响应该消息。例如,设备管理器树由大约 30个子窗口组成。

最后,我还想提一下,有些窗口(像Chrome这样的网络浏览)有自己的 Windows 系统,所以这些都行不通。