/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/win32utils.py

  • Committer: Jelmer Vernooij
  • Date: 2019-03-05 07:32:38 UTC
  • mto: (7290.1.21 work)
  • mto: This revision was merged to the branch mainline in revision 7311.
  • Revision ID: jelmer@jelmer.uk-20190305073238-zlqn981opwnqsmzi
Add appveyor configuration.

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
Only one dependency: ctypes should be installed.
20
20
"""
21
21
 
 
22
from __future__ import absolute_import
 
23
 
22
24
import glob
 
25
import operator
23
26
import os
24
27
import struct
25
28
import sys
29
32
    )
30
33
from breezy.i18n import gettext
31
34
 
 
35
# We can cope without it; use a separate variable to help pyflakes
 
36
try:
 
37
    import ctypes
 
38
    has_ctypes = True
 
39
except ImportError:
 
40
    has_ctypes = False
 
41
else:
 
42
    create_buffer = ctypes.create_unicode_buffer
 
43
    extract_buffer = operator.attrgetter("value")
 
44
    suffix = 'W'
 
45
try:
 
46
    import pywintypes
 
47
    has_pywintypes = True
 
48
except ImportError:
 
49
    has_pywintypes = has_win32file = has_win32api = False
 
50
else:
 
51
    try:
 
52
        import win32file
 
53
        has_win32file = True
 
54
    except ImportError:
 
55
        has_win32file = False
 
56
    try:
 
57
        import win32api
 
58
        has_win32api = True
 
59
    except ImportError:
 
60
        has_win32api = False
 
61
 
 
62
# pulling in win32com.shell is a bit of overhead, and normally we don't need
 
63
# it as ctypes is preferred and common.  lazy_imports and "optional"
 
64
# modules don't work well, so we do our own lazy thing...
 
65
has_win32com_shell = None  # Set to True or False once we know for sure...
32
66
 
33
67
# Special Win32 API constants
34
68
# Handles of std streams
54
88
 
55
89
def debug_memory_win32api(message='', short=True):
56
90
    """Use trace.note() to dump the running memory info."""
57
 
    import ctypes
58
91
    from breezy import trace
59
 
    class PROCESS_MEMORY_COUNTERS_EX(ctypes.Structure):
60
 
        """Used by GetProcessMemoryInfo"""
61
 
        _fields_ = [('cb', ctypes.c_ulong),
62
 
                    ('PageFaultCount', ctypes.c_ulong),
63
 
                    ('PeakWorkingSetSize', ctypes.c_size_t),
64
 
                    ('WorkingSetSize', ctypes.c_size_t),
65
 
                    ('QuotaPeakPagedPoolUsage', ctypes.c_size_t),
66
 
                    ('QuotaPagedPoolUsage', ctypes.c_size_t),
67
 
                    ('QuotaPeakNonPagedPoolUsage', ctypes.c_size_t),
68
 
                    ('QuotaNonPagedPoolUsage', ctypes.c_size_t),
69
 
                    ('PagefileUsage', ctypes.c_size_t),
70
 
                    ('PeakPagefileUsage', ctypes.c_size_t),
71
 
                    ('PrivateUsage', ctypes.c_size_t),
72
 
                    ]
73
 
    cur_process = ctypes.windll.kernel32.GetCurrentProcess()
74
 
    mem_struct = PROCESS_MEMORY_COUNTERS_EX()
75
 
    ret = ctypes.windll.psapi.GetProcessMemoryInfo(
76
 
        cur_process, ctypes.byref(mem_struct), ctypes.sizeof(mem_struct))
77
 
    if not ret:
78
 
        trace.note(gettext('Failed to GetProcessMemoryInfo()'))
 
92
    if has_ctypes:
 
93
        class PROCESS_MEMORY_COUNTERS_EX(ctypes.Structure):
 
94
            """Used by GetProcessMemoryInfo"""
 
95
            _fields_ = [('cb', ctypes.c_ulong),
 
96
                        ('PageFaultCount', ctypes.c_ulong),
 
97
                        ('PeakWorkingSetSize', ctypes.c_size_t),
 
98
                        ('WorkingSetSize', ctypes.c_size_t),
 
99
                        ('QuotaPeakPagedPoolUsage', ctypes.c_size_t),
 
100
                        ('QuotaPagedPoolUsage', ctypes.c_size_t),
 
101
                        ('QuotaPeakNonPagedPoolUsage', ctypes.c_size_t),
 
102
                        ('QuotaNonPagedPoolUsage', ctypes.c_size_t),
 
103
                        ('PagefileUsage', ctypes.c_size_t),
 
104
                        ('PeakPagefileUsage', ctypes.c_size_t),
 
105
                        ('PrivateUsage', ctypes.c_size_t),
 
106
                        ]
 
107
        cur_process = ctypes.windll.kernel32.GetCurrentProcess()
 
108
        mem_struct = PROCESS_MEMORY_COUNTERS_EX()
 
109
        ret = ctypes.windll.psapi.GetProcessMemoryInfo(
 
110
            cur_process, ctypes.byref(mem_struct), ctypes.sizeof(mem_struct))
 
111
        if not ret:
 
112
            trace.note(gettext('Failed to GetProcessMemoryInfo()'))
 
113
            return
 
114
        info = {'PageFaultCount': mem_struct.PageFaultCount,
 
115
                'PeakWorkingSetSize': mem_struct.PeakWorkingSetSize,
 
116
                'WorkingSetSize': mem_struct.WorkingSetSize,
 
117
                'QuotaPeakPagedPoolUsage': mem_struct.QuotaPeakPagedPoolUsage,
 
118
                'QuotaPagedPoolUsage': mem_struct.QuotaPagedPoolUsage,
 
119
                'QuotaPeakNonPagedPoolUsage':
 
120
                    mem_struct.QuotaPeakNonPagedPoolUsage,
 
121
                'QuotaNonPagedPoolUsage': mem_struct.QuotaNonPagedPoolUsage,
 
122
                'PagefileUsage': mem_struct.PagefileUsage,
 
123
                'PeakPagefileUsage': mem_struct.PeakPagefileUsage,
 
124
                'PrivateUsage': mem_struct.PrivateUsage,
 
125
                }
 
126
    elif has_win32api:
 
127
        import win32process
 
128
        # win32process does not return PrivateUsage, because it doesn't use
 
129
        # PROCESS_MEMORY_COUNTERS_EX (it uses the one without _EX).
 
130
        proc = win32process.GetCurrentProcess()
 
131
        info = win32process.GetProcessMemoryInfo(proc)
 
132
    else:
 
133
        trace.note(gettext('Cannot debug memory on win32 without ctypes'
 
134
                           ' or win32process'))
79
135
        return
80
 
    info = {'PageFaultCount': mem_struct.PageFaultCount,
81
 
            'PeakWorkingSetSize': mem_struct.PeakWorkingSetSize,
82
 
            'WorkingSetSize': mem_struct.WorkingSetSize,
83
 
            'QuotaPeakPagedPoolUsage': mem_struct.QuotaPeakPagedPoolUsage,
84
 
            'QuotaPagedPoolUsage': mem_struct.QuotaPagedPoolUsage,
85
 
            'QuotaPeakNonPagedPoolUsage':
86
 
                mem_struct.QuotaPeakNonPagedPoolUsage,
87
 
            'QuotaNonPagedPoolUsage': mem_struct.QuotaNonPagedPoolUsage,
88
 
            'PagefileUsage': mem_struct.PagefileUsage,
89
 
            'PeakPagefileUsage': mem_struct.PeakPagefileUsage,
90
 
            'PrivateUsage': mem_struct.PrivateUsage,
91
 
            }
92
136
    if short:
93
137
        # using base-2 units (see HACKING.txt).
94
138
        trace.note(gettext('WorkingSize {0:>7}KiB'
119
163
    console window and return tuple (sizex, sizey) if success,
120
164
    or default size (defaultx, defaulty) otherwise.
121
165
    """
