124 lines · python
1# DExTer : Debugging Experience Tester2# ~~~~~~ ~ ~~ ~ ~~3#4# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.5# See https://llvm.org/LICENSE.txt for license information.6# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception7"""Communication via the Windows COM interface."""8 9import inspect10import time11import sys12 13# pylint: disable=import-error14import win32com.client as com15import win32api16 17# pylint: enable=import-error18 19from dex.utils.Exceptions import LoadDebuggerException20 21_com_error = com.pywintypes.com_error # pylint: disable=no-member22 23 24def get_file_version(file_):25 try:26 info = win32api.GetFileVersionInfo(file_, "\\")27 ms = info["FileVersionMS"]28 ls = info["FileVersionLS"]29 return ".".join(30 str(s)31 for s in [32 win32api.HIWORD(ms),33 win32api.LOWORD(ms),34 win32api.HIWORD(ls),35 win32api.LOWORD(ls),36 ]37 )38 except com.pywintypes.error: # pylint: disable=no-member39 return "no versioninfo present"40 41 42def _handle_com_error(e):43 exc = sys.exc_info()44 msg = win32api.FormatMessage(e.hresult)45 try:46 msg = msg.decode("CP1251")47 except AttributeError:48 pass49 msg = msg.strip()50 return msg, exc51 52 53class ComObject(object):54 """Wrap a raw Windows COM object in a class that implements auto-retry of55 failed calls.56 """57 58 def __init__(self, raw):59 assert not isinstance(raw, ComObject), raw60 self.__dict__["raw"] = raw61 62 def __str__(self):63 return self._call(self.raw.__str__)64 65 def __getattr__(self, key):66 if key in self.__dict__:67 return self.__dict__[key]68 return self._call(self.raw.__getattr__, key)69 70 def __setattr__(self, key, val):71 if key in self.__dict__:72 self.__dict__[key] = val73 self._call(self.raw.__setattr__, key, val)74 75 def __getitem__(self, key):76 return self._call(self.raw.__getitem__, key)77 78 def __setitem__(self, key, val):79 self._call(self.raw.__setitem__, key, val)80 81 def __call__(self, *args):82 return self._call(self.raw, *args)83 84 @classmethod85 def _call(cls, fn, *args):86 """COM calls tend to randomly fail due to thread sync issues.87 The Microsoft recommended solution is to set up a message filter object88 to automatically retry failed calls, but this seems prohibitively hard89 from python, so this is a custom solution to do the same thing.90 All COM accesses should go through this function.91 """92 ex = AssertionError("this should never be raised!")93 94 assert (95 inspect.isfunction(fn) or inspect.ismethod(fn) or inspect.isbuiltin(fn)96 ), (fn, type(fn))97 retries = ([0] * 50) + ([1] * 5)98 for r in retries:99 try:100 try:101 result = fn(*args)102 if inspect.ismethod(result) or "win32com" in str(result.__class__):103 result = ComObject(result)104 return result105 except _com_error as e:106 msg, _ = _handle_com_error(e)107 e = WindowsError(msg) # pylint: disable=undefined-variable108 raise e109 except (AttributeError, TypeError, OSError) as e:110 ex = e111 time.sleep(r)112 raise ex113 114 115class DTE(ComObject):116 def __init__(self, class_string):117 try:118 super(DTE, self).__init__(com.DispatchEx(class_string))119 except _com_error as e:120 msg, exc = _handle_com_error(e)121 raise LoadDebuggerException(122 "{} [{}]".format(msg, class_string), orig_exception=exc123 )124