From 94430825f3b8e26ab75ba6e6f16215a97e8450b4 Mon Sep 17 00:00:00 2001 From: Vasily Ryabov Date: Tue, 3 Jan 2023 19:15:21 +0300 Subject: [PATCH 1/3] Fix #1249 (error in dump_tree when None is returned by class_name property) --- pywinauto/findbestmatch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pywinauto/findbestmatch.py b/pywinauto/findbestmatch.py index 146a28c25..d7415dd5b 100644 --- a/pywinauto/findbestmatch.py +++ b/pywinauto/findbestmatch.py @@ -306,7 +306,7 @@ def get_control_names(control, allcontrols, textcontrols): # Todo - I don't like the hardcoded classnames here! if cleaned and control.has_title: names.append(cleaned) - names.append(cleaned + friendly_class_name) + names.append(cleaned + six.text_type(friendly_class_name)) elif control.has_title and friendly_class_name != 'TreeView': try: for text in control.texts()[1:]: From bc60767cdc3785d69d43492ea653c32ebf4bf7fa Mon Sep 17 00:00:00 2001 From: Vasily Ryabov Date: Sun, 5 Feb 2023 13:26:00 +0300 Subject: [PATCH 2/3] Allow typing Unicode symbols beyond U+ffff (fix #1278) --- pywinauto/unittests/test_keyboard.py | 7 +++++ pywinauto/windows/keyboard.py | 44 ++++++++++++++++++++++++---- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/pywinauto/unittests/test_keyboard.py b/pywinauto/unittests/test_keyboard.py index c8c47415d..921a0078c 100644 --- a/pywinauto/unittests/test_keyboard.py +++ b/pywinauto/unittests/test_keyboard.py @@ -295,6 +295,13 @@ def testAltModifier(self): received = self.receive_text() self.assertEqual('abc', received) + def test_issue_1278__utf32_surrogate_pair_of_utf16(self): + """Make sure that typing Uniocode symbols beyond U+ffff works (issue #1278)""" + cherry_symbol = '\ud83c\udf52'.encode('utf-16', errors="surrogatepass").decode("utf-16") + send_keys(cherry_symbol) + received = self.receive_text() + self.assertEqual(cherry_symbol, received) + if sys.platform == 'win32': class SendKeysModifiersTests(unittest.TestCase): diff --git a/pywinauto/windows/keyboard.py b/pywinauto/windows/keyboard.py index 751cf9144..90c3ea7d9 100644 --- a/pywinauto/windows/keyboard.py +++ b/pywinauto/windows/keyboard.py @@ -736,31 +736,65 @@ def get_key_info(self): """ return self._get_key_info() + def _get_utf_16_surrogate_pair(self): + """Return 2 scan codes of utf-16 surrogate pair for utf-32 symbol beyond \uFFFF""" + scan_utf32 = ord(self.key) + code = (scan_utf32 - 0x10000) + return 0xD800 | (code >> 10), 0xDC00 | (code & 0x3FF) + def GetInput(self): """Build the INPUT structure for the action""" + vk, scan, flags = self._get_key_info() + actions = 1 # if both up and down if self.up and self.down: actions = 2 + # utf-32 4 byte symbol requires utf-16 surrogate pair + if 0xffff < scan <= 0x10ffff: + actions *= 2 + scan_codes = self._get_utf_16_surrogate_pair() + else: + scan_codes = (scan, scan) + inputs = (win32structures.INPUT * actions)() - vk, scan, flags = self._get_key_info() + if self.down: + if self.up: + # reference to the first half of array + down_inputs = (win32structures.INPUT * (actions // 2)).from_buffer(inputs) + else: + # reference to the whole array + down_inputs = (win32structures.INPUT * actions).from_buffer(inputs) + if self.up: + if self.down: + # reference to the second half of array + up_inputs = (win32structures.INPUT * (actions // 2)).from_buffer(inputs, sizeof(win32structures.INPUT) * (actions // 2)) + else: + # reference to the whole array + up_inputs = (win32structures.INPUT * actions).from_buffer(inputs) - for inp in inputs: + def _fill_input_struct(input_instance, scan_code, input_flags): inp.type = INPUT_KEYBOARD inp.ki.wVk = vk - inp.ki.wScan = scan + inp.ki.wScan = scan_code inp.ki.dwFlags |= flags # it seems to return 0 every time but it's required by MSDN specification # so call it just in case inp.ki.dwExtraInfo = win32functions.GetMessageExtraInfo() - # if we are releasing - then let it up + if self.down: + for i, inp in enumerate(down_inputs): + _fill_input_struct(inp, scan_codes[i], flags) + if self.up: - inputs[-1].ki.dwFlags |= KEYEVENTF_KEYUP + # if we are releasing - then let it up + flags |= KEYEVENTF_KEYUP + for i, inp in enumerate(up_inputs): + _fill_input_struct(inp, scan_codes[i], flags) return inputs From e09e8f062de4b77e0e32e4161f22fa35e74396ce Mon Sep 17 00:00:00 2001 From: Vasily Ryabov Date: Fri, 10 Feb 2023 21:33:50 +0300 Subject: [PATCH 3/3] Fix the first part of issue #949 --- pywinauto/windows/uia_element_info.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pywinauto/windows/uia_element_info.py b/pywinauto/windows/uia_element_info.py index c57cc0503..77e56e373 100644 --- a/pywinauto/windows/uia_element_info.py +++ b/pywinauto/windows/uia_element_info.py @@ -511,8 +511,8 @@ def rectangle(self): rect.top = bound_rect.top rect.right = bound_rect.right rect.bottom = bound_rect.bottom - except COMError: - pass + except COMError as e: + warnings.warn("Can't get element rectangle due to error: {}".format(e), RuntimeWarning) return rect def dump_window(self): @@ -548,7 +548,11 @@ def __eq__(self, other): """Check if 2 UIAElementInfo objects describe 1 actual element""" if not isinstance(other, UIAElementInfo): return False - return bool(IUIA().iuia.CompareElements(self.element, other.element)) + try: + return bool(IUIA().iuia.CompareElements(self.element, other.element)) + except COMError: + warnings.warn("Can't compare elements due to error: {}".format(e), RuntimeWarning) + return False def __ne__(self, other): """Check if 2 UIAElementInfo objects describe 2 different elements"""