122
 
    import ctypes
 
166
    if not has_ctypes:
 
167
        # no ctypes is found
 
168
        return (defaultx, defaulty)
 
169
 
123
170
    # To avoid problem with redirecting output via pipe
124
171
    # we need to use stderr instead of stdout
125
172
    h = ctypes.windll.kernel32.GetStdHandle(WIN32_STDERR_HANDLE)
142
189
 
143
190
    Result is always unicode (or None).
144
191
    """
145
 
    import ctypes
146
 
    try:
147
 
        SHGetSpecialFolderPath = \
148
 
            ctypes.windll.shell32.SHGetSpecialFolderPathW
149
 
    except AttributeError:
150
 
        pass
151
 
    else:
152
 
        buf = ctypes.create_unicode_buffer(MAX_PATH)
153
 
        if SHGetSpecialFolderPath(None, buf, csidl, 0):
154
 
            return buf.value
 
192
    if has_ctypes:
 
193
        try:
 
194
            SHGetSpecialFolderPath = \
 
195
                ctypes.windll.shell32.SHGetSpecialFolderPathW
 
196
        except AttributeError:
 
197
            pass
 
198
        else:
 
199
            buf = ctypes.create_unicode_buffer(MAX_PATH)
 
200
            if SHGetSpecialFolderPath(None, buf, csidl, 0):
 
201
                return buf.value
 
202
 
 
203
    global has_win32com_shell
 
204
    if has_win32com_shell is None:
 
205
        try:
 
206
            from win32com.shell import shell
 
207
            has_win32com_shell = True
 
208
        except ImportError:
 
209
            has_win32com_shell = False
 
210
    if has_win32com_shell:
 
211
        # still need to bind the name locally, but this is fast.
 
212
        from win32com.shell import shell
 
213
        try:
 
214
            return shell.SHGetSpecialFolderPath(0, csidl, 0)
 
215
        except shell.error:
 
216
            # possibly E_NOTIMPL meaning we can't load the function pointer,
 
217
            # or E_FAIL meaning the function failed - regardless, just ignore it
 
218
            pass
 
219
    return None
155
220
 
156
221
 
157
222
def get_appdata_location():
167
232
    if appdata:
168
233
        return appdata
169
234
    # Use APPDATA if defined, will return None if not
170
 
    return os.environ.get('APPDATA')
 
235
    return get_environ_unicode('APPDATA')
171
236
 
172
237
 
173
238
def get_local_appdata_location():
184
249
    if local:
185
250
        return local
186
251
    # Vista supplies LOCALAPPDATA, but XP and earlier do not.
187
 
    local = os.environ.get('LOCALAPPDATA')
 
252
    local = get_environ_unicode('LOCALAPPDATA')
188
253
    if local:
189
254
        return local
190
255
    return get_appdata_location()
199
264
    home = _get_sh_special_folder_path(CSIDL_PERSONAL)
200
265
    if home:
201
266
        return home
202
 
    home = os.environ.get('HOME')
 
267
    home = get_environ_unicode('HOME')
203
268
    if home is not None:
204
269
        return home
205
 
    homepath = os.environ.get('HOMEPATH')
 
270
    homepath = get_environ_unicode('HOMEPATH')
206
271
    if homepath is not None:
207
 
        return os.path.join(os.environ.get('HOMEDIR', ''), home)
 
272
        return os.path.join(get_environ_unicode('HOMEDIR', ''), home)
208
273
    # at least return windows root directory
209
 
    windir = os.environ.get('WINDIR')
 
274
    windir = get_environ_unicode('WINDIR')
210
275
    if windir:
211
276
        return os.path.splitdrive(windir)[0] + '/'
212
277
    # otherwise C:\ is good enough for 98% users
217
282
    """Return user name as login name.
