Python 第三方模块之 win32gui

  • Win32gui: Windows图形界面接口模块。主要负责操作窗口切换以及窗口中元素id标签的获取
  • Win32api: Windows开发接口模块。主要负责模拟键盘和鼠标操作,对win32gui获取的标签进行点击/获取值/修改值等操作
  • Win32con:全面的库函数,提供Win32gui和Win32api需要的操作参数

Win32 GUI模块介绍

Win32 GUI模块实际上是Python对Win32 API的一层封装,使得Python开发者可以更加方便地使用Win32 API来创建Windows GUI应用程序。它提供了一些Python类和函数来表示窗口、控件、消息等概念,并且可以处理Windows消息来响应Windows事件。

Win32 GUI模块是一个底层的模块,需要对Windows消息有一定的了解,并且需要熟悉一些基本的Windows API函数。但是,使用Win32 GUI模块可以更加灵活地控制窗口和控件,也可以在Windows环境下实现一些高级的自动化任务。

该模块的功能包括:

  1. 创建和注册窗口、消息循环
  2. 获取窗口的句柄、样式、扩展样式、标题、尺寸、位置等信息
  3. 枚举窗口、子窗口和控件
  4. 发送和接收Windows消息
  5. 显示和隐藏、关闭窗口
  6. 设置窗口属性、风格
  7. 获取和设置控件的属性、文本

安装

1
pip install pywin32

python win32gui 自动化操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
import os
import win32gui
import win32con
import win32api
import win32lib
import ctypes


# FindWindow(ClassName=ClassName, Title=None) 从顶层窗口向下搜索主窗口,无法搜索子窗口
# ClassName:窗口的类名
# Title:窗口的标题名称,即左上角的文字描述信息
handle = win32gui.FindWindow("Notepad", None)
print(handle) # 打印句柄,十进制
print("%x" %(handle) ) # 打印句柄,十六进制


# GetWindowRect(handle) 获取窗口位置,left,top分别指与屏幕左上角距离,right,bottom 指长和高。
left, top, right, bottom = win32gui.GetWindowRect(handle)

# SetWindowPos(HWN hWnd,HWND hWndlnsertAfter, int X, int Y, int cx, int cy, UNIT.Flags) 改变一个子窗口,弹出式窗口式顶层窗口的尺寸,位置和Z序。子窗口,弹出式窗口,及顶层窗口根据它们在屏幕上出现的顺序排序、顶层窗口设置的级别最高,并且被设置为Z序的第一个窗口。
# hWnd:窗口句柄。
# hWndlnsertAfter:在z序中的位于被置位的窗口前的窗口句柄。该参数必须为一个窗口句柄,或下列值之一:
# HWND_BOTTOM:将窗口置于Z序的底部。如果参数hWnd标识了一个顶层窗口,则窗口失去顶级位置,并且被置在其他窗口的底部。
# HWND_DOTTOPMOST:将窗口置于所有非顶层窗口之上(即在所有顶层窗口之后)。如果窗口已经是非顶层窗口则该标志不起作用。
# HWND_TOP:将窗口置于Z序的顶部。
# HWND_TOPMOST:将窗口置于所有非顶层窗口之上。即使窗口未被激活窗口也将保持顶级位置。
# x:以客户坐标指定窗口新位置的左边界。
# Y:以客户坐标指定窗口新位置的顶边界。
# cx:以像素指定窗口的新的宽度。
# cy:以像素指定窗口的新的高度。
# uFlags:窗口尺寸和定位的标志。该参数可以是下列值的组合,参考:https://dandelioncloud.cn/article/details/1513086501389942785/
# 返回值:如果函数成功,返回值为非零;如果函数失败,返回值为零。若想获得更多错误消息,请调用GetLastError函数。
win32gui.SetWindowPos(handle, "hWndInsertAfter", left, top, right, bottom, "uFlags")


# 把窗口放到最顶层
win32gui.BringWindowToTop(handle)


# 激活该窗口,设置该窗口为当前活动窗口,也就是前台窗口,此时窗口会是最前面一层,在找到窗口句柄后,需要先将窗口设置为最前面一层才能模拟鼠标键盘操作当前窗口上的元素。
win32gui.SetForegroundWindow(handle)


# 获取某个句柄的类名,标题
title = win32gui.GetWindowText(handle)
clsname = win32gui.GetClassName(handle)


# 枚举所有所有子窗口句柄
hwndChildList = []
win32gui.EnumChildWindows(handle, lambda hwnd, param: param.append(hwnd), hwndChildList)


