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

标签 python windows python-2.7 pywin32

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

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

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

如果我使用记事本窗口的 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)

编辑

我试过了
win32api.SendMessage(hwndChild, win32con.WM_CHAR, win32con.WM_KEYDOWN, 0)

代替
win32api.SendMessage(hwndChild, win32con.WM_CHAR, 0x28, 0)

但这也行不通。

我在 python 2.7

最佳答案

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

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

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

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

  • 您正在使用 [ActiveState.Docs]: win32gui.GetWindow , 包装 [MS.Docs]: GetWindow function其中指出(对于 GW_CHILD ):

    The retrieved handle identifies the child window at the top of the Z order, if the specified window is a parent window; otherwise, the retrieved handle is NULL. The function examines only child windows of the specified window. It does not examine descendant windows.



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

    另一方面,对于设备管理器,事情就没有那么简单了。如您所见,它的结构更加复杂(例如 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()
    

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

  • 输出 :

    e:\Work\Dev\StackOverflow\q053778227>"e:\Work\Dev\VEnvs\py_064_03.06.08_test0\Scripts\python.exe" code.py
    Python 3.6.8 (tags/v3.6.8:3c6b436a57, Dec 24 2018, 00:16:47) [MSC v.1916 64 bit (AMD64)] on win32
    
    Handling 0x01DB1EE2 - [Untitled - Notepad]
        Handling child 0x01811FA4 - [] - 0x01DB1EE2
    Handling 0x000E2042 - [Device Manager]
        Handling child 0x00621A5A - [] - 0x000E2042
        Handling child 0x01991F44 - [Device Manager] - 0x00621A5A
        Handling child 0x01691F3E - [] - 0x01991F44
        Handling child 0x000C20B0 - [] - 0x01691F3E
        Handling child 0x004D2000 - [] - 0x000C20B0
        Handling child 0x004420CA - [] - 0x004D2000
        Handling child 0x01191F20 - [] - 0x004420CA
    


    从输出中可以看出,TreeView 窗口是 第七个 child (第 7 个 child 的 :) )设备管理器窗口意味着 它们之间有 6 个中间(和不可见)窗口 (忽略该消息)。

    尽管代码对相关窗口起到了作用,当前没有适用于任何窗口的配方 (或者如果有,我不知道)。我必须提到,我已经尝试通过查看树来确定感兴趣的子窗口:
  • 姓名
  • 类(class)
  • 风格(MS doc 在这方面很差)
  • 加长款
  • 位置(相对于其父级)
  • SendMessage 的返回码

  • 但我找不到任何可以将它与其他 window 区分开来的东西。我唯一注意到的是,对于记事本,所需的窗口是枚举的第一个子窗口,而对于设备管理器,它是第 7 个子窗口,所以我根据这个事实 (child_index) 进行了过滤,但我认为它是 完全不可靠 .

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

    最后,我还想提一下,有些窗口(Chrome 之类的网络浏览器)有自己的 Windows 系统,因此这些都不起作用。

    关于python - 键盘事件未使用 pywin32 发送到窗口,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53778227/

    相关文章:

    python - 无法在 Windows 中构建 tensorflow C++ API

    python - Azure 访问 token 请求返回 HTML

    python - 在循环中追加 Pandas DataFrame

    python - 在 BranchPython Operator 之后跳过 Airflow 2.0 任务

    c++ - 如何在 C++ 中将字节数组发送到另一个进程

    python - 我怎样才能更有效地编写这个解决方案?

    Python:知道函数是从单元测试调用的吗?

    python - 获取属性python

    python - 如何连接到 TT X_TRADER API 以便使用 python 创建自动交易系统?

    windows - Windows 上的进程何时可以更改其 PID?