218
283
    If name cannot be obtained return None.
219
284
    """
220
 
    try:
221
 
        advapi32 = ctypes.windll.advapi32
222
 
        GetUserName = getattr(advapi32, 'GetUserNameW')
223
 
    except AttributeError:
224
 
        pass
225
 
    else:
226
 
        buf = ctypes.create_unicode_buffer(UNLEN + 1)
227
 
        n = ctypes.c_int(UNLEN + 1)
228
 
        if GetUserName(buf, ctypes.byref(n)):
229
 
            return buf.value
 
285
    if has_ctypes:
 
286
        try:
 
287
            advapi32 = ctypes.windll.advapi32
 
288
            GetUserName = getattr(advapi32, 'GetUserName' + suffix)
 
289
        except AttributeError:
 
290
            pass
 
291
        else:
 
292
            buf = create_buffer(UNLEN + 1)
 
293
            n = ctypes.c_int(UNLEN + 1)
 
294
            if GetUserName(buf, ctypes.byref(n)):
 
295
                return extract_buffer(buf)
230
296
    # otherwise try env variables
231
 
    return os.environ.get('USERNAME')
 
297
    return get_environ_unicode('USERNAME')
232
298
 
233
299
 
234
300
# 1 == ComputerNameDnsHostname, which returns "The DNS host name of the local
242
308
 
243
309
    :return: A unicode string representing the host name.
244
310
    """
