File: ComInterface.py

package info (click to toggle)
llvm-toolchain-18 1%3A18.1.8-18
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,908,340 kB
  • sloc: cpp: 6,667,937; ansic: 1,440,452; asm: 883,619; python: 230,549; objc: 76,880; f90: 74,238; lisp: 35,989; pascal: 16,571; sh: 10,229; perl: 7,459; ml: 5,047; awk: 3,523; makefile: 2,987; javascript: 2,149; xml: 892; fortran: 649; cs: 573
file content (123 lines) | stat: -rw-r--r-- 3,813 bytes parent folder | download | duplicates (23)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# DExTer : Debugging Experience Tester
# ~~~~~~   ~         ~~         ~   ~~
#
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
"""Communication via the Windows COM interface."""

import inspect
import time
import sys

# pylint: disable=import-error
import win32com.client as com
import win32api

# pylint: enable=import-error

from dex.utils.Exceptions import LoadDebuggerException

_com_error = com.pywintypes.com_error  # pylint: disable=no-member


def get_file_version(file_):
    try:
        info = win32api.GetFileVersionInfo(file_, "\\")
        ms = info["FileVersionMS"]
        ls = info["FileVersionLS"]
        return ".".join(
            str(s)
            for s in [
                win32api.HIWORD(ms),
                win32api.LOWORD(ms),
                win32api.HIWORD(ls),
                win32api.LOWORD(ls),
            ]
        )
    except com.pywintypes.error:  # pylint: disable=no-member
        return "no versioninfo present"


def _handle_com_error(e):
    exc = sys.exc_info()
    msg = win32api.FormatMessage(e.hresult)
    try:
        msg = msg.decode("CP1251")
    except AttributeError:
        pass
    msg = msg.strip()
    return msg, exc


class ComObject(object):
    """Wrap a raw Windows COM object in a class that implements auto-retry of
    failed calls.
    """

    def __init__(self, raw):
        assert not isinstance(raw, ComObject), raw
        self.__dict__["raw"] = raw

    def __str__(self):
        return self._call(self.raw.__str__)

    def __getattr__(self, key):
        if key in self.__dict__:
            return self.__dict__[key]
        return self._call(self.raw.__getattr__, key)

    def __setattr__(self, key, val):
        if key in self.__dict__:
            self.__dict__[key] = val
        self._call(self.raw.__setattr__, key, val)

    def __getitem__(self, key):
        return self._call(self.raw.__getitem__, key)

    def __setitem__(self, key, val):
        self._call(self.raw.__setitem__, key, val)

    def __call__(self, *args):
        return self._call(self.raw, *args)

    @classmethod
    def _call(cls, fn, *args):
        """COM calls tend to randomly fail due to thread sync issues.
        The Microsoft recommended solution is to set up a message filter object
        to automatically retry failed calls, but this seems prohibitively hard
        from python, so this is a custom solution to do the same thing.
        All COM accesses should go through this function.
        """
        ex = AssertionError("this should never be raised!")

        assert (
            inspect.isfunction(fn) or inspect.ismethod(fn) or inspect.isbuiltin(fn)
        ), (fn, type(fn))
        retries = ([0] * 50) + ([1] * 5)
        for r in retries:
            try:
                try:
                    result = fn(*args)
                    if inspect.ismethod(result) or "win32com" in str(result.__class__):
                        result = ComObject(result)
                    return result
                except _com_error as e:
                    msg, _ = _handle_com_error(e)
                    e = WindowsError(msg)  # pylint: disable=undefined-variable
                    raise e
            except (AttributeError, TypeError, OSError) as e:
                ex = e
                time.sleep(r)
        raise ex


class DTE(ComObject):
    def __init__(self, class_string):
        try:
            super(DTE, self).__init__(com.DispatchEx(class_string))
        except _com_error as e:
            msg, exc = _handle_com_error(e)
            raise LoadDebuggerException(
                "{} [{}]".format(msg, class_string), orig_exception=exc
            )