/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-06-03 23:48:08 UTC
  • mfrom: (7316 work)
  • mto: This revision was merged to the branch mainline in revision 7328.
  • Revision ID: jelmer@jelmer.uk-20190603234808-15yk5c7054tj8e2b
Merge trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005-2010 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
"""Win32-specific helper functions
 
18
 
 
19
Only one dependency: ctypes should be installed.
 
20
"""
 
21
 
 
22
from __future__ import absolute_import
 
23
 
 
24
import glob
 
25
import operator
 
26
import os
 
27
import struct
 
28
import sys
 
29
 
 
30
from breezy import (
 
31
    cmdline,
 
32
    )
 
33
from breezy.i18n import gettext
 
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...
 
66
 
 
67
# Special Win32 API constants
 
68
# Handles of std streams
 
69
WIN32_STDIN_HANDLE = -10
 
70
WIN32_STDOUT_HANDLE = -11
 
71
WIN32_STDERR_HANDLE = -12
 
72
 
 
73
# CSIDL constants (from MSDN 2003)
 
74
CSIDL_APPDATA = 0x001A      # Application Data folder
 
75
# <user name>\Local Settings\Application Data (non roaming)
 
76
CSIDL_LOCAL_APPDATA = 0x001c
 
77
CSIDL_PERSONAL = 0x0005     # My Documents folder
 
78
 
 
79
# from winapi C headers
 
80
MAX_PATH = 260
 
81
UNLEN = 256
 
82
MAX_COMPUTERNAME_LENGTH = 31
 
83
 
 
84
# Registry data type ids
 
85
REG_SZ = 1
 
86
REG_EXPAND_SZ = 2
 
87
 
 
88
 
 
89
def debug_memory_win32api(message='', short=True):
 
90
    """Use trace.note() to dump the running memory info."""
 
91
    from breezy import trace
 
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'))
 
135
        return
 
136
    if short:
 
137
        # using base-2 units (see HACKING.txt).
 
138
        trace.note(gettext('WorkingSize {0:>7}KiB'
 
139
                           '\tPeakWorking {1:>7}KiB\t{2}').format(
 
140
                   info['WorkingSetSize'] / 1024,
 
141
                   info['PeakWorkingSetSize'] / 1024,
 
142
                   message))
 
143
        return
 
144
    if message:
 
145
        trace.note('%s', message)
 
146
    trace.note(gettext('WorkingSize       %8d KiB'),
 
147
               info['WorkingSetSize'] / 1024)
 
148
    trace.note(gettext('PeakWorking       %8d KiB'),
 
149
               info['PeakWorkingSetSize'] / 1024)
 
150
    trace.note(gettext('PagefileUsage     %8d KiB'),
 
151
               info.get('PagefileUsage', 0) / 1024)
 
152
    trace.note(gettext('PeakPagefileUsage %8d KiB'),
 
153
               info.get('PeakPagefileUsage', 0) / 1024)
 
154
    trace.note(gettext('PrivateUsage      %8d KiB'),
 
155
               info.get('PrivateUsage', 0) / 1024)
 
156
    trace.note(gettext('PageFaultCount    %8d'), info.get('PageFaultCount', 0))
 
157
 
 
158
 
 
159
def get_console_size(defaultx=80, defaulty=25):
 
160
    """Return size of current console.
 
161
 
 
162
    This function try to determine actual size of current working
 
163
    console window and return tuple (sizex, sizey) if success,
 
164
    or default size (defaultx, defaulty) otherwise.
 
165
    """
 
166
    if not has_ctypes:
 
167
        # no ctypes is found
 
168
        return (defaultx, defaulty)
 
169
 
 
170
    # To avoid problem with redirecting output via pipe
 
171
    # we need to use stderr instead of stdout
 
172
    h = ctypes.windll.kernel32.GetStdHandle(WIN32_STDERR_HANDLE)
 
173
    csbi = ctypes.create_string_buffer(22)
 
174
    res = ctypes.windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)
 
175
 
 
176
    if res:
 
177
        (bufx, bufy, curx, cury, wattr,
 
178
         left, top, right, bottom, maxx, maxy) = struct.unpack(
 
179
            "hhhhHhhhhhh", csbi.raw)
 
180
        sizex = right - left + 1
 
181
        sizey = bottom - top + 1
 
182
        return (sizex, sizey)
 
183
    else:
 
184
        return (defaultx, defaulty)
 
185
 
 
186
 
 
187
def _get_sh_special_folder_path(csidl):
 