245
 
    import ctypes
246
 
    buf = ctypes.create_unicode_buffer(MAX_COMPUTERNAME_LENGTH + 1)
247
 
    n = ctypes.c_int(MAX_COMPUTERNAME_LENGTH + 1)
248
 
 
249
 
    # Try GetComputerNameEx which gives a proper Unicode hostname
250
 
    GetComputerNameEx = getattr(
251
 
        ctypes.windll.kernel32, 'GetComputerNameExW', None)
252
 
    if (GetComputerNameEx is not None
253
 
        and GetComputerNameEx(_WIN32_ComputerNameDnsHostname,
254
 
                              buf, ctypes.byref(n))):
255
 
        return buf.value
256
 
    return os.environ.get('COMPUTERNAME')
 
311
    if has_win32api:
 
312
        try:
 
313
            return win32api.GetComputerNameEx(_WIN32_ComputerNameDnsHostname)
 
314
        except (NotImplementedError, win32api.error):
 
315
            # NotImplemented will happen on win9x...
 
316
            pass
 
317
    if has_ctypes:
 
318
        try:
 
319
            kernel32 = ctypes.windll.kernel32
 
320
        except AttributeError:
 
321
            pass  # Missing the module we need
 
322
        else:
 
323
            buf = create_buffer(MAX_COMPUTERNAME_LENGTH + 1)
 
324
            n = ctypes.c_int(MAX_COMPUTERNAME_LENGTH + 1)
 
325
 
 
326
            # Try GetComputerNameEx which gives a proper Unicode hostname
 
327
            GetComputerNameEx = getattr(kernel32, 'GetComputerNameEx' + suffix,
 
328
                                        None)
 
329
            if (GetComputerNameEx is not None
 
330
                and GetComputerNameEx(_WIN32_ComputerNameDnsHostname,
 
331
                                      buf, ctypes.byref(n))):
 
332
                return extract_buffer(buf)
 
333
 
 
334
            # Try GetComputerName in case GetComputerNameEx wasn't found
 
335
            # It returns the NETBIOS name, which isn't as good, but still ok.
 
336
            # The first GetComputerNameEx might have changed 'n', so reset it
 
337
            n = ctypes.c_int(MAX_COMPUTERNAME_LENGTH + 1)
 