# FindWindowEx(hwndParent=0, hwndChildAfter=0, lpszClass=None, lpszWindow=None)
# 搜索类名和窗体名匹配的窗体,并返回这个窗体的句柄。不区分大小写,找不到就返回0。
# hwndParent:目标窗口的父窗口,也是上面获取到的窗口句柄信息。通过父向下找子。若不为0,则搜索句柄为hwndParent窗体的子窗体。
# hwndChildAfter:目标窗口的子窗口。通过子向上找父,从而找到目标窗口。若不为0,则按照z-index的顺序从hwndChildAfter向后开始搜索子窗体,否则从第一个子窗体开始搜索。
# lpszClass:目标窗口的类名,这个可以在Spy++里找到。
# lpszWindow:字符型,是窗口名,也就是标题栏上你能看见的那个标题。
subHandle = win32gui.FindWindowEx(handle, 0, "EDIT", None)


# 获得窗口的菜单句柄
menuHandle = win32gui.GetMenu(subHandle)
# 获得子菜单或下拉菜单句柄
# 参数:菜单句柄 子菜单索引号
subMenuHandle = win32gui.GetSubMenu(menuHandle, 0)
# 获得菜单项中的的标志符,注意,分隔符是被编入索引的
# 参数:子菜单句柄 项目索引号
menuItemHandle = win32gui.GetMenuItemID(subMenuHandle, 0)


# win32gui.postMessage(hWnd, Msg, wParam, lParam)发送消息,加入消息队列,无返回
# 参数:句柄 消息类型 WParam IParam
# 参数和使用方法同SendMessage。不同的是,PostMessage只将消息放入待执行消息队列,不等待处理和返回,只要放入队列即算执行完毕。而SendMessage需要等待执行处理完后,才继续,返回的是其他程序处理后的返回值。
win32gui.postMessage(subHandle, win32con.WM_COMMAND, menuItemHandle, 0)

# 示例:关闭窗口
win32gui.PostMessage(win32lib.findWindow(classname, titlename), win32con.WM_CLOSE, 0, 0)


# win32gui.SendMessage(hWnd, Msg, wParam, lParam)。向指定的Windows消息发送到窗口。
# hWnd:接收消息的窗体句柄
# Msg:要发送的消息,这些消息都是windows预先定义好的,可以参见系统定义消息(System-Defined Messages)。
# 系统定义消息中不同消息分别有相应的参数:wParam和lParam,可查询官网参数详情:以 WM_KEYDOWN消息 为例:wParam 虚拟键参数,lParam 重复次数
# wParam:消息的wParam参数。wParam 的定义是32位整型,high word就是他的31至16位,low word是它的15至0位。当参数超过两个,wParam和lParam不够用时,可以将wParam就给拆成两个int16来使用。这种时候在python里记得用把HIWORD的常数向左移16位,再加LOWORD,即wParam = HIWORD<<16+LOWORD。
# lParam:消息的lParam参数

# 示例:Enter键 重复0次
win32gui.SendMessage(handle, win32con.WM_KEYDOWN, win32con.VK_RETURN, 0)

# 示例:设置文本框内容,等窗口处理完毕后返回true。中文需编码成gbk
# 参数:句柄;消息类型;参数WParam,无需使用; 参数IParam,要设置的内容,字符串
win32api.SendMessage(handle, win32con.WM_SETTEXT, 0, os.path.abspath("abc.txt").encode('gbk'))

# 示例:控件点击确定,处理消息后返回0
# 参数:窗口句柄; 消息类型; 参数WParam HIWORD为0(未使用),LOWORD为控件的ID; 参数IParam 0(未使用),确定控件的句柄
win32api.SendMessage(handle, win32con.WM_COMMAND, 1, "confirmBTN_handle")

# 示例:获取窗口文本不含截尾空字符的长度
# 参数:窗口句柄; 消息类型; 参数WParam; 参数IParam
bufSize = win32api.SendMessage(subHandle, win32con.WM_GETTEXTLENGTH, 0, 0) +1

# 利用api生成Buffer
strBuf = win32gui.PyMakeBuffer(bufSize)
print(strBuf)

# 示例:发送消息获取文本内容
# 参数:窗口句柄; 消息类型;文本大小; 存储位置
length = win32gui.SendMessage(subHandle, win32con.WM_GETTEXT, bufSize, strBuf)
# 反向内容,转为字符串
# text = str(strBuf[:-1])


address, length = win32gui.PyGetBufferAddressAndLen(strBuf)
text = win32gui.PyGetString(address, length)
# print('text: ', text)

# 鼠标单击事件, 鼠标定位到(30,50)
win32api.SetCursorPos([30,150])
#执行左单键击,若需要双击则延时几毫秒再点击一次即可
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP | win32con.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)
#右键单击
win32api.mouse_event(win32con.MOUSEEVENTF_RIGHTUP | win32con.MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, 0)

def click1(x,y): #第一种
win32api.SetCursorPos((x,y))
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN,x,y,0,0)
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP,x,y,0,0)

def click2(x,y): #第二种
ctypes.windll.user32.SetCursorPos(x,y)
ctypes.windll.user32.mouse_event(2,0,0,0,0)
ctypes.windll.user32.mouse_event(4,0,0,0,0)