188
    """Call SHGetSpecialFolderPathW if available, or return None.
 
189
 
 
190
    Result is always unicode (or None).
 
191
    """
 
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
 
220
 
 
221
 
 
222
def get_appdata_location():
 
223
    """Return Application Data location.
 
224
    Return None if we cannot obtain location.
 
225
 
 
226
    Windows defines two 'Application Data' folders per user - a 'roaming'
 
227
    one that moves with the user as they logon to different machines, and
 
228
    a 'local' one that stays local to the machine.  This returns the 'roaming'
 
229
    directory, and thus is suitable for storing user-preferences, etc.
 
230
    """
 
231
    appdata = _get_sh_special_folder_path(CSIDL_APPDATA)
 
232
    if appdata:
 
233
        return appdata
 
234
    # Use APPDATA if defined, will return None if not
 
235
    return get_environ_unicode('APPDATA')
 
236
 
 
237
 
 
238
def get_local_appdata_location():
 
239
    """Return Local Application Data location.
 
240
    Return the same as get_appdata_location() if we cannot obtain location.
 
241
 
 
242
    Windows defines two 'Application Data' folders per user - a 'roaming'
 
243
    one that moves with the user as they logon to different machines, and
 
244
    a 'local' one that stays local to the machine.  This returns the 'local'
 
245
    directory, and thus is suitable for caches, temp files and other things
 
246
    which don't need to move with the user.
 
247
    """
 
248
    local = _get_sh_special_folder_path(CSIDL_LOCAL_APPDATA)
 
249
    if local:
 
250
        return local
 
251
    # Vista supplies LOCALAPPDATA, but XP and earlier do not.
 
252
    local = get_environ_unicode('LOCALAPPDATA')
 
253
    if local:
 
254
        return local
 
255
    return get_appdata_location()
 
256
 
 
257
 
 
258
def get_home_location():
 
259
    """Return user's home location.
 
260
    Assume on win32 it's the <My Documents> folder.
 
261
    If location cannot be obtained return system drive root,
 
262
    i.e. C:\
 
263
    """
 
264
    home = _get_sh_special_folder_path(CSIDL_PERSONAL)
 
265
    if home:
 
266
        return home
 
267
    home = get_environ_unicode('HOME')
 
268
    if home is not None:
 
269
        return home
 
270
    homepath = get_environ_unicode('HOMEPATH')
 
271
    if homepath is not None:
 
272
        return os.path.join(get_environ_unicode('HOMEDIR', ''), home)
 
273
    # at least return windows root directory
 
274
    windir = get_environ_unicode('WINDIR')
 
275
    if windir:
 
276
        return os.path.splitdrive(windir)[0] + '/'
 
277
    # otherwise C:\ is good enough for 98% users
 
278
    return u'C:/'
 
279
 
 
280
 
 
281
def get_user_name():
 
282
    """Return user name as login name.
 
283
    If name cannot be obtained return None.
 
284
    """
 
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)
 
296
    # otherwise try env variables
 
297
    return get_environ_unicode('USERNAME')
 
298
 
 
299
 
 
300
# 1 == ComputerNameDnsHostname, which returns "The DNS host name of the local
 
301
# computer or the cluster associated with the local computer."
 
302
_WIN32_ComputerNameDnsHostname = 1
 
303
 
 
304
 
 
305
def get_host_name():
 
306
    """Return host machine name.
 
307
    If name cannot be obtained return None.
 
308
 
 
309
    :return: A unicode string representing the host name.
 
310
    """
 
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')
 
344
 
 
345
 
 
346
def _ensure_with_dir(path):
 
347
    if (not os.path.split(path)[0] or path.startswith(u'*')
 
348
            or path.startswith(u'?')):
 
349
        return u'./' + path, True
 
350
    else:
 
351
        return path, False
 
352
 
 
353
 
 
354
def _undo_ensure_with_dir(path, corrected):
 
355
    if corrected:
 
356
        return path[2:]
 
357
    else:
 
358
        return path
 
359
 
 
360
 
 
361
def glob_one(possible_glob):
 
362
    """Same as glob.glob().
 
363
 
 
364
    work around bugs in glob.glob()
 
365
    - Python bug #1001604 ("glob doesn't return unicode with ...")
 
366
    - failing expansion for */* with non-iso-8859-* chars
 
367
    """
 
368
    corrected_glob, corrected = _ensure_with_dir(possible_glob)
 
369
    glob_files = glob.glob(corrected_glob)
 
