# 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 warnings import win32process import win32api import win32con import win32gui from ctypes import wintypes from ctypes import WINFUNCTYPE from ctypes import c_int from ctypes import byref from ctypes import sizeof from ctypes import create_unicode_buffer 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' if class_name is None: return '' #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_PTR(0) result = win32functions.SendMessageTimeout( handle, win32defines.WM_GETTEXTLENGTH, 0, 0, win32defines.SMTO_ABORTIFHUNG, 500, byref(c_length) ) if result == 0: ActionLogger().log('WARNING! Cannot retrieve text length for handle = ' + str(handle)) return '' 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_ = create_unicode_buffer(length) ret = win32functions.SendMessage( handle, win32defines.WM_GETTEXT, length, byref(buffer_)) if ret: textval = buffer_.value return textval #========================================================================= def classname(handle): """Return the class name of the window""" if handle is None: return None class_name = create_unicode_buffer(u"", 257) win32functions.GetClassName(handle, 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 False if handle is None else bool(win32functions.IsWindow(handle)) #========================================================================= def isvisible(handle): """Return True if the window is visible""" return False if handle is None else bool(win32functions.IsWindowVisible(handle)) #========================================================================= def isunicode(handle): """Return True if the window is a Unicode window""" return False if handle is None else bool(win32functions.IsWindowUnicode(handle)) #========================================================================= def isenabled(handle): """Return True if the window is enabled""" return False if handle is None else 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, byref(client_rect)) return client_rect #========================================================================= def rectangle(handle): """Return the rectangle of the window""" rect = win32structures.RECT() win32functions.GetWindowRect(handle, byref(rect)) return rect #========================================================================= def font(handle): """Return the font as a LOGFONTW of the window""" # get the font handle if handle is None: handle = 0 # make sure we don't pass window handle down as None 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) # Get the Logfont structure of the font of the control fontval = win32structures.LOGFONTW() ret = win32functions.GetObject( font_handle, sizeof(fontval), 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 = sizeof(ncms) win32functions.SystemParametersInfo( win32defines.SPI_GETNONCLIENTMETRICS, sizeof(ncms), 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""" pid = wintypes.DWORD() win32functions.GetWindowThreadProcessId(handle, byref(pid)) return pid.value #========================================================================= 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 = WINFUNCTYPE( c_int, # return type wintypes.HWND, # the window handle wintypes.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