• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# DExTer : Debugging Experience Tester
2# ~~~~~~   ~         ~~         ~   ~~
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-exception
7"""Communication via the Windows COM interface."""
8
9import inspect
10import time
11import sys
12
13# pylint: disable=import-error
14import win32com.client as com
15import win32api
16# pylint: enable=import-error
17
18from dex.utils.Exceptions import LoadDebuggerException
19
20_com_error = com.pywintypes.com_error  # pylint: disable=no-member
21
22
23def get_file_version(file_):
24    try:
25        info = win32api.GetFileVersionInfo(file_, '\\')
26        ms = info['FileVersionMS']
27        ls = info['FileVersionLS']
28        return '.'.join(
29            str(s) for s in [
30                win32api.HIWORD(ms),
31                win32api.LOWORD(ms),
32                win32api.HIWORD(ls),
33                win32api.LOWORD(ls)
34            ])
35    except com.pywintypes.error:  # pylint: disable=no-member
36        return 'no versioninfo present'
37
38
39def _handle_com_error(e):
40    exc = sys.exc_info()
41    msg = win32api.FormatMessage(e.hresult)
42    try:
43        msg = msg.decode('CP1251')
44    except AttributeError:
45        pass
46    msg = msg.strip()
47    return msg, exc
48
49
50class ComObject(object):
51    """Wrap a raw Windows COM object in a class that implements auto-retry of
52    failed calls.
53    """
54
55    def __init__(self, raw):
56        assert not isinstance(raw, ComObject), raw
57        self.__dict__['raw'] = raw
58
59    def __str__(self):
60        return self._call(self.raw.__str__)
61
62    def __getattr__(self, key):
63        if key in self.__dict__:
64            return self.__dict__[key]
65        return self._call(self.raw.__getattr__, key)
66
67    def __setattr__(self, key, val):
68        if key in self.__dict__:
69            self.__dict__[key] = val
70        self._call(self.raw.__setattr__, key, val)
71
72    def __getitem__(self, key):
73        return self._call(self.raw.__getitem__, key)
74
75    def __setitem__(self, key, val):
76        self._call(self.raw.__setitem__, key, val)
77
78    def __call__(self, *args):
79        return self._call(self.raw, *args)
80
81    @classmethod
82    def _call(cls, fn, *args):
83        """COM calls tend to randomly fail due to thread sync issues.
84        The Microsoft recommended solution is to set up a message filter object
85        to automatically retry failed calls, but this seems prohibitively hard
86        from python, so this is a custom solution to do the same thing.
87        All COM accesses should go through this function.
88        """
89        ex = AssertionError("this should never be raised!")
90
91        assert (inspect.isfunction(fn) or inspect.ismethod(fn)
92                or inspect.isbuiltin(fn)), (fn, type(fn))
93        retries = ([0] * 50) + ([1] * 5)
94        for r in retries:
95            try:
96                try:
97                    result = fn(*args)
98                    if inspect.ismethod(result) or 'win32com' in str(
99                            result.__class__):
100                        result = ComObject(result)
101                    return result
102                except _com_error as e:
103                    msg, _ = _handle_com_error(e)
104                    e = WindowsError(msg)  # pylint: disable=undefined-variable
105                    raise e
106            except (AttributeError, TypeError, OSError) as e:
107                ex = e
108                time.sleep(r)
109        raise ex
110
111
112class DTE(ComObject):
113    def __init__(self, class_string):
114        try:
115            super(DTE, self).__init__(com.DispatchEx(class_string))
116        except _com_error as e:
117            msg, exc = _handle_com_error(e)
118            raise LoadDebuggerException(
119                '{} [{}]'.format(msg, class_string), orig_exception=exc)
120