370
 
 
371
    if not glob_files:
 
372
        # special case to let the normal code path handle
 
373
        # files that do not exist, etc.
 
374
        glob_files = [possible_glob]
 
375
    elif corrected:
 
376
        glob_files = [_undo_ensure_with_dir(elem, corrected)
 
377
                      for elem in glob_files]
 
378
    return [elem.replace(u'\\', u'/') for elem in glob_files]
 
379
 
 
380
 
 
381
def glob_expand(file_list):
 
382
    """Replacement for glob expansion by the shell.
 
383
 
 
384
    Win32's cmd.exe does not do glob expansion (eg ``*.py``), so we do our own
 
385
    here.
 
386
 
 
387
    :param file_list: A list of filenames which may include shell globs.
 
388
    :return: An expanded list of filenames.
 
389
 
 
390
    Introduced in breezy 0.18.
 
391
    """
 
392
    if not file_list:
 
393
        return []
 
394
    expanded_file_list = []
 
395
    for possible_glob in file_list:
 
396
        expanded_file_list.extend(glob_one(possible_glob))
 
397
    return expanded_file_list
 
398
 
 
399
 
 
400
def get_app_path(appname):
 
401
    r"""Look up in Windows registry for full path to application executable.
 
402
    Typically, applications create subkey with their basename
 
403
    in HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\
 
404
 
 
405
    :param  appname:    name of application (if no filename extension
 
406
                        is specified, .exe used)
 
407
    :return:    full path to aplication executable from registry,
 
408
                or appname itself if nothing found.
 
409
    """
 
410
    import _winreg
 
411
 
 
412
    basename = appname
 
413
    if not os.path.splitext(basename)[1]:
 
414
        basename = appname + '.exe'
 
415
 
 
416
    try:
 
417
        hkey = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE,
 
418
                               'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\' +
 
419
                               basename)
 
420
    except EnvironmentError:
 
421
        return appname
 
422
 
 
423
    try:
 
424
        try:
 
425
            path, type_id = _winreg.QueryValueEx(hkey, '')
 
426
        except WindowsError:
 
427
            return appname
 
428
    finally:
 
429
        _winreg.CloseKey(hkey)
 
430
 
 
431
    if type_id == REG_SZ:
 
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
 
438
    return appname
 
439
 
 
440
 
 
441
def set_file_attr_hidden(path):
 
442
    """Set file attributes to hidden if possible"""
 
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)
 
450
 
 
451
 
 
452
def _command_line_to_argv(command_line, argv, single_quotes_allowed=False):
 
453
    """Convert a Unicode command line into a list of argv arguments.
 
454
 
 
455
    It performs wildcard expansion to make wildcards act closer to how they
 
456
    work in posix shells, versus how they work by default on Windows. Quoted
 
457
    arguments are left untouched.
 
458
 
 
459
    :param command_line: The unicode string to split into an arg list.
 
460
    :param single_quotes_allowed: Whether single quotes are accepted as quoting
 
461
                                  characters like double quotes. False by
 
462
                                  default.
 
463
    :return: A list of unicode strings.
 
464
    """
 
465
    # First, split the command line
 
466
    s = cmdline.Splitter(
 
467
        command_line, single_quotes_allowed=single_quotes_allowed)
 
468
 
 
469
    # Bug #587868 Now make sure that the length of s agrees with sys.argv
 
470
    # we do this by simply counting the number of arguments in each. The counts should
 
471
    # agree no matter what encoding sys.argv is in (AFAIK)
 
472
    # len(arguments) < len(sys.argv) should be an impossibility since python gets
 
473
    # args from the very same PEB as does GetCommandLineW
 
474
    arguments = list(s)
 
475
 
 
476
    # Now shorten the command line we get from GetCommandLineW to match sys.argv
 
477
    if len(arguments) < len(argv):
 
478
        raise AssertionError("Split command line can't be shorter than argv")
 
479
    arguments = arguments[len(arguments) - len(argv):]
 
480
 
 
481
    # Carry on to process globs (metachars) in the command line
 
482
    # expand globs if necessary
 
483
    # TODO: Use 'globbing' instead of 'glob.glob', this gives us stuff like
 
484
    #       '**/' style globs
 
485
    args = []
 
486
    for is_quoted, arg in arguments:
 
487
        if is_quoted or not glob.has_magic(arg):
 
488
            args.append(arg)
 
489
        else:
 
490
            args.extend(glob_one(arg))
 
491
    return args
 
492
 
 
493
 
 
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
 
582
    return False