def click_it(pos): #第三种
handle= win32gui.WindowFromPoint(pos)
client_pos =win32gui.ScreenToClient(handle,pos)
tmp=win32api.MAKELONG(client_pos[0],client_pos[1])
win32gui.SendMessage(handle, win32con.WM_ACTIVATE,win32con.WA_ACTIVE,0)
win32gui.SendMessage(handle, win32con.WM_LBUTTONDOWN,win32con.MK_LBUTTON,tmp)
win32gui.SendMessage(handle, win32con.WM_LBUTTONUP,win32con.MK_LBUTTON,tmp)


def doClick(cx,cy,hwnd):
# hwnd为需要点击的窗口控件句柄,cx、cy为点击位置在该窗口的相对坐标
long_position = win32api.MAKELONG(cx, cy)#模拟鼠标指针 传送到指定坐标
win32api.SendMessage(hwnd, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, long_position)#模拟鼠标按下
win32api.SendMessage(hwnd, win32con.WM_LBUTTONUP, win32con.MK_LBUTTON, long_position)#模拟鼠标弹起


# 发送回车
win32api.keybd_event(13,0,0,0)
win32api.keybd_event(13,0,win32con.KEYEVENTF_KEYUP,0)


win32gui.IsIconic(handle)


# 改变窗口状态
win32gui.ShowWindow(handle, win32con.SW_MAXIMIZE)
# SW_HIDE:隐藏窗口并激活其他窗口。nCmdShow=0。
# SW_MAXIMIZE:最大化指定的窗口。nCmdShow=3。
# SW_MINIMIZE:最小化指定的窗口并且激活在Z序中的下一个顶层窗口。nCmdShow=6。
# SW_RESTORE:激活并显示窗口。如果窗口最小化或最大化,则系统将窗口恢复到原来的尺寸和位置。在恢复最小化窗口时,应用程序应该指定这个标志。nCmdShow=9。
# SW_SHOW:在窗口原来的位置以原来的尺寸激活和显示窗口。nCmdShow=5。
# SW_SHOWDEFAULT:依据在STARTUPINFO结构中指定的SW_FLAG标志设定显示状态,STARTUPINFO 结构是由启动应用程序的程序传递给CreateProcess函数的。nCmdShow=10。
# SW_SHOWMAXIMIZED:激活窗口并将其最大化。nCmdShow=3。
# SW_SHOWMINIMIZED:激活窗口并将其最小化。nCmdShow=2。
# SW_SHOWMINNOACTIVE:窗口最小化,激活窗口仍然维持激活状态。nCmdShow=7。
# SW_SHOWNA:以窗口原来的状态显示窗口。激活窗口仍然维持激活状态。nCmdShow=8。
# SW_SHOWNOACTIVATE:以窗口最近一次的大小和状态显示窗口。激活窗口仍然维持激活状态。nCmdShow=4。
# SW_SHOWNORMAL:激活并显示一个窗口。如果窗口被最小化或最大化,系统将其恢复到原来的尺寸和大小。应用程序在第一次显示窗口的时候应该指定此标志。nCmdShow=1。


# win32gui.GetDlgItem(hld,ID) 作用:用来捕获按钮 注:需要确保,多次打开窗口时目标窗口ID是不变的,才能准确获取目标窗口句柄
# hld:目标窗口的父窗口,也是上面获取到的窗口句柄信息。通过父向下找子
# ID: 目标窗口ID


# win32gui.MoveWindow(hld, int X, int Y, int nWidth, int nHeight, BOOL bRepaint) 移动某窗口hld到指定位置。
# x,y指与屏幕左上角距离,nWidth, nHeight 指长和高
# bRepaint:是否重绘

鼠标相关

1
2
# 获取当前鼠标点击的窗口元素的坐标
win32gui. GetCursorPos()

获取控件相对坐标

1
2
3
4
5
6
7
8
9
10
11
12
13
# -*- coding:utf-8 -*-
import win32gui,win32api
import time
wdname = u'父句柄名称'
hwnd = win32gui.FindWindow(0, wdname) # 父句柄
hwnd1 = win32gui.FindWindowEx(hwnd, None,'类名称', None) # 目标子句柄
windowRec = win32gui.GetWindowRect(hwnd1) # 目标子句柄窗口的坐标
while True:
tempt = win32api.GetCursorPos() # 记录鼠标所处位置的坐标
x = tempt[0]-windowRec[0] # 计算相对x坐标
y = tempt[1]-windowRec[1] # 计算相对y坐标
print(x,y)
time.sleep(0.5) # 每0.5s输出一次

Reference


Python 第三方模块之 win32gui
https://flepeng.github.io/021-Python-31-Python-第三方模块-01-windows-相关-Python-第三方模块之-win32gui/
作者
Lepeng
发布于
2021年4月27日
许可协议