最近开始学习整理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的文件
005 | import os,sys,subprocess,win32gui,win32con,win32api |
009 | def createProcess(cmd, wait = False ): |
011 |
proc = tryExcept(subprocess.Popen, cmd, stdout = subprocess.PIPE, stderr = subprocess.PIPE) |
013 |
proc = tryExcept(subprocess.Popen, cmd) |
021 | def tryExcept(func, * params, * * paramMap): |
023 |
return func( * params, * * paramMap) |
026 | def isExcept(e, eType = Exception): |
027 |
return isinstance (e, eType) |
032 |
def parseClickConfig(clkCfg): |
034 |
return None , None , False , 0 |
035 |
if type (clkCfg) = = bool : |
036 |
return None , None , clkCfg, 0 |
037 |
if type (clkCfg) = = int : |
038 |
return None , None , False , clkCfg |
040 |
if type (clkCfg[ 0 ]) = = int and type (clkCfg[ 1 ]) = = int : |
041 |
return clkCfg[ 0 ], clkCfg[ 1 ], False , 0 |
042 |
return None , None , clkCfg[ 0 ], clkCfg[ 1 ] |
044 |
if type (clkCfg[ 2 ]) = = bool : |
045 |
return clkCfg[ 0 ], clkCfg[ 1 ], clkCfg[ 2 ], 0 |
046 |
return clkCfg[ 0 ], clkCfg[ 1 ], False , clkCfg[ 2 ] |
052 | def clickWindow(hwnd, clkCfg = None ): |
056 |
rect = getWindowRect(hwnd) |
059 |
x, y, byDrv, mode = BaseWindow.parseClickConfig(clkCfg) |
061 |
x = (rect[ 0 ] + rect[ 2 ]) / 2 |
067 |
y = (rect[ 1 ] + rect[ 3 ]) / 2 |
072 |
clickMouse(x, y, byDrv, mode) |
076 | CLICK_MOUSE_DOUBLE = 1 |
079 | def clickMouse(x, y, byDrv = False , mode = CLICK_MOUSE): |
080 |
moveMouse(x, y, byDrv) |
081 |
downMsg, upMsg = win32con.MOUSEEVENTF_LEFTDOWN, win32con.MOUSEEVENTF_LEFTUP |
082 |
if mode = = CLICK_MOUSE_RIGHT: |
083 |
downMsg, upMsg = win32con.MOUSEEVENTF_RIGHTDOWN, win32con.MOUSEEVENTF_RIGHTUP |
084 |
win32api.mouse_event(downMsg, 0 , 0 , 0 , 0 ) |
085 |
win32api.mouse_event(upMsg, 0 , 0 , 0 , 0 ) |
086 |
if mode = = CLICK_MOUSE_DOUBLE: |
087 |
win32api.mouse_event(downMsg, 0 , 0 , 0 , 0 ) |
088 |
win32api.mouse_event(upMsg, 0 , 0 , 0 , 0 ) |
091 | def moveMouse(x, y, byDrv = False ): |
092 |
w, h = win32api.GetSystemMetrics( 0 ), win32api.GetSystemMetrics( 1 ) |
093 |
x, y = int ( float (x) / w * 65535 ), int ( float (y) / h * 65535 ) |
094 |
win32api.mouse_event(win32con.MOUSEEVENTF_MOVE | win32con.MOUSEEVENTF_ABSOLUTE, x, y, 0 , 0 ) |
098 | def getWindowRect(hwnd): |
099 |
rect = tryExcept(win32gui.GetWindowRect, hwnd) |
100 |
if not isExcept(rect): |
105 |
fgHwnd = win32gui.GetForegroundWindow() |
108 |
rst = tryExcept(win32gui.SetForegroundWindow, hwnd) |
111 |
return getWindowClass(fgHwnd) = = 'Progman' and getWindowText(fgHwnd) = = 'Program Manager' |
115 | def getWindowText(hwnd, buf = ctypes.c_buffer( 1024 )): |
116 |
size = ctypes.sizeof(buf) |
117 |
ctypes.memset(buf, 0 , size) |
118 |
tryExcept(win32gui.SendMessageTimeout, hwnd, win32con.WM_GETTEXT, size, buf, win32con.SMTO_ABORTIFHUNG, 10 ) |
119 |
return buf.value.strip() |
122 | def getWindowClass(hwnd, buf = ctypes.c_buffer( 1024 )): |
123 |
size = ctypes.sizeof(buf) |
124 |
ctypes.memset(buf, 0 , size) |
125 |
ctypes.windll.user32.GetClassNameA(hwnd, ctypes.addressof(buf), size) |
126 |
return buf.value.strip() |
131 | def findWindow(title, parentTitle = None , isRaw = False ): |
132 |
hwndList = findWindows(title, parentTitle, isRaw) |
135 | def findRawWindows(title, parentTitle = None ): |
136 |
return findWindows(title, parentTitle, True ) |
139 | def isRawWindow(hwnd): |
140 |
return not win32gui.IsWindowVisible(hwnd) or not win32gui.IsWindowEnabled(hwnd) or ctypes.windll.user32.IsHungAppWindow(hwnd) |
145 | def findWindows(title, parentTitle = None , isRaw = False ): |
146 |
def __enumWindowHandler__(hwnd, wndList): |
147 |
text = re.split( '[\r|\n]+' , getWindowText(hwnd))[ 0 ].strip() |
148 |
clazz = getWindowClass(hwnd).strip() |
149 |
ctrlId = win32gui.GetDlgCtrlID(hwnd) |
150 |
wndList.append((hwnd, text, clazz, ctrlId)) |
152 |
parentHwndList = [ None ] |
153 |
elif type (parentTitle) = = int : |
154 |
parentHwndList = [parentTitle] |
156 |
parentHwndList = findRawWindows(parentTitle) |
158 |
for parentHwnd in parentHwndList: |
162 |
tryExcept(win32gui.EnumChildWindows, parentHwnd, __enumWindowHandler__, wndList) |
164 |
win32gui.EnumWindows(__enumWindowHandler__, wndList) |
166 |
hwnd, foundHwndList = None , [] |
168 |
hwnd = tryExcept(win32gui.FindWindowEx, parentHwnd, hwnd, None , None ) |
169 |
if not hwnd or isExcept(hwnd) or hwnd in foundHwndList: |
171 |
__enumWindowHandler__(hwnd, wndList) |
172 |
foundHwndList.append(hwnd) |
175 |
hwnd = tryExcept(win32gui.GetWindow, parentHwnd, win32con.GW_CHILD) |
177 |
hwnd = tryExcept(win32gui.GetWindow, win32gui.GetDesktopWindow(), win32con.GW_CHILD) |
178 |
while hwnd and not isExcept(hwnd): |
179 |
__enumWindowHandler__(hwnd, wndList) |
180 |
hwnd = tryExcept(win32gui.GetWindow, hwnd, win32con.GW_HWNDNEXT) |
182 |
for hwnd, text, clazz, ctrlId in set (wndList): |
183 |
if type (title) = = int : |
186 |
elif text = = title or re.match( '^' + title + '$' , text, re.S) or clazz = = title or re.match( '^' + title + '$' , clazz, re.S): |
189 |
hwndSet.update(findRawWindows(title, hwnd)) |
194 |
if not isRawWindow(hwnd): |
199 | def setWindowText(hwnd, text): |
200 |
rst = tryExcept(win32gui.SendMessageTimeout, hwnd, win32con.WM_SETTEXT, 0 , text, win32con.SMTO_ABORTIFHUNG, 10 ) |
201 |
return not isExcept(rst) |
204 | def killProcessByName(pname, user = None ): |
205 |
killProcessByNames([pname], user) |
207 | def listFile(path, isDeep = True ): |
211 |
for root, dirs, files in os.walk(path): |
213 |
_list.append( '%s\%s' % (root, fl)) |
217 |
for fn in glob.glob( path + os.sep + '*' ): |
218 |
if not os.path.isdir(fn): |
219 |
_list.append( '%s' % path + os.sep + fn[fn.rfind( '\\' ) + 1 :]) |
224 | def killProcessByNames(pnameList, user = None ): |
225 |
cmd = 'taskkill /F /T' |
227 |
cmd + = ' /FI "USERNAME eq %s"' % user |
228 |
for pname in pnameList: |
229 |
cmd + = ' /IM %s' % pname |
230 |
createProcess(cmd, True ) |
233 | def handleTimeout(func, timeout, * params, * * paramMap): |
235 |
if type (timeout) = = tuple : |
236 |
timeout, interval = timeout |
239 |
rst = func( * params, * * paramMap) |
240 |
if rst and not isExcept(rst): |
243 |
timeout - = time.time() - t |
247 | def setFileData(filename, data, mode): |
248 |
f = open (filename, mode) |
253 | if __name__ = = '__main__' : |
254 |
if os.path.isfile( 'md5.log' ): |
255 |
os.system( 'del md5.log' ) |
257 |
parentHwnd = r 'Hash.*?' |
259 |
filedir = os.path.join(os.getcwd(), 'needCheck' ) |
260 |
filelist = listFile(filedir) |
265 |
createProcess( 'Hash.exe' ) |
267 |
browse_button = findWindow(r '浏览.*?' ,parentHwnd) |
269 |
clickWindow(browse_button) |
270 |
textblack = findWindow( 0x47C , '#32770' ) |
271 |
handleTimeout(setWindowText, 10 ,textblack, file ) |
272 |
open_hwnd = findWindow( '打开.*?' , '#32770' ) |
274 |
clickWindow(open_hwnd) |
277 |
expect_content_id = [ 0 , 4 ] |
278 |
content_hwnd = findWindow( 0x3EA ,r 'Hash.*?' ) |
279 |
content_text = handleTimeout(getWindowText, 20 ,content_hwnd) |
280 |
content = content_text.split( '\r\n' ) |
281 |
md5content = [i.split( ': ' )[ 1 ].strip() for ind, i in enumerate (content) if ind in expect_content_id] |
283 |
filename,md5value = md5content |
284 |
setFileData( 'md5.log' , '文件名:' + filename + '\n' + 'md5:' + md5value + '\n\n' , 'a' ) |
285 |
killProcessByName( 'Hash.exe' ) |