11364750dSJames Henderson# DExTer : Debugging Experience Tester 21364750dSJames Henderson# ~~~~~~ ~ ~~ ~ ~~ 31364750dSJames Henderson# 41364750dSJames Henderson# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 51364750dSJames Henderson# See https://llvm.org/LICENSE.txt for license information. 61364750dSJames Henderson# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 71364750dSJames Henderson"""Communication via the Windows COM interface.""" 81364750dSJames Henderson 91364750dSJames Hendersonimport inspect 101364750dSJames Hendersonimport time 111364750dSJames Hendersonimport sys 121364750dSJames Henderson 131364750dSJames Henderson# pylint: disable=import-error 141364750dSJames Hendersonimport win32com.client as com 151364750dSJames Hendersonimport win32api 16*f98ee40fSTobias Hieta 171364750dSJames Henderson# pylint: enable=import-error 181364750dSJames Henderson 191364750dSJames Hendersonfrom dex.utils.Exceptions import LoadDebuggerException 201364750dSJames Henderson 211364750dSJames Henderson_com_error = com.pywintypes.com_error # pylint: disable=no-member 221364750dSJames Henderson 231364750dSJames Henderson 241364750dSJames Hendersondef get_file_version(file_): 251364750dSJames Henderson try: 26*f98ee40fSTobias Hieta info = win32api.GetFileVersionInfo(file_, "\\") 27*f98ee40fSTobias Hieta ms = info["FileVersionMS"] 28*f98ee40fSTobias Hieta ls = info["FileVersionLS"] 29*f98ee40fSTobias Hieta return ".".join( 30*f98ee40fSTobias Hieta str(s) 31*f98ee40fSTobias Hieta for s in [ 321364750dSJames Henderson win32api.HIWORD(ms), 331364750dSJames Henderson win32api.LOWORD(ms), 341364750dSJames Henderson win32api.HIWORD(ls), 35*f98ee40fSTobias Hieta win32api.LOWORD(ls), 36*f98ee40fSTobias Hieta ] 37*f98ee40fSTobias Hieta ) 381364750dSJames Henderson except com.pywintypes.error: # pylint: disable=no-member 39*f98ee40fSTobias Hieta return "no versioninfo present" 401364750dSJames Henderson 411364750dSJames Henderson 421364750dSJames Hendersondef _handle_com_error(e): 431364750dSJames Henderson exc = sys.exc_info() 441364750dSJames Henderson msg = win32api.FormatMessage(e.hresult) 451364750dSJames Henderson try: 46*f98ee40fSTobias Hieta msg = msg.decode("CP1251") 471364750dSJames Henderson except AttributeError: 481364750dSJames Henderson pass 491364750dSJames Henderson msg = msg.strip() 501364750dSJames Henderson return msg, exc 511364750dSJames Henderson 521364750dSJames Henderson 531364750dSJames Hendersonclass ComObject(object): 541364750dSJames Henderson """Wrap a raw Windows COM object in a class that implements auto-retry of 551364750dSJames Henderson failed calls. 561364750dSJames Henderson """ 571364750dSJames Henderson 581364750dSJames Henderson def __init__(self, raw): 591364750dSJames Henderson assert not isinstance(raw, ComObject), raw 60*f98ee40fSTobias Hieta self.__dict__["raw"] = raw 611364750dSJames Henderson 621364750dSJames Henderson def __str__(self): 631364750dSJames Henderson return self._call(self.raw.__str__) 641364750dSJames Henderson 651364750dSJames Henderson def __getattr__(self, key): 661364750dSJames Henderson if key in self.__dict__: 671364750dSJames Henderson return self.__dict__[key] 681364750dSJames Henderson return self._call(self.raw.__getattr__, key) 691364750dSJames Henderson 701364750dSJames Henderson def __setattr__(self, key, val): 711364750dSJames Henderson if key in self.__dict__: 721364750dSJames Henderson self.__dict__[key] = val 731364750dSJames Henderson self._call(self.raw.__setattr__, key, val) 741364750dSJames Henderson 751364750dSJames Henderson def __getitem__(self, key): 761364750dSJames Henderson return self._call(self.raw.__getitem__, key) 771364750dSJames Henderson 781364750dSJames Henderson def __setitem__(self, key, val): 791364750dSJames Henderson self._call(self.raw.__setitem__, key, val) 801364750dSJames Henderson 811364750dSJames Henderson def __call__(self, *args): 821364750dSJames Henderson return self._call(self.raw, *args) 831364750dSJames Henderson 841364750dSJames Henderson @classmethod 851364750dSJames Henderson def _call(cls, fn, *args): 861364750dSJames Henderson """COM calls tend to randomly fail due to thread sync issues. 871364750dSJames Henderson The Microsoft recommended solution is to set up a message filter object 881364750dSJames Henderson to automatically retry failed calls, but this seems prohibitively hard 891364750dSJames Henderson from python, so this is a custom solution to do the same thing. 901364750dSJames Henderson All COM accesses should go through this function. 911364750dSJames Henderson """ 921364750dSJames Henderson ex = AssertionError("this should never be raised!") 931364750dSJames Henderson 94*f98ee40fSTobias Hieta assert ( 95*f98ee40fSTobias Hieta inspect.isfunction(fn) or inspect.ismethod(fn) or inspect.isbuiltin(fn) 96*f98ee40fSTobias Hieta ), (fn, type(fn)) 971364750dSJames Henderson retries = ([0] * 50) + ([1] * 5) 981364750dSJames Henderson for r in retries: 991364750dSJames Henderson try: 1001364750dSJames Henderson try: 1011364750dSJames Henderson result = fn(*args) 102*f98ee40fSTobias Hieta if inspect.ismethod(result) or "win32com" in str(result.__class__): 1031364750dSJames Henderson result = ComObject(result) 1041364750dSJames Henderson return result 1051364750dSJames Henderson except _com_error as e: 1061364750dSJames Henderson msg, _ = _handle_com_error(e) 1071364750dSJames Henderson e = WindowsError(msg) # pylint: disable=undefined-variable 1081364750dSJames Henderson raise e 1091364750dSJames Henderson except (AttributeError, TypeError, OSError) as e: 1101364750dSJames Henderson ex = e 1111364750dSJames Henderson time.sleep(r) 1121364750dSJames Henderson raise ex 1131364750dSJames Henderson 1141364750dSJames Henderson 1151364750dSJames Hendersonclass DTE(ComObject): 1161364750dSJames Henderson def __init__(self, class_string): 1171364750dSJames Henderson try: 1181364750dSJames Henderson super(DTE, self).__init__(com.DispatchEx(class_string)) 1191364750dSJames Henderson except _com_error as e: 1201364750dSJames Henderson msg, exc = _handle_com_error(e) 1211364750dSJames Henderson raise LoadDebuggerException( 122*f98ee40fSTobias Hieta "{} [{}]".format(msg, class_string), orig_exception=exc 123*f98ee40fSTobias Hieta ) 124