最近开始学习整理python在windows自动化测试中的使用,觉得挺有意思的
主要思路,在windows下,主要通过启进程,然后查找进程的句柄,然后再操作这个句柄,包括点击,填写文字,关闭,获取文字等操作
下面以一个简单的校验文件md5值的操作来介绍一个python的应用,当然python中有校验md5的函数,不用非要使用工具来校验,这里只是练习使用python来自动化操作
所用的工具有SpyLite,用于查看窗口ID,句柄等信息
工具下载
我们要达到的目的是通过md5校验工具将文件的md5值保存到一个log文档中
测试的目录结构
--automd5--
--needCheck--
checkmd5.py
SpyLite24.exe
Hash.exe
needCheck目录放需要检查md5的文件
#! /usr/bin/env python#coding=gbkimport time,re,ctypesimport os,sys,subprocess,win32gui,win32con,win32apiimport 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 edef 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 = 0CLICK_MOUSE_DOUBLE = 1CLICK_MOUSE_RIGHT = 2def 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,classdef 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,classdef 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('\r\n') 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+'\n\n','a') killProcessByName('Hash.exe') os.system('pause')