最近开始学习整理python在windows自动化测试中的使用,觉得挺有意思的

主要思路,在windows下,主要通过启进程,然后查找进程的句柄,然后再操作这个句柄,包括点击,填写文字,关闭,获取文字等操作

下面以一个简单的校验文件md5值的操作来介绍一个python的应用,当然python中有校验md5的函数,不用非要使用工具来校验,这里只是练习使用python来自动化操作

所用的工具有SpyLite,用于查看窗口ID,句柄等信息

工具下载

http://www.xiazaiba.com/html/358.html 

http://www.xiazaiba.com/html/5861.html

我们要达到的目的是通过md5校验工具将文件的md5值保存到一个log文档中

测试的目录结构

–automd5–

    –needCheck–

    checkmd5.py

    SpyLite24.exe

    Hash.exe

needCheck目录放需要检查md5的文件

 

 

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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
#! /usr/bin/env python
#coding=gbk

import time,re,ctypes
import os,sys,subprocess,win32gui,win32con,win32api
import glob,time

#启进程函数
def createProcess(cmd, wait = False):
if wait:
proc = tryExcept(subprocess.Popen, cmd, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
else:
proc = tryExcept(subprocess.Popen, cmd)
if isExcept(proc):
return
if not wait:
return proc.pid
proc.communicate()


def tryExcept(func, *params, **paramMap):
try:
return func(*params, **paramMap)
except Exception, e:
return e
def isExcept(e, eType = Exception):
return isinstance(e, eType)


class BaseWindow:
@staticmethod
def parseClickConfig(clkCfg):
if clkCfg == None:
return None, None, False, 0
if type(clkCfg) == bool:
return None, None, clkCfg, 0
if type(clkCfg) == int:
return None, None, False, clkCfg
if len(clkCfg) == 2:
if type(clkCfg[0]) == int and type(clkCfg[1]) == int:
return clkCfg[0], clkCfg[1], False, 0
return None, None, clkCfg[0], clkCfg[1]
if len(clkCfg) == 3:
if type(clkCfg[2]) == bool:
return clkCfg[0], clkCfg[1], clkCfg[2], 0
return clkCfg[0], clkCfg[1], False, clkCfg[2]
return clkCfg


#点击窗口
#clkCfg:byDrv|mode|(x,y)|(byDrv,mode)|(x,y,byDrv)|(x,y,mode)|(x,y,byDrv,mode)
def clickWindow(hwnd, clkCfg = None):
if isRawWindow(hwnd):
return
topWindow(hwnd)
rect = getWindowRect(hwnd)
if not rect:
return
x, y, byDrv, mode = BaseWindow.parseClickConfig(clkCfg)
if x == None:
x = (rect[0] + rect[2]) / 2
elif x < 0:
x += rect[2]
else:
x += rect[0]
if y == None:
y = (rect[1] + rect[3]) / 2
elif y < 0:
y += rect[3]
else:
y += rect[1]
clickMouse(x, y, byDrv, mode)

#点击鼠标
CLICK_MOUSE = 0
CLICK_MOUSE_DOUBLE = 1
CLICK_MOUSE_RIGHT = 2

def clickMouse(x, y, byDrv = False, mode = CLICK_MOUSE):
moveMouse(x, y, byDrv)
downMsg, upMsg = win32con.MOUSEEVENTF_LEFTDOWN, win32con.MOUSEEVENTF_LEFTUP
if mode == CLICK_MOUSE_RIGHT:
downMsg, upMsg = win32con.MOUSEEVENTF_RIGHTDOWN, win32con.MOUSEEVENTF_RIGHTUP
win32api.mouse_event(downMsg, 0, 0, 0, 0)
win32api.mouse_event(upMsg, 0, 0, 0, 0)
if mode == CLICK_MOUSE_DOUBLE:
win32api.mouse_event(downMsg, 0, 0, 0, 0)
win32api.mouse_event(upMsg, 0, 0, 0, 0)

#移动鼠标
def moveMouse(x, y, byDrv = False):
w, h = win32api.GetSystemMetrics(0), win32api.GetSystemMetrics(1)
x, y = int(float(x) / w * 65535), int(float(y) / h * 65535)
win32api.mouse_event(win32con.MOUSEEVENTF_MOVE | win32con.MOUSEEVENTF_ABSOLUTE, x, y, 0, 0)


#获得窗口尺寸
def getWindowRect(hwnd):
rect = tryExcept(win32gui.GetWindowRect, hwnd)
if not isExcept(rect):
return rect

#置顶窗口
def topWindow(hwnd):
fgHwnd = win32gui.GetForegroundWindow()
if fgHwnd == hwnd:
return True
rst = tryExcept(win32gui.SetForegroundWindow, hwnd)
if not isExcept(rst):
return True
return getWindowClass(fgHwnd) == 'Progman' and getWindowText(fgHwnd) == 'Program Manager'


##获取窗口文字
def getWindowText(hwnd, buf = ctypes.c_buffer(1024)):
size = ctypes.sizeof(buf)
ctypes.memset(buf, 0, size)
tryExcept(win32gui.SendMessageTimeout, hwnd, win32con.WM_GETTEXT, size, buf, win32con.SMTO_ABORTIFHUNG, 10)
return buf.value.strip()

#获取窗口类名
def getWindowClass(hwnd, buf = ctypes.c_buffer(1024)):
size = ctypes.sizeof(buf)
ctypes.memset(buf, 0, size)
ctypes.windll.user32.GetClassNameA(hwnd, ctypes.addressof(buf), size)
return buf.value.strip()

#查找第一个窗口
#title:text,class,ctrlid
#parentTitle:None,hwnd,text,class
def findWindow(title, parentTitle = None, isRaw = False):
hwndList = findWindows(title, parentTitle, isRaw)
if hwndList:
return hwndList[0]
def findRawWindows(title, parentTitle = None):
return findWindows(title, parentTitle, True)

#判断是否为非正常窗口
def isRawWindow(hwnd):
return not win32gui.IsWindowVisible(hwnd) or not win32gui.IsWindowEnabled(hwnd) or ctypes.windll.user32.IsHungAppWindow(hwnd)

#查找窗口
#title:text,class,ctrlid
#parentTitle:None,hwnd,text,class
def findWindows(title, parentTitle = None, isRaw = False):
def __enumWindowHandler__(hwnd, wndList):
text = re.split('[r|n]+', getWindowText(hwnd))[0].strip()
clazz = getWindowClass(hwnd).strip()
ctrlId = win32gui.GetDlgCtrlID(hwnd)
wndList.append((hwnd, text, clazz, ctrlId))
if not parentTitle:
parentHwndList = [None]
elif type(parentTitle) == int:
parentHwndList = [parentTitle]
else:
parentHwndList = findRawWindows(parentTitle)
hwndSet = set()
for parentHwnd in parentHwndList:
wndList = []
#EnumWindows
if parentHwnd:
tryExcept(win32gui.EnumChildWindows, parentHwnd, __enumWindowHandler__, wndList)
else:
win32gui.EnumWindows(__enumWindowHandler__, wndList)
#FindWindowEx
hwnd, foundHwndList = None, []
while True:
hwnd = tryExcept(win32gui.FindWindowEx, parentHwnd, hwnd, None, None)
if not hwnd or isExcept(hwnd) or hwnd in foundHwndList:
break
__enumWindowHandler__(hwnd, wndList)
foundHwndList.append(hwnd)
#GetWindow
if parentHwnd:
hwnd = tryExcept(win32gui.GetWindow, parentHwnd, win32con.GW_CHILD)
else:
hwnd = tryExcept(win32gui.GetWindow, win32gui.GetDesktopWindow(), win32con.GW_CHILD)
while hwnd and not isExcept(hwnd):
__enumWindowHandler__(hwnd, wndList)
hwnd = tryExcept(win32gui.GetWindow, hwnd, win32con.GW_HWNDNEXT)
#Combine
for hwnd, text, clazz, ctrlId in set(wndList):
if type(title) == int:
if ctrlId == title:
hwndSet.add(hwnd)
elif text == title or re.match('^' + title + '$', text, re.S) or clazz == title or re.match('^' + title + '$', clazz, re.S):
hwndSet.add(hwnd)
if parentHwnd:
hwndSet.update(findRawWindows(title, hwnd))
if isRaw:
return list(hwndSet)
hwndList = []
for hwnd in hwndSet:
if not isRawWindow(hwnd):
hwndList.append(hwnd)
return hwndList

#设置窗口文字
def setWindowText(hwnd, text):
rst = tryExcept(win32gui.SendMessageTimeout, hwnd, win32con.WM_SETTEXT, 0, text, win32con.SMTO_ABORTIFHUNG, 10)
return not isExcept(rst)

#杀掉指定name的进程
def killProcessByName(pname, user = None):
killProcessByNames([pname], user)

def listFile(path, isDeep=True):
_list = []
if isDeep:
try:
for root, dirs, files in os.walk(path):
for fl in files:
_list.append('%s%s' % (root, fl))
except:
pass
else:
for fn in glob.glob( path + os.sep + '*' ):
if not os.path.isdir(fn):
_list.append('%s' % path + os.sep + fn[fn.rfind('')+1:])
return _list


#杀掉指定name的进程列表
def killProcessByNames(pnameList, user = None):
cmd = 'taskkill /F /T'
if user:
cmd += ' /FI "USERNAME eq %s"' % user
for pname in pnameList:
cmd += ' /IM %s' % pname
createProcess(cmd, True)

#超时设置
def handleTimeout(func, timeout, *params, **paramMap):
interval = 0.8
if type(timeout) == tuple:
timeout, interval = timeout
while timeout > 0:
t = time.time()
rst = func(*params, **paramMap)
if rst and not isExcept(rst):
break
time.sleep(interval)
timeout -= time.time() - t
return rst

#写文件
def setFileData(filename, data, mode):
f = open(filename, mode)
f.write(data)
f.close()


if __name__ == '__main__':
if os.path.isfile('md5.log'):
os.system('del md5.log')

parentHwnd = r'Hash.*?'
#setWindowText(textblack,'123')
filedir = os.path.join(os.getcwd(),'needCheck')
filelist = listFile(filedir)
#os.chdir(filedir)

for file in filelist:
#启进程
createProcess('Hash.exe')
#查找浏览按钮
browse_button = findWindow(r'浏览.*?',parentHwnd)
#点击浏览按钮
clickWindow(browse_button)
textblack = findWindow(0x47C,'#32770')
handleTimeout(setWindowText,10,textblack,file)
open_hwnd = findWindow('打开.*?','#32770')
#点击打开按钮
clickWindow(open_hwnd)
#获取文件md5信息
#需要内容的行号
expect_content_id = [0,4]
content_hwnd = findWindow(0x3EA,r'Hash.*?')
content_text = handleTimeout(getWindowText,20,content_hwnd)
content = content_text.split('rn')
md5content = [i.split(': ')[1].strip() for ind, i in enumerate(content) if ind in expect_content_id]
print md5content
filename,md5value = md5content
setFileData('md5.log','文件名:'+filename+'n'+'md5:'+ md5value+'nn','a')
killProcessByName('Hash.exe')
os.system('pause')