将击键发送到特定窗口(在后台),但与此同时执行其他操作 [英] Send keystrokes to a specific window (in background), but do something else in the meantime

查看:102
本文介绍了将击键发送到特定窗口(在后台),但与此同时执行其他操作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此代码(灵感来自在Python上模拟键盘和鼠标的最简单方法是哪种?)打开记事本并每秒发送一次键A,B,C,D,...,Z:

import win32com.client, time
shell = win32com.client.Dispatch("WScript.Shell")
shell.Run('Notepad')
time.sleep(1)
shell.AppActivate("Notepad")
for i in range(65,91):
    shell.SendKeys(chr(i))
    time.sleep(1)

我想让此操作在后台继续,在计算机上继续我的工作,并将击键发送到记事本(在后台).

问题:如果同时打开另一个应用程序(例如浏览器),则击键会发送到当前不希望使用的窗口中!

问题:即使此应用程序不在前台,如何让Python仅将键盘发送到notepad.exe ?

上下文:我正在自动化一些繁琐的任务,要求我的Python脚本在大约15分钟内将击键发送到app.exe(在后台),但与此同时,我想对计算机执行其他操作.

注意:更一般而言,我感兴趣的用例是进程app.exe可能会打开对话框,关闭对话框,打开其他窗口的情况,因此解决方案应该能够将击键发送给进程的活动窗口.因此,像此处这样具有固定hWnd的解决方案无法直接使用.

解决方案

为了将击键发送到任何应用程序窗口,而无需激活应用程序以获取输入焦点.我们必须先获取Windows处理程序.这需要Windows API FindWindowFindWindowsEx.首先,通过FindWindow获取应用程序顶级窗口的句柄.然后使用FindWindowsEx获取子窗口的句柄或控件以接收键. 因为应用程序的顶部窗口并不总是接受按键的窗口(例如notepad.exe,实际上接受按键的窗口是记事本主窗口下的Edit控件),所以可以通过ClassID或Caption找到它.

假定目标窗口的句柄已被获取(hwnd),则密钥消息将通过PostMessage发送到窗口.

对于普通的字符键,最简单的直接使用WM_CHAR消息的方法如下:

PostMessage(hwnd, WM_CHAR, 'a', 0);

对于非正常的字符键,例如功能键,方向键等,应按以下方式使用WM_KEYDOWNWM_KEYUP消息:

VirtualKey = MapVirtualKeyA(VK_RIGHT, 0);
PostMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 0x0001|VirtualKey>>16);
PostMessage(hwnd, WM_KEYUP, VK_RIGHT, 0x0001|VirtualKey>>16|0xC0>>24);

您可以参考

keybd_event(VK_SHIFT, 0, 0, 0);
PostMessage(hwnd, WM_KEYDOWN, 0x41, 0x001E0001);
PostMessage(hwnd, WM_KEYUP, 0x41, 0xC01E0001);
keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0);

对于按键"Alt",它使用WM_SYSKEYDOWN/WM_SYSKEYUP消息属于系统按钮.样本:

PostMessage(hwnd, WM_SYSKEYDOWN, VK_F4, 0x003E0001 |0x20000000);
PostMessage(hwnd, WM_SYSKEYUP, VK_F4, 0xC03E0001 | 0x20000000);

0x20000000表示上下文代码,如果按下"Alt"键,则值为1.

This code (inspired from Which is the easiest way to simulate keyboard and mouse on Python?) opens a Notepad and send the keys A, B, C, D, ..., Z every second:

import win32com.client, time
shell = win32com.client.Dispatch("WScript.Shell")
shell.Run('Notepad')
time.sleep(1)
shell.AppActivate("Notepad")
for i in range(65,91):
    shell.SendKeys(chr(i))
    time.sleep(1)

I would like to let this operation continue in background, continue my work on the computer, and have the keystrokes sent to Notepad (in background).

Problem: if I open another application (example: browser) in the meantime, the keystrokes are sent ... to the currently active window, which I dont't want!

Question: how to have Python send the keystrokes to notepad.exe only, even if this application is not in foreground?

Context: I'm automating some long task requiring that my Python script sends keystrokes to app.exe (in background) during maybe 15 minutes, but I'd like to do something else with the computer in the meantime.

Note: More generally, the use case I'm interested in is the case where the process app.exe might open dialogs, close dialogs, open other windows, so the solution should be able to send the keystrokes to the active window of the process. Thus a solution with a fixed hWnd like here doesn't work directly.

解决方案

In order to send Keystroke to any application window, without activating the application to get input focus. We must get the windows handler first. This requires Windows API FindWindow and FindWindowsEx. First, the handle of Top Level window of application is obtained by FindWindow. Then use FindWindowsEx to get the handle of the child window or control to receive keys. Because the top window of the application is not always the window that accepts Keystroke (such as notepad.exe, the window that actually accepts Keystroke is the Edit control under the main window of Notepad), it can be found by ClassID or Caption.

Assuming that the handle of the target window has been got(hwnd), the key message will be sent to the window with PostMessage.

For normal character keys, it is simplest to use WM_CHAR message directly, as follows:

PostMessage(hwnd, WM_CHAR, 'a', 0);

For un-normal character keys, such as function keys, direction keys, etc., WM_KEYDOWN and WM_KEYUP messages should be used as follows:

VirtualKey = MapVirtualKeyA(VK_RIGHT, 0);
PostMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 0x0001|VirtualKey>>16);
PostMessage(hwnd, WM_KEYUP, VK_RIGHT, 0x0001|VirtualKey>>16|0xC0>>24);

The details for last parameter (lParam) you can reference to msdn.

For the keys "Shift/Ctrl", sample:

keybd_event(VK_SHIFT, 0, 0, 0);
PostMessage(hwnd, WM_KEYDOWN, 0x41, 0x001E0001);
PostMessage(hwnd, WM_KEYUP, 0x41, 0xC01E0001);
keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0);

For the keys "Alt", It belongs to the system button, using WM_SYSKEYDOWN/WM_SYSKEYUP message. sample:

PostMessage(hwnd, WM_SYSKEYDOWN, VK_F4, 0x003E0001 |0x20000000);
PostMessage(hwnd, WM_SYSKEYUP, VK_F4, 0xC03E0001 | 0x20000000);

0x20000000 means context code, the value is 1 if the "Alt" key is down.

这篇关于将击键发送到特定窗口(在后台),但与此同时执行其他操作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