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/win32_element_info.py

241 lines
8.4 KiB

# 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.
"""Implementation of the class to deal with a native element (window with a handle)"""
import ctypes
from ctypes import wintypes
import six
import win32gui
from . import win32functions
from . import handleprops
from .element_info import ElementInfo
from .remote_memory_block import RemoteMemoryBlock
def _register_win_msg(msg_name):
msg_id = win32functions.RegisterWindowMessage(six.text_type(msg_name))
if msg_id > 0:
return msg_id
else:
raise Exception("Cannot register {}".format(msg_name))
class HwndElementInfo(ElementInfo):
"""Wrapper for window handler"""
wm_get_ctrl_name = _register_win_msg('WM_GETCONTROLNAME')
wm_get_ctrl_type = _register_win_msg('WM_GETCONTROLTYPE')
def __init__(self, handle=None):
"""Create element by handle (default is root element)"""
self._cache = {}
if handle is None: # root element
self._handle = win32functions.GetDesktopWindow()
else:
self._handle = handle
def set_cache_strategy(self, cached):
"""Set a cache strategy for frequently used attributes of the element"""
pass # TODO: implement a cache strategy for native elements
@property
def handle(self):
"""Return the handle of the window"""
return self._handle
@property
def rich_text(self):
"""Return the text of the window"""
return handleprops.text(self.handle)
name = rich_text
@property
def control_id(self):
"""Return the ID of the window"""
return handleprops.controlid(self.handle)
@property
def process_id(self):
"""Return the ID of process that controls this window"""
return handleprops.processid(self.handle)
@property
def class_name(self):
"""Return the class name of the window"""
return handleprops.classname(self.handle)
@property
def enabled(self):
"""Return True if the window is enabled"""
return handleprops.isenabled(self.handle)
@property
def visible(self):
"""Return True if the window is visible"""
return handleprops.isvisible(self.handle)
@property
def parent(self):
"""Return the parent of the window"""
parent_hwnd = handleprops.parent(self.handle)
if parent_hwnd:
return HwndElementInfo(parent_hwnd)
else:
return None
def children(self, **kwargs):
"""Return a list of immediate children of the window"""
class_name = kwargs.get('class_name', None)
title = kwargs.get('title', None)
control_type = kwargs.get('control_type', None)
# TODO: 'cache_enable' and 'depth' are ignored so far
# this will be filled in the callback function
child_elements = []
# The callback function that will be called for each HWND
# all we do is append the wrapped handle
def enum_window_proc(hwnd, lparam):
"""Called for each window - adds wrapped elements to a list"""
element = HwndElementInfo(hwnd)
if class_name is not None and class_name != element.class_name:
return True
if title is not None and title != element.rich_text:
return True
if control_type is not None and control_type != element.control_type:
return True
child_elements.append(element)
return True
# define the type of the child procedure
enum_win_proc_t = ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.HWND, wintypes.LPARAM)
# 'construct' the callback with our function
proc = enum_win_proc_t(enum_window_proc)
if self == HwndElementInfo(): # self == root
# loop over all the top level windows (callback called for each)
win32functions.EnumWindows(proc, 0)
else:
# loop over all the children (callback called for each)
win32functions.EnumChildWindows(self.handle, proc, 0)
return child_elements
def iter_children(self, **kwargs):
"""Return a generator of immediate children of the window"""
# TODO: Iterate over children using Win32 API
for child in self.children(**kwargs):
yield child
def descendants(self, **kwargs):
"""Return descendants of the window (all children from sub-tree)"""
if self == HwndElementInfo(): # root
top_elements = self.children()
child_elements = self.children(**kwargs)
for child in top_elements:
child_elements.extend(child.children(**kwargs))
else:
child_elements = self.children(**kwargs)
depth = kwargs.pop('depth', None)
child_elements = ElementInfo.filter_with_depth(child_elements, self, depth)
return child_elements
@property
def rectangle(self):
"""Return rectangle of the element"""
return handleprops.rectangle(self.handle)
def dump_window(self):
"""Dump a window as a set of properties"""
return handleprops.dumpwindow(self.handle)
def __eq__(self, other):
"""Check if 2 HwndElementInfo objects describe 1 actual element"""
if not isinstance(other, HwndElementInfo):
return self.handle == other
return self.handle == other.handle
@property
def automation_id(self):
"""Return AutomationId of the element"""
textval = ''
length = 1024
remote_mem = RemoteMemoryBlock(self, size=length*2)
ret = win32gui.SendMessage(self.handle, self.wm_get_ctrl_name, length, remote_mem.mem_address)
if ret:
text = ctypes.create_unicode_buffer(length)
remote_mem.Read(text)
textval = text.value
del remote_mem
return textval
def __get_control_type(self, full=False):
"""Internal parameterized method to distinguish control_type and full_control_type properties"""
textval = ''
length = 1024
remote_mem = RemoteMemoryBlock(self, size=length*2)
ret = win32gui.SendMessage(self.handle, self.wm_get_ctrl_type, length, remote_mem.mem_address)
if ret:
text = ctypes.create_unicode_buffer(length)
remote_mem.Read(text)
textval = text.value
del remote_mem
# simplify control type for WinForms controls
if (not full) and ("PublicKeyToken" in textval):
textval = textval.split(", ")[0]
return textval
@property
def control_type(self):
"""Return control type of the element"""
return self.__get_control_type(full=False)
@property
def full_control_type(self):
"""Return full string of control type of the element"""
return self.__get_control_type(full=True)