# GUI Application automation and testing library # Copyright (C) 2006-2018 Mark Mc Mahon and Contributors # https://github.com/pywinauto/pywinauto/graphs/contributors # http://pywinauto.readthedocs.io/en/latest/credits.html # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # * Neither the name of pywinauto nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """Functions to retrieve properties from a window handle These are implemented in a procedural way so as to to be useful to other modules with the least conceptual overhead """ import ctypes import warnings import win32process import win32api import win32con import win32gui from . import win32functions from . import win32defines from . import win32structures from .actionlogger import ActionLogger #========================================================================= def text(handle): """Return the text of the window""" class_name = classname(handle) if class_name == 'IME': return 'Default IME' if class_name == 'MSCTFIME UI': return 'M' #length = win32functions.SendMessage(handle, win32defines.WM_GETTEXTLENGTH, 0, 0) # XXX: there are some very rare cases when WM_GETTEXTLENGTH hangs! # WM_GETTEXTLENGTH may hang even for notepad.exe main window! c_length = win32structures.DWORD(0) result = win32functions.SendMessageTimeout( handle, win32defines.WM_GETTEXTLENGTH, 0, 0, win32defines.SMTO_ABORTIFHUNG, 500, ctypes.byref(c_length) ) if result == 0: ActionLogger().log('WARNING! Cannot retrieve text length for handle = ' + str(handle)) return None else: length = c_length.value textval = '' # In some rare cases, the length returned by WM_GETTEXTLENGTH is <0. # Guard against this by checking it is >0 (==0 is not of interest): if length > 0: length += 1 buffer_ = ctypes.create_unicode_buffer(length) ret = win32functions.SendMessage( handle, win32defines.WM_GETTEXT, length, ctypes.byref(buffer_)) if ret: textval = buffer_.value return textval #========================================================================= def classname(handle): """Return the class name of the window""" class_name = (ctypes.c_wchar * 257)() win32functions.GetClassName(handle, ctypes.byref(class_name), 256) return class_name.value #========================================================================= def parent(handle): """Return the handle of the parent of the window""" return win32functions.GetParent(handle) #========================================================================= def style(handle): """Return the style of the window""" return win32functions.GetWindowLong(handle, win32defines.GWL_STYLE) #========================================================================= def exstyle(handle): """Return the extended style of the window""" return win32functions.GetWindowLong(handle, win32defines.GWL_EXSTYLE) #========================================================================= def controlid(handle): """Return the ID of the control""" return win32functions.GetWindowLong(handle, win32defines.GWL_ID) #========================================================================= def userdata(handle): """Return the value of any user data associated with the window""" return win32functions.GetWindowLong(handle, win32defines.GWL_USERDATA) #========================================================================= def contexthelpid(handle): """Return the context help id of the window""" return win32functions.GetWindowContextHelpId(handle) #========================================================================= def iswindow(handle): """Return True if the handle is a window""" return bool(win32functions.IsWindow(handle)) #========================================================================= def isvisible(handle): """Return True if the window is visible""" return bool(win32functions.IsWindowVisible(handle)) #========================================================================= def isunicode(handle): """Return True if the window is a Unicode window""" return bool(win32functions.IsWindowUnicode(handle)) #========================================================================= def isenabled(handle): """Return True if the window is enabled""" return bool(win32functions.IsWindowEnabled(handle)) #========================================================================= def is64bitprocess(process_id): """Return True if the specified process is a 64-bit process on x64 Return False if it is only a 32-bit process running under Wow64. Always return False for x86. """ from .sysinfo import is_x64_OS is32 = True if is_x64_OS(): phndl = win32api.OpenProcess(win32con.MAXIMUM_ALLOWED, 0, process_id) if phndl: is32 = win32process.IsWow64Process(phndl) #print("is64bitprocess, is32: %d, procid: %d" % (is32, process_id)) return (not is32) #========================================================================= def is64bitbinary(filename): """Check if the file is 64-bit binary""" import win32file try: binary_type = win32file.GetBinaryType(filename) return binary_type != win32file.SCS_32BIT_BINARY except Exception as exc: warnings.warn('Cannot get binary type for file "{}". Error: {}' \ ''.format(filename, exc), RuntimeWarning, stacklevel=2) return None #========================================================================= def clientrect(handle): """Return the client rectangle of the control""" client_rect = win32structures.RECT() win32functions.GetClientRect(handle, ctypes.byref(client_rect)) return client_rect #========================================================================= def rectangle(handle): """Return the rectangle of the window""" # GetWindowRect returns 4-tuple return win32structures.RECT(*win32gui.GetWindowRect(handle)) #========================================================================= def font(handle): """Return the font as a LOGFONTW of the window""" # get the font handle font_handle = win32functions.SendMessage( handle, win32defines.WM_GETFONT, 0, 0) # if the fondUsed is 0 then the control is using the # system font (well probably not - even though that is what the docs say) # instead we switch to the default GUI font - which is more likely correct. if not font_handle: # So just get the default system font font_handle = win32functions.GetStockObject(win32defines.DEFAULT_GUI_FONT) # if we still don't have a font! # ----- ie, we're on an antiquated OS, like NT 3.51 if not font_handle: # ----- On Asian platforms, ANSI font won't show. if win32functions.GetSystemMetrics(win32defines.SM_DBCSENABLED): # ----- was...(SYSTEM_FONT) font_handle = win32functions.GetStockObject( win32defines.SYSTEM_FONT) else: # ----- was...(SYSTEM_FONT) font_handle = win32functions.GetStockObject( win32defines.ANSI_VAR_FONT) else: fontval = win32structures.LOGFONTW() ret = win32functions.GetObject( font_handle, ctypes.sizeof(fontval), ctypes.byref(fontval)) # Get the Logfont structure of the font of the control fontval = win32structures.LOGFONTW() ret = win32functions.GetObject( font_handle, ctypes.sizeof(fontval), ctypes.byref(fontval)) # The function could not get the font - this is probably # because the control does not have associated Font/Text # So we should make sure the elements of the font are zeroed. if not ret: fontval = win32structures.LOGFONTW() # if it is a main window if is_toplevel_window(handle): if "MS Shell Dlg" in fontval.lfFaceName or \ fontval.lfFaceName == "System": # these are not usually the fonts actaully used in for # title bars so we need to get the default title bar font # get the title font based on the system metrics rather # than the font of the control itself ncms = win32structures.NONCLIENTMETRICSW() ncms.cbSize = ctypes.sizeof(ncms) win32functions.SystemParametersInfo( win32defines.SPI_GETNONCLIENTMETRICS, ctypes.sizeof(ncms), ctypes.byref(ncms), 0) # with either of the following 2 flags set the font of the # dialog isthe small one (but there is normally no difference! if has_style(handle, win32defines.WS_EX_TOOLWINDOW) or \ has_style(handle, win32defines.WS_EX_PALETTEWINDOW): fontval = ncms.lfSmCaptionFont else: fontval = ncms.lfCaptionFont return fontval #========================================================================= def processid(handle): """Return the ID of process that controls this window""" _, process_id = win32process.GetWindowThreadProcessId(int(handle)) return process_id #========================================================================= def has_enough_privileges(process_id): """Check if target process has enough rights to query GUI actions""" try: access_level = win32con.PROCESS_QUERY_INFORMATION | win32con.PROCESS_VM_READ process_handle = win32api.OpenProcess(access_level, 0, process_id) if process_handle: win32api.CloseHandle(process_handle) return True return False except win32gui.error: return False #========================================================================= def children(handle): """Return a list of handles to the children of this window""" # this will be filled in the callback function child_windows = [] # callback function for EnumChildWindows def enum_child_proc(hwnd, lparam): """Called for each child - adds child hwnd to list""" # append it to our list child_windows.append(hwnd) # return true to keep going return True # define the child proc type enum_child_proc_t = ctypes.WINFUNCTYPE( ctypes.c_int, # return type win32structures.HWND, # the window handle win32structures.LPARAM) # extra information # update the proc to the correct type proc = enum_child_proc_t(enum_child_proc) # loop over all the children (callback called for each) win32functions.EnumChildWindows(handle, proc, 0) return child_windows #========================================================================= def has_style(handle, tocheck): """Return True if the control has style tocheck""" hwnd_style = style(handle) return tocheck & hwnd_style == tocheck #========================================================================= def has_exstyle(handle, tocheck): """Return True if the control has extended style tocheck""" hwnd_exstyle = exstyle(handle) return tocheck & hwnd_exstyle == tocheck #========================================================================= def is_toplevel_window(handle): """Return whether the window is a top level window or not""" # only request the style once - this is an optimization over calling # (handle, style) for each style I wan to check! style_ = style(handle) if (style_ & win32defines.WS_OVERLAPPED == win32defines.WS_OVERLAPPED or style_ & win32defines.WS_CAPTION == win32defines.WS_CAPTION) and \ not (style_ & win32defines.WS_CHILD == win32defines.WS_CHILD): return True else: return False #========================================================================= def dumpwindow(handle): """Dump a window to a set of properties""" props = {} for func in (text, classname, rectangle, clientrect, style, exstyle, contexthelpid, controlid, userdata, font, parent, processid, isenabled, isunicode, isvisible, children, ): props[func.__name__] = func(handle) return props