You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ORPA-pyOpenRPA/3rdParty/pywinauto/handleprops.py

391 lines
14 KiB

6 years ago
# 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