我們通過python來開發自動化或者RPA工具時,經常需要用到大名鼎鼎的pywin32庫。里面有諸如sendMessage、PostMessage、FindWindow、FindwindowEx、EnumWindows等大票好用的windows系統API函數。提到Findow函數,就不得不提及它的一大缺憾:不支持基於窗口標題關鍵字模糊查找。
我們先來認識下今天的主角FindWindow函數。
MSDN中的解釋如下:
簡單地說,該函數檢索處理頂級窗口的類名和窗口名稱匹配指定的字符串,並不會搜索子窗口。
它的C++參數構成如下:
HWND FindWindowA( [in, optional] LPCSTR lpClassName, [in, optional] LPCSTR lpWindowName );
再來看下它的返回值:
如果函數執行成功,它會返回包含指定類名和窗口名的窗口對應的句柄,如果失敗,則返回Null。
認識了該函數之后,我們再回到文章開頭提到的痛點,如何讓該函數支持基於窗口標題模糊查找呢?
大體的思路是,利用EnumWindows函數得到所有頂級窗口的句柄,再通過GetClassName、GetWindowText等函數依次拿到對應窗口的類名、標題,並與我們預設的類名、窗口名關鍵字進行匹配,如果能關聯到,返回對應的窗口句柄即可,我們需要的是這樣一個人性化的FindWindow函數,姑且取名find_window_wildcard,說干就干,下面是具體的實現過程。
1 import win32gui,re 2 class WindowMgr: 3 """Encapsulates some calls to the winapi for window management""" 4 5 def __init__ (self): 6 """Constructor""" 7 self._handle = None 8 9 def find_window(self, class_name, window_name=None): 10 """基於類名來查找窗口""" 11 self._handle = win32gui.FindWindow(class_name, window_name) 12 13 def _window_enum_callback(self, hwnd, class_name_wildcard_list): 14 """傳遞給win32gui.EnumWindows(),檢查所有打開的頂級窗口""" 15 class_name,wildcard=class_name_wildcard_list 16 if win32gui.GetClassName(hwnd)==class_name and re.match(wildcard, str(win32gui.GetWindowText(hwnd))) is not None: 17 self._handle = hwnd 18 19 def find_window_wildcard(self, class_name,wildcard): 20 """根據類名,查找一個頂級窗口,確保其類名相符,且標題可以用正則表達式匹配對應的通配符""" 21 self._handle = None 22 win32gui.EnumWindows(self._window_enum_callback,[class_name, wildcard]) 23 return self._handle 24 25 def set_foreground(self): 26 """put the window in the foreground""" 27 win32gui.SetForegroundWindow(self._handle) 28 29 def get_hwnd(self): 30 """return hwnd for further use""" 31 return self._handle 32 if __name__=="__main__": 33 myWindowMgr=WindowMgr() 34 '''查找一個類名為XLMAIN,窗口標題中含【銀行余額】字符串的窗口,並返回它的句柄;如果沒找到,返回None''' 35 excelHwnd=myWindowMgr.find_window_wildcard("XLMAIN",".*?銀行余額.*?") 36 print(excelHwnd)
各位筒子趕緊試試,試完你一定會回來感謝我的,相信我~~
快來掃碼關注我的公眾號 獲取更多爬蟲、數據分析的知識!