win32gui中定义了一些有关图形操作的API,例如FindWindow、PostMessage等。win32gui中的API多达几百个,这里我们挑选一些比较重要且常用的来介绍。
通过类名和标题搜索窗体并返回句柄
FindWindow(lpClassName=None, lpWindowName=None)
描述:
自顶层窗口(也就是桌面)开始搜索条件匹配的窗体,并返回这个窗体的句柄。不搜索子窗口、不区分大小写。找不到就返回0
参数:
lpClassName:字符型,是窗体的类名,这个可以在Spy++里找到。
lpWindowName:字符型,是窗口名,也就是标题栏上你能看见的那个标题。
说明:
1、找到了主窗口以后就靠它来定位子窗体,该函数只能搜索顶层窗口。
2、若子窗口不以窗口句柄的形式创建(windowless),只是逻辑上的窗口,绘制在父窗口之上。这种情况是只能定位到主窗口的。比如微信。
3、若lpClassName=None或lpWindowName=None,则表示对这两个参数不做匹配要求,返回第一个匹配到的窗口。
窗口的类名和打开的程序相关,和文件无关。如下:
文件格式/程序 | 类名 |
---|---|
记事本 | Notepad |
360浏览器 | 360se6_Frame |
IE浏览器 | IEFrame |
Excel2016标准版 | XLMAIN |
UC | wxWindowNR |
谷歌浏览器 | Chrome_WidgetWin_1 |
pycharm | SunAwtFrame |
word2016标准版 | OpusApp |
微信 | WeChatMainWndForPC |
百度云盘 | DuiHostWnd |
Notepad++ | Notepad++ |
企业微信 | WeWorkWindow |
示例1:通过类和标题查找企业微信窗口句柄
import win32gui
from icecream import ic
handle = win32gui.FindWindow('WeWorkWindow', '企业微信')
ic(handle, hex(handle))
11:03:54|> handle: 68028, hex(handle): ‘0x109bc’
示例2:通过类查找谷歌浏览器窗口句柄
import win32gui
from icecream import ic
handle = win32gui.FindWindow('Chrome_WidgetWin_1', None)
ic(handle, hex(handle))
11:06:18|> handle: 66520, hex(handle): ‘0x103d8’
这里同时也输出了目标窗口句柄的16进制,方便和spy++软件查找的结果对照。
检索其类名和窗口名称与指定字符串匹配的窗口的句柄。该函数搜索子窗口,从给定的子窗口之后开始。
参数:
hwndParent:标识要搜索子窗口的父窗口。如果【hwndParent】为None,该函数将使用桌面窗口作为父窗口。该函数将在桌面的子窗口中进行搜索。
hwndChildAfter:标识子窗口。搜索从Z顺序中的下一个子窗口开始。【hwndChildAfter】必须是【hwndParent】的直接子窗口,而不仅仅是后代窗口。如果【hwndChildAfter】为None,则搜索从第一个子窗口【hwndParent】开始。请注意,如果【hwndParent】和【hwndChildAfter】均为None,则该函数将搜索所有顶级窗口。
lpszClasspl:要搜索的窗口类名。
lpszWindow:要搜索的窗口标题。
注意:
若lpszClasspl =None或lpszWindow =None,则表示对这两个参数不做匹配要求,返回第一个匹配到的窗口。
import win32gui
from icecream import ic
handle = win32gui.FindWindowEx(None, None, 'Notepad', None) # 在顶层窗口中搜索`记事本`窗口
ic(handle, hex(handle))
handle = win32gui.FindWindowEx(handle, None, 'Edit', None) # 在`记事本`窗口中搜索`Edit`窗口
ic(handle, hex(handle))
11:25:40|> handle: 659030, hex(handle): ‘0xa0e56’
11:25:40|> handle: 855762, hex(handle): ‘0xd0ed2’
获取子窗口的父窗口句柄
import win32gui
child_handle = 69136
parent_handle = win32gui.GetParent(child_handle)
print(parent_handle)
返回窗口的边界矩形的尺寸。尺寸以相对于屏幕左上角的屏幕坐标给出。
注意:窗口不能是最小化到任务栏,否则返回的尺寸会是负数。
import win32gui
from icecream import ic
handle = win32gui.FindWindowEx(None, None, 'Notepad', None) # 在顶层窗口中搜索`记事本`窗口
ic(handle, hex(handle))
left, top, right, bottom = win32gui.GetWindowRect(handle)
ic(left, top, right, bottom)
11:29:24|> handle: 659030, hex(handle): ‘0xa0e56’
11:29:24|> left: 843, top: 132, right: 1502, bottom: 698
获取窗口标题
import win32gui
from icecream import ic
handle = win32gui.FindWindowEx(None, None, 'Notepad', None)
ic(handle, hex(handle))
title = win32gui.GetWindowText(handle)
ic(title)
11:30:48|> handle: 659030, hex(handle): ‘0xa0e56’
11:30:48|> title: ‘Temp.txt - 记事本’
获取窗口的类
import win32gui
handle = 201008
class_name = win32gui.GetClassName(handle)
print(class_name)
判断某个整数是否是一个窗口的句柄。若是,则返回值不为0,否则返回0。
win32gui.IsWindow(hwnd)
确定是否为鼠标和键盘输入启用了指定的窗口。窗口可用。
返回值:如果启用该窗口,则返回值不为零。如果窗口未启用,返回值为零。
备注:子窗口只有在启用和可见时才接收输入。
win32gui.IsWindowEnabled(hwnd)
检索指定窗口的可见性状态(是否具有WS_VISIBLE样式)。
返回值:
如果指定的窗口及其父窗口具有WS_VISIBLE样式,则返回值不为零。
如果指定的窗口及其父窗口没有WS_VISIBLE样式,返回值为零。
因为返回值指定窗口是否具有WS_VISIBLE样式,即使窗口完全被其他窗口遮挡,它也可能非零。
备注
窗口的可见性状态由WS_VISIBLE样式位指示。设置WS_VISIBLE时,只要窗口具有WS_VISIBLE样式,将显示该窗口,并显示其后续绘图。
如果窗口被其他窗口遮挡或被其父窗口剪裁,任何绘制到具有WS_VISIBLE样式的窗口将不会显示。
win32gui.IsWindowVisible(hwnd)
通过将每个窗口的句柄依次传递给应用程序定义的回调函数来枚举屏幕上的所有顶级窗口
。EnumWindows继续,直到最后一个顶级窗口被枚举或回调函数返回FALSE。
示例1:遍历windows下所有句柄及窗口名称
from icecream import ic
import win32gui
hwnd_title = dict()
def get_all_hwnd(hwnd, mouse):
if win32gui.IsWindow(hwnd) and win32gui.IsWindowEnabled(hwnd) and win32gui.IsWindowVisible(hwnd):
hwnd_title.update({hwnd: win32gui.GetWindowText(hwnd)})
win32gui.EnumWindows(get_all_hwnd, 0)
for handle, title in hwnd_title.items():
if title:
ic(handle, title)
15:20:14|> handle: 986708, title: ‘t1.py – t5.py’
15:20:14|> handle: 921718, title: ‘Python - 进阶.docx - Word’
15:20:14|> handle: 659030, title: ‘Temp.txt - 记事本’
15:20:14|> handle: 1577106, title: ‘查找窗口’
15:20:14|> handle: 67000, title: ‘企业邮箱前台-邮箱 - 360安全浏览器 14.1’
15:20:14|> handle: 394748, title: ‘数据驱动.xlsm - Excel’
15:20:14|> handle: 68028, title: ‘企业微信’
15:20:14|> handle: 133664, title: ‘PythonFiles’
15:20:14|> handle: 983950, title: ‘spy - Everything’
15:20:14|> handle: 327756, title: ‘Microsoft Text Input Application’
15:20:14|> handle: 68710, title: ‘非常用工作文档.xlsm - Excel’
15:20:14|> handle: 66004, title: ‘Program Manager’
示例2:通过窗体标题模糊搜索并返回窗体句柄
from icecream import ic
import win32gui
hwnd_title = dict()
def get_all_hwnd(hwnd, mouse):
if win32gui.IsWindow(hwnd) and win32gui.IsWindowEnabled(hwnd) and win32gui.IsWindowVisible(hwnd):
hwnd_title.update({hwnd: win32gui.GetWindowText(hwnd)})
win32gui.EnumWindows(get_all_hwnd, 0)
def find_window(part_title):
for hwnd, title in hwnd_title.items():
if part_title in title:
return hwnd
handle = find_window('企业微信')
ic(handle)
15:24:17|> handle: 68028
通过将每个子窗口的句柄传递给应用程序定义的回调函数来枚举属于指定父窗口的子窗口,直到最后一个子窗口被枚举或回调函数返回FALSE。
示例1:获取主窗口句柄下的所有子孙窗口句柄
from icecream import ic
import win32gui
parent_handle = int('000A0E56', 16)
def get_children_window(parent):
hwndChildList = []
win32gui.EnumChildWindows(parent, lambda hwnd, param: param.append(hwnd), hwndChildList)
return hwndChildList
child_list = get_children_window(parent_handle)
ic(child_list)
15:27:14|> child_list: [855762, 201106]
激活显示窗口,根据第二个参数的不同改变窗口的不同状态。
import win32gui
import win32con
hwnd = 3806050
win32gui.ShowWindow(hwnd, win32con.SW_SHOWNORMAL)
其中ShowWindow 函数显示方式(第二个参数)定义如下
参数(win32con) | 意义 | 备注 |
---|---|---|
SW_HIDE | 隐藏窗口,大小不变,激活状态不变 | 不光是窗口最小化,连任务栏里都不会存在 |
SW_MAXIMIZE | 最大化窗口,显示状态不变,激活状态不变 | |
SW_MINIMIZE | 最小化窗口,显示状态不变,激活状态不变 | 最小化窗口 |
SW_RESTORE | 从最大化或最小化恢复正常大小,显示状态不变,激活状态不变 | 窗口恢复正常 |
SW_SHOW | 显示并激活窗口,大小状态不变 | sw_hide的反向操作。不过只是会在任务栏中显示,桌面上看不到窗口 |
SW_SHOWMAXIMIZED | 显示并激活窗口,以最大化显示 | |
SW_SHOWMINIMIZED | 显示并激活窗口,以最小化显示 | |
SW_SHOWMINNOACTIVE | 显示窗口并最小化,激活状态不变 | |
SW_SHOWNA | 显示窗口,大小状态不变,激活状态不变 | 和sw_show一样,没看出什么区别 |
SW_SHOWNOACTIVATE | 显示并从最大化或最小化恢复正常大小,激活状态不变 | 显示窗口到正常大小,但不会前置 |
SW_SHOWNORMAL | 显示并激活窗口,恢复正常大小(初始化时用这个参数) | 显示窗口到正常大小,但不会前置 |
前置窗口
win32gui.SetForegroundWindow(hwnd)
函数功能:
该函数将一个消息放入(寄送)到与指定窗口创建的线程相联系消息队列里,Postmessage不等待线程处理消息就返回。而SendMessage则会一直等待处理完并返回处理结果。消息队列里的消息通过调用GetMessage
和PeekMessage
取得。
函数原型:
B00L PostMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam);
LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM IParam);
参数:
hWnd:其窗口程序接收消息的窗口的句柄。
Msg:指定被寄送的消息。
wParam:指定附加的消息特定的信息。
IParam:指定附加的消息特定的信息。
返回值:如果函数调用成功,返回非零值:如果函数调用失败,返回值是零。若想获得更多的错误信息,请调用GetLastError
函数。
import win32gui
hwnd = int('000E0EE2', 16)
win32gui.SendMessage(0x00041192, win32con.WM_SETTEXT, None, 'hello world') # 将hello world写入到记事本中
win32api.PostMessage(hwnd, win32con.WM_KEYDOWN, win32con.VK_F5, 0) # 发送F5键到记事本中,将会写入当前时间
win32api.PostMessage(hwnd, win32con.WM_KEYUP, win32con.VK_F5, 0)
win32gui.PostMessage(hwnd, win32con.WM_CLOSE, 0, 0) # 关闭窗口,将会询问是否要保存