338
            GetComputerName = getattr(kernel32, 'GetComputerName' + suffix,
 
339
                                      None)
 
340
            if (GetComputerName is not None
 
341
                    and GetComputerName(buf, ctypes.byref(n))):
 
342
                return extract_buffer(buf)
 
343
    return get_environ_unicode('COMPUTERNAME')
257
344
 
258
345
 
259
346
def _ensure_with_dir(path):
343
430
 
344
431
    if type_id == REG_SZ:
345
432
        return path
 
433
    if type_id == REG_EXPAND_SZ and has_win32api:
 
434
        fullpath = win32api.ExpandEnvironmentStrings(path)
 
435
        if len(fullpath) > 1 and fullpath[0] == '"' and fullpath[-1] == '"':
 
436
            fullpath = fullpath[1:-1]   # remove quotes around value
 
437
        return fullpath
346
438
    return appname
347
439
 
348
440
 
349
441
def set_file_attr_hidden(path):
350
442
    """Set file attributes to hidden if possible"""
351
 
    from ctypes.wintypes import BOOL, DWORD, LPWSTR
352
 
    import ctypes
353
 
    # <https://docs.microsoft.com/windows/desktop/api/fileapi/nf-fileapi-setfileattributesw>
354
 
    SetFileAttributes = ctypes.windll.kernel32.SetFileAttributesW
355
 
    SetFileAttributes.argtypes = LPWSTR, DWORD
356
 
    SetFileAttributes.restype = BOOL
357
 
    FILE_ATTRIBUTE_HIDDEN = 2
358
 
    if not SetFileAttributes(path, FILE_ATTRIBUTE_HIDDEN):
359
 
        e = ctypes.WinError()
360
 
        from . import trace
361
 
        trace.mutter('Unable to set hidden attribute on %r: %s', path, e)
 
443
    if has_win32file:
 
444
        SetFileAttributes = win32file.SetFileAttributesW
 
445
        try:
 
446
            SetFileAttributes(path, win32file.FILE_ATTRIBUTE_HIDDEN)
 
447
        except pywintypes.error as e:
 
448
            from . import trace
 
449
            trace.mutter('Unable to set hidden attribute on %r: %s', path, e)
362
450
 
363
451
 
364
452
def _command_line_to_argv(command_line, argv, single_quotes_allowed=False):
403
491
    return args
404
492
 
405
493
 
406
 
def _ctypes_is_local_pid_dead(pid):
407
 
    import ctypes
408
 
    kernel32 = ctypes.wintypes.windll.kernel32
409
 
    """True if pid doesn't correspond to live process on this machine"""
410
 
    handle = kernel32.OpenProcess(1, False, pid)
411
 
    if not handle:
412
 
        errorcode = ctypes.GetLastError()
413
 
        if errorcode == 5:  # ERROR_ACCESS_DENIED
414
 
            # Probably something alive we're not allowed to kill
415
 
            return False
416
 
        elif errorcode == 87:  # ERROR_INVALID_PARAMETER
417
 
            return True
418
 
        raise ctypes.WinError(errorcode)
419
 
    Kernel32.CloseHandle(handle)
 
494
if has_ctypes:
 
495
    def get_unicode_argv():
 
496
        prototype = ctypes.WINFUNCTYPE(ctypes.c_wchar_p)
 
497
        GetCommandLineW = prototype(("GetCommandLineW",
 
498
                                     ctypes.windll.kernel32))
 
499
        command_line = GetCommandLineW()
 
500
        if command_line is None:
 
501
            raise ctypes.WinError()
 
502
        # Skip the first argument, since we only care about parameters
 
503
        argv = _command_line_to_argv(command_line, sys.argv)[1:]
 
504
        return argv
 
505
 
 
506
    def get_environ_unicode(key, default=None):
 
507
        """Get `key` from environment as unicode or `default` if unset
 
508
 
 
509
        The environment is natively unicode on modern windows versions but
 
510
        Python 2 only accesses it through the legacy bytestring api.
 
511
 
 
512
        Environmental variable names are case insenstive on Windows.
 
513
 
 
514
        A large enough buffer will be allocated to retrieve the value, though
 
515
        it may take two calls to the underlying library function.
 
516
 
 
517
        This needs ctypes because pywin32 does not expose the wide version.
 
518
        """
 
519
        cfunc = getattr(get_environ_unicode, "_c_function", None)
 
520
        if cfunc is None:
 
521
            from ctypes.wintypes import DWORD, LPCWSTR, LPWSTR
 
522
            cfunc = ctypes.WINFUNCTYPE(DWORD, LPCWSTR, LPWSTR, DWORD)(
 
523
                ("GetEnvironmentVariableW", ctypes.windll.kernel32))
 
524
            get_environ_unicode._c_function = cfunc
 
525
        buffer_size = 256  # heuristic, 256 characters often enough
 
526
        while True:
 
527
            buffer = ctypes.create_unicode_buffer(buffer_size)
 
528
            length = cfunc(key, buffer, buffer_size)
 
529
            if not length:
 
530
                code = ctypes.GetLastError()
 
531
                if code == 203:  # ERROR_ENVVAR_NOT_FOUND
 
532
                    return default
 
533
                raise ctypes.WinError(code)
 
534
            if buffer_size > length:
 
535
                return buffer[:length]
 
536
            buffer_size = length
 
537
 
 
538
 
 
539
if has_win32api:
 
540
    def _pywin32_is_local_pid_dead(pid):
 
541
        """True if pid doesn't correspond to live process on this machine"""
 
542
        try:
 
543
            handle = win32api.OpenProcess(1, False, pid)  # PROCESS_TERMINATE
 
544
        except pywintypes.error as e:
 
545
            if e[0] == 5:  # ERROR_ACCESS_DENIED
 
546
                # Probably something alive we're not allowed to kill
 
547
                return False
 
548
            elif e[0] == 87:  # ERROR_INVALID_PARAMETER
 
549
                return True
 
550
            raise
 
551
        handle.close()
 
552
        return False
 
553
    is_local_pid_dead = _pywin32_is_local_pid_dead
 
554
elif has_ctypes and sys.platform == 'win32':
 
555
    from ctypes.wintypes import BOOL, DWORD, HANDLE
 
556
    _kernel32 = ctypes.windll.kernel32
 
557
    _CloseHandle = ctypes.WINFUNCTYPE(BOOL, HANDLE)(
 
558
        ("CloseHandle", _kernel32))
 
559
    _OpenProcess = ctypes.WINFUNCTYPE(HANDLE, DWORD, BOOL, DWORD)(
 
560
        ("OpenProcess", _kernel32))
 
561
 
 
562
    def _ctypes_is_local_pid_dead(pid):
 
563
        """True if pid doesn't correspond to live process on this machine"""
 
564
        handle = _OpenProcess(1, False, pid)  # PROCESS_TERMINATE
 
565
        if not handle:
 
566
            errorcode = ctypes.GetLastError()
 
567
            if errorcode == 5:  # ERROR_ACCESS_DENIED
 
568
                # Probably something alive we're not allowed to kill
 
569
                return False
 
570
            elif errorcode == 87:  # ERROR_INVALID_PARAMETER
 
571
                return True
 
572
            raise ctypes.WinError(errorcode)
 
573
        _CloseHandle(handle)
 
574
        return False
 
575
    is_local_pid_dead = _ctypes_is_local_pid_dead
 
576
 
 
577
 
 
578
def _is_pywintypes_error(evalue):
 
579
    """True if exception instance is an error from pywin32"""
 
580
    if has_pywintypes and isinstance(evalue, pywintypes.error):
 
581
        return True
420
582
    return False
421
 
 
422
 
is_local_pid_dead = _ctypes_is_local_pid_dead