changeset 13375:f1fa8f481c7c

port win32.py to using the Python ctypes library The pywin32 package is no longer needed. ctypes is now required for running Mercurial on Windows. ctypes is included in Python since version 2.5. For Python 2.4, ctypes is available as an extra installer package for Windows. Moved spawndetached() from windows.py to win32.py and fixed it, using ctypes as well. spawndetached was defunct with Python 2.6.6 because Python removed their undocumented subprocess.CreateProcess. This fixes 'hg serve -d' on Windows.
author Adrian Buehlmann <adrian@cadifra.com>
date Mon, 14 Feb 2011 11:12:26 +0100
parents 1c613c1ae43d
children 60b5c6c3fd12
files contrib/wix/dist.wxs contrib/wix/guids.wxi mercurial/posix.py mercurial/util.py mercurial/win32.py mercurial/windows.py
diffstat 6 files changed, 286 insertions(+), 183 deletions(-) [+]
line wrap: on
line diff
--- a/contrib/wix/dist.wxs	Mon Feb 14 11:12:22 2011 +0100
+++ b/contrib/wix/dist.wxs	Mon Feb 14 11:12:26 2011 +0100
@@ -16,23 +16,14 @@
         <File Name="mercurial.parsers.pyd" />
         <File Name="pyexpat.pyd" />
         <File Name="python26.dll" />
-        <File Name="pythoncom26.dll" />
-        <File Name="pywintypes26.dll" />
         <File Name="bz2.pyd" />
         <File Name="select.pyd" />
         <File Name="unicodedata.pyd" />
-        <File Name="win32api.pyd" />
-        <File Name="win32com.shell.shell.pyd" />
-        <File Name="win32console.pyd" />
-        <File Name="win32file.pyd" />
-        <File Name="win32gui.pyd" />
-        <File Name="win32pipe.pyd" />
-        <File Name="win32process.pyd" />
+        <File Name="_ctypes.pyd" />
         <File Name="_elementtree.pyd" />
         <File Name="_hashlib.pyd" />
         <File Name="_socket.pyd" />
         <File Name="_ssl.pyd" />
-        <File Name="_win32sysloader.pyd" />
       </Component>
     </DirectoryRef>
   </Fragment>
--- a/contrib/wix/guids.wxi	Mon Feb 14 11:12:22 2011 +0100
+++ b/contrib/wix/guids.wxi	Mon Feb 14 11:12:26 2011 +0100
@@ -9,7 +9,7 @@
   <?define contrib.vim.guid = {BB04903A-652D-4C4F-9590-2BD07A2304F2} ?>
 
   <!-- dist.wxs -->
-  <?define dist.guid = {0F63D160-0740-4BAF-BF25-0C6930310F51} ?>
+  <?define dist.guid = {C3B634A4-1B05-4A40-94A9-38EE853CF693} ?>
 
   <!-- doc.wxs -->
   <?define doc.hg.1.html.guid = {AAAA3FDA-EDC5-4220-B59D-D342722358A2} ?>
--- a/mercurial/posix.py	Mon Feb 14 11:12:22 2011 +0100
+++ b/mercurial/posix.py	Mon Feb 14 11:12:26 2011 +0100
@@ -13,6 +13,7 @@
 nulldev = '/dev/null'
 normpath = os.path.normpath
 samestat = os.path.samestat
+os_link = os.link
 unlink = os.unlink
 rename = os.rename
 expandglobs = False
@@ -24,6 +25,10 @@
     '''return true if it is safe to hold open file handles to hardlinks'''
     return True
 
+def nlinks(name):
+    '''return number of hardlinks for the given file'''
+    return os.lstat(name).st_nlink
+
 def rcfiles(path):
     rcs = [os.path.join(path, 'hgrc')]
     rcdir = os.path.join(path, 'hgrc.d')
--- a/mercurial/util.py	Mon Feb 14 11:12:22 2011 +0100
+++ b/mercurial/util.py	Mon Feb 14 11:12:26 2011 +0100
@@ -554,16 +554,6 @@
         # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
         self.auditeddir.update(prefixes)
 
-def nlinks(pathname):
-    """Return number of hardlinks for the given file."""
-    return os.lstat(pathname).st_nlink
-
-if hasattr(os, 'link'):
-    os_link = os.link
-else:
-    def os_link(src, dst):
-        raise OSError(0, _("Hardlinks not supported"))
-
 def lookup_reg(key, name=None, scope=None):
     return None
 
--- a/mercurial/win32.py	Mon Feb 14 11:12:22 2011 +0100
+++ b/mercurial/win32.py	Mon Feb 14 11:12:26 2011 +0100
@@ -5,74 +5,173 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-"""Utility functions that use win32 API.
+import osutil, encoding
+import ctypes, errno, os, struct, subprocess
+
+_kernel32 = ctypes.windll.kernel32
+
+_BOOL = ctypes.c_long
+_WORD = ctypes.c_ushort
+_DWORD = ctypes.c_ulong
+_LPCSTR = _LPSTR = ctypes.c_char_p
+_HANDLE = ctypes.c_void_p
+_HWND = _HANDLE
+
+_INVALID_HANDLE_VALUE = -1
+
+# GetLastError
+_ERROR_SUCCESS = 0
+_ERROR_INVALID_PARAMETER = 87
+_ERROR_INSUFFICIENT_BUFFER = 122
+
+# WPARAM is defined as UINT_PTR (unsigned type)
+# LPARAM is defined as LONG_PTR (signed type)
+if ctypes.sizeof(ctypes.c_long) == ctypes.sizeof(ctypes.c_void_p):
+    _WPARAM = ctypes.c_ulong
+    _LPARAM = ctypes.c_long
+elif ctypes.sizeof(ctypes.c_longlong) == ctypes.sizeof(ctypes.c_void_p):
+    _WPARAM = ctypes.c_ulonglong
+    _LPARAM = ctypes.c_longlong
+
+class _FILETIME(ctypes.Structure):
+    _fields_ = [('dwLowDateTime', _DWORD),
+                ('dwHighDateTime', _DWORD)]
+
+class _BY_HANDLE_FILE_INFORMATION(ctypes.Structure):
+    _fields_ = [('dwFileAttributes', _DWORD),
+                ('ftCreationTime', _FILETIME),
+                ('ftLastAccessTime', _FILETIME),
+                ('ftLastWriteTime', _FILETIME),
+                ('dwVolumeSerialNumber', _DWORD),
+                ('nFileSizeHigh', _DWORD),
+                ('nFileSizeLow', _DWORD),
+                ('nNumberOfLinks', _DWORD),
+                ('nFileIndexHigh', _DWORD),
+                ('nFileIndexLow', _DWORD)]
+
+# CreateFile 
+_FILE_SHARE_READ = 0x00000001
+_FILE_SHARE_WRITE = 0x00000002
+_FILE_SHARE_DELETE = 0x00000004
+
+_OPEN_EXISTING = 3
+
+# Process Security and Access Rights
+_PROCESS_QUERY_INFORMATION = 0x0400
+
+# GetExitCodeProcess
+_STILL_ACTIVE = 259
+
+# registry
+_HKEY_CURRENT_USER = 0x80000001L
+_HKEY_LOCAL_MACHINE = 0x80000002L
+_KEY_READ = 0x20019
+_REG_SZ = 1
+_REG_DWORD = 4
 
-Mark Hammond's win32all package allows better functionality on
-Windows. This module overrides definitions in util.py. If not
-available, import of this module will fail, and generic code will be
-used.
-"""
+class _STARTUPINFO(ctypes.Structure):
+    _fields_ = [('cb', _DWORD),
+                ('lpReserved', _LPSTR),
+                ('lpDesktop', _LPSTR),
+                ('lpTitle', _LPSTR),
+                ('dwX', _DWORD),
+                ('dwY', _DWORD),
+                ('dwXSize', _DWORD),
+                ('dwYSize', _DWORD),
+                ('dwXCountChars', _DWORD),
+                ('dwYCountChars', _DWORD),
+                ('dwFillAttribute', _DWORD),
+                ('dwFlags', _DWORD),
+                ('wShowWindow', _WORD),
+                ('cbReserved2', _WORD),
+                ('lpReserved2', ctypes.c_char_p),
+                ('hStdInput', _HANDLE),
+                ('hStdOutput', _HANDLE),
+                ('hStdError', _HANDLE)]
+
+class _PROCESS_INFORMATION(ctypes.Structure):
+    _fields_ = [('hProcess', _HANDLE),
+                ('hThread', _HANDLE),
+                ('dwProcessId', _DWORD),
+                ('dwThreadId', _DWORD)]
+
+_DETACHED_PROCESS = 0x00000008
+_STARTF_USESHOWWINDOW = 0x00000001
+_SW_HIDE = 0
 
-import win32api
+class _COORD(ctypes.Structure):
+    _fields_ = [('X', ctypes.c_short),
+                ('Y', ctypes.c_short)]
+
+class _SMALL_RECT(ctypes.Structure):
+    _fields_ = [('Left', ctypes.c_short),
+                ('Top', ctypes.c_short),
+                ('Right', ctypes.c_short),
+                ('Bottom', ctypes.c_short)]
+
+class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
+    _fields_ = [('dwSize', _COORD),
+                ('dwCursorPosition', _COORD),
+                ('wAttributes', _WORD),
+                ('srWindow', _SMALL_RECT),
+                ('dwMaximumWindowSize', _COORD)]
 
-import errno, os, sys, pywintypes, win32con, win32file, win32process
-import winerror, win32gui, win32console
-import osutil, encoding
-from win32com.shell import shell, shellcon
+_STD_ERROR_HANDLE = 0xfffffff4L # (DWORD)-12
+
+def _raiseoserror(name):
+    err = ctypes.WinError()
+    raise OSError(err.errno, '%s: %s' % (name, err.strerror))
+
+def _getfileinfo(name):
+    fh = _kernel32.CreateFileA(name, 0,
+            _FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE,
+            None, _OPEN_EXISTING, 0, None)
+    if fh == _INVALID_HANDLE_VALUE:
+        _raiseoserror(name)
+    try:
+        fi = _BY_HANDLE_FILE_INFORMATION()
+        if not _kernel32.GetFileInformationByHandle(fh, ctypes.byref(fi)):
+            _raiseoserror(name)
+        return fi
+    finally:
+        _kernel32.CloseHandle(fh)
 
 def os_link(src, dst):
-    try:
-        win32file.CreateHardLink(dst, src)
-    except pywintypes.error:
-        raise OSError(errno.EINVAL, 'target implements hardlinks improperly')
-    except NotImplementedError: # Another fake error win Win98
-        raise OSError(errno.EINVAL, 'Hardlinking not supported')
+    if not _kernel32.CreateHardLinkA(dst, src, None):
+        _raiseoserror(src)
 
-def _getfileinfo(pathname):
-    """Return number of hardlinks for the given file."""
-    try:
-        fh = win32file.CreateFile(pathname, 0,
-            win32file.FILE_SHARE_READ | win32file.FILE_SHARE_WRITE |
-            win32file.FILE_SHARE_DELETE,
-            None, win32file.OPEN_EXISTING, 0, None)
-    except pywintypes.error:
-        raise OSError(errno.ENOENT, 'The system cannot find the file specified')
-    try:
-        return win32file.GetFileInformationByHandle(fh)
-    finally:
-        fh.Close()
-
-def nlinks(pathname):
-    """Return number of hardlinks for the given file."""
-    return _getfileinfo(pathname)[7]
+def nlinks(name):
+    '''return number of hardlinks for the given file'''
+    return _getfileinfo(name).nNumberOfLinks
 
 def samefile(fpath1, fpath2):
-    """Returns whether fpath1 and fpath2 refer to the same file. This is only
-    guaranteed to work for files, not directories."""
+    '''Returns whether fpath1 and fpath2 refer to the same file. This is only
+    guaranteed to work for files, not directories.'''
     res1 = _getfileinfo(fpath1)
     res2 = _getfileinfo(fpath2)
-    # Index 4 is the volume serial number, and 8 and 9 contain the file ID
-    return res1[4] == res2[4] and res1[8] == res2[8] and res1[9] == res2[9]
+    return (res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
+        and res1.nFileIndexHigh == res2.nFileIndexHigh
+        and res1.nFileIndexLow == res2.nFileIndexLow)
 
 def samedevice(fpath1, fpath2):
-    """Returns whether fpath1 and fpath2 are on the same device. This is only
-    guaranteed to work for files, not directories."""
+    '''Returns whether fpath1 and fpath2 are on the same device. This is only
+    guaranteed to work for files, not directories.'''
     res1 = _getfileinfo(fpath1)
     res2 = _getfileinfo(fpath2)
-    return res1[4] == res2[4]
+    return res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
 
 def testpid(pid):
     '''return True if pid is still running or unable to
     determine, False otherwise'''
-    try:
-        handle = win32api.OpenProcess(
-            win32con.PROCESS_QUERY_INFORMATION, False, pid)
-        if handle:
-            status = win32process.GetExitCodeProcess(handle)
-            return status == win32con.STILL_ACTIVE
-    except pywintypes.error, details:
-        return details[0] != winerror.ERROR_INVALID_PARAMETER
-    return True
+    h = _kernel32.OpenProcess(_PROCESS_QUERY_INFORMATION, False, pid)
+    if h:
+        try:
+            status = _DWORD()
+            if _kernel32.GetExitCodeProcess(h, ctypes.byref(status)):
+                return status.value == _STILL_ACTIVE
+        finally:
+            _kernel32.CloseHandle(h)
+    return _kernel32.GetLastError() != _ERROR_INVALID_PARAMETER
 
 def lookup_reg(key, valname=None, scope=None):
     ''' Look up a key/value name in the Windows registry.
@@ -83,101 +182,169 @@
     a sequence of scopes to look up in order. Default (CURRENT_USER,
     LOCAL_MACHINE).
     '''
-    try:
-        from _winreg import HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, \
-            QueryValueEx, OpenKey
-    except ImportError:
-        return None
-
+    adv = ctypes.windll.advapi32
+    byref = ctypes.byref
     if scope is None:
-        scope = (HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE)
+        scope = (_HKEY_CURRENT_USER, _HKEY_LOCAL_MACHINE)
     elif not isinstance(scope, (list, tuple)):
         scope = (scope,)
     for s in scope:
+        kh = _HANDLE()
+        res = adv.RegOpenKeyExA(s, key, 0, _KEY_READ, ctypes.byref(kh))
+        if res != _ERROR_SUCCESS:
+            continue
         try:
-            val = QueryValueEx(OpenKey(s, key), valname)[0]
-            # never let a Unicode string escape into the wild
-            return encoding.tolocal(val.encode('UTF-8'))
-        except EnvironmentError:
-            pass
+            size = _DWORD(600)
+            type = _DWORD()
+            buf = ctypes.create_string_buffer(size.value + 1)
+            res = adv.RegQueryValueExA(kh.value, valname, None,
+                                       byref(type), buf, byref(size))
+            if res != _ERROR_SUCCESS:
+                continue
+            if type.value == _REG_SZ:
+                # never let a Unicode string escape into the wild
+                return encoding.tolocal(buf.value.encode('UTF-8'))
+            elif type.value == _REG_DWORD:
+                fmt = '<L'
+                s = ctypes.string_at(byref(buf), struct.calcsize(fmt))
+                return struct.unpack(fmt, s)[0]
+        finally:
+            adv.RegCloseKey(kh.value)
 
 def system_rcpath_win32():
     '''return default os-specific hgrc search path'''
-    filename = win32api.GetModuleFileName(0)
+    rcpath = []
+    size = 600
+    buf = ctypes.create_string_buffer(size + 1)
+    len = _kernel32.GetModuleFileNameA(None, ctypes.byref(buf), size)
+    if len == 0:
+        raise ctypes.WinError()
+    elif len == size:
+        raise ctypes.WinError(_ERROR_INSUFFICIENT_BUFFER)
+    filename = buf.value
     # Use mercurial.ini found in directory with hg.exe
     progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
     if os.path.isfile(progrc):
-        return [progrc]
+        rcpath.append(progrc)
+        return rcpath
     # Use hgrc.d found in directory with hg.exe
     progrcd = os.path.join(os.path.dirname(filename), 'hgrc.d')
     if os.path.isdir(progrcd):
-        rcpath = []
         for f, kind in osutil.listdir(progrcd):
             if f.endswith('.rc'):
                 rcpath.append(os.path.join(progrcd, f))
         return rcpath
     # else look for a system rcpath in the registry
-    try:
-        value = win32api.RegQueryValue(
-                win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Mercurial')
-        rcpath = []
-        for p in value.split(os.pathsep):
-            if p.lower().endswith('mercurial.ini'):
-                rcpath.append(p)
-            elif os.path.isdir(p):
-                for f, kind in osutil.listdir(p):
-                    if f.endswith('.rc'):
-                        rcpath.append(os.path.join(p, f))
+    value = lookup_reg('SOFTWARE\\Mercurial', None, _HKEY_LOCAL_MACHINE)
+    if not isinstance(value, str) or not value:
         return rcpath
-    except pywintypes.error:
-        return []
+    value = value.replace('/', os.sep)
+    for p in value.split(os.pathsep):
+        if p.lower().endswith('mercurial.ini'):
+            rcpath.append(p)
+        elif os.path.isdir(p):
+            for f, kind in osutil.listdir(p):
+                if f.endswith('.rc'):
+                    rcpath.append(os.path.join(p, f))
+    return rcpath
 
 def user_rcpath_win32():
     '''return os-specific hgrc search path to the user dir'''
     userdir = os.path.expanduser('~')
-    if sys.getwindowsversion()[3] != 2 and userdir == '~':
-        # We are on win < nt: fetch the APPDATA directory location and use
-        # the parent directory as the user home dir.
-        appdir = shell.SHGetPathFromIDList(
-            shell.SHGetSpecialFolderLocation(0, shellcon.CSIDL_APPDATA))
-        userdir = os.path.dirname(appdir)
     return [os.path.join(userdir, 'mercurial.ini'),
             os.path.join(userdir, '.hgrc')]
 
 def getuser():
     '''return name of current user'''
-    return win32api.GetUserName()
+    adv = ctypes.windll.advapi32
+    size = _DWORD(300)
+    buf = ctypes.create_string_buffer(size.value + 1)
+    if not adv.GetUserNameA(ctypes.byref(buf), ctypes.byref(size)):
+        raise ctypes.WinError()
+    return buf.value
 
-def set_signal_handler_win32():
-    """Register a termination handler for console events including
+_SIGNAL_HANDLER = ctypes.WINFUNCTYPE(_BOOL, _DWORD)
+_signal_handler = []
+
+def set_signal_handler():
+    '''Register a termination handler for console events including
     CTRL+C. python signal handlers do not work well with socket
     operations.
-    """
+    '''
     def handler(event):
-        win32process.ExitProcess(1)
-    win32api.SetConsoleCtrlHandler(handler)
+        _kernel32.ExitProcess(1)
+
+    if _signal_handler:
+        return # already registered
+    h = _SIGNAL_HANDLER(handler)
+    _signal_handler.append(h) # needed to prevent garbage collection
+    if not _kernel32.SetConsoleCtrlHandler(h, True):
+        raise ctypes.WinError()
+
+_WNDENUMPROC = ctypes.WINFUNCTYPE(_BOOL, _HWND, _LPARAM)
 
 def hidewindow():
-    def callback(*args, **kwargs):
-        hwnd, pid = args
-        wpid = win32process.GetWindowThreadProcessId(hwnd)[1]
-        if pid == wpid:
-            win32gui.ShowWindow(hwnd, win32con.SW_HIDE)
+    user32 = ctypes.windll.user32
 
-    pid =  win32process.GetCurrentProcessId()
-    win32gui.EnumWindows(callback, pid)
+    def callback(hwnd, pid):
+        wpid = _DWORD()
+        user32.GetWindowThreadProcessId(hwnd, ctypes.byref(wpid))
+        if pid == wpid.value:
+            user32.ShowWindow(hwnd, _SW_HIDE)
+            return False # stop enumerating windows
+        return True
+
+    pid = _kernel32.GetCurrentProcessId()
+    user32.EnumWindows(_WNDENUMPROC(callback), pid)
 
 def termwidth():
-    try:
-        # Query stderr to avoid problems with redirections
-        screenbuf = win32console.GetStdHandle(win32console.STD_ERROR_HANDLE)
-        if screenbuf is None:
-            return 79
-        try:
-            window = screenbuf.GetConsoleScreenBufferInfo()['Window']
-            width = window.Right - window.Left
-            return width
-        finally:
-            screenbuf.Detach()
-    except pywintypes.error:
-        return 79
+    # cmd.exe does not handle CR like a unix console, the CR is
+    # counted in the line length. On 80 columns consoles, if 80
+    # characters are written, the following CR won't apply on the
+    # current line but on the new one. Keep room for it.
+    width = 79
+    # Query stderr to avoid problems with redirections
+    screenbuf = _kernel32.GetStdHandle(
+                  _STD_ERROR_HANDLE) # don't close the handle returned
+    if screenbuf is None or screenbuf == _INVALID_HANDLE_VALUE:
+        return width
+    csbi = _CONSOLE_SCREEN_BUFFER_INFO()
+    if not _kernel32.GetConsoleScreenBufferInfo(
+                        screenbuf, ctypes.byref(csbi)):
+        return width
+    width = csbi.srWindow.Right - csbi.srWindow.Left
+    return width
+
+def spawndetached(args):
+    # No standard library function really spawns a fully detached
+    # process under win32 because they allocate pipes or other objects
+    # to handle standard streams communications. Passing these objects
+    # to the child process requires handle inheritance to be enabled
+    # which makes really detached processes impossible.
+    si = _STARTUPINFO()
+    si.cb = ctypes.sizeof(_STARTUPINFO)
+    si.dwFlags = _STARTF_USESHOWWINDOW
+    si.wShowWindow = _SW_HIDE
+
+    pi = _PROCESS_INFORMATION()
+
+    env = ''
+    for k in os.environ:
+        env += "%s=%s\0" % (k, os.environ[k])
+    if not env:
+        env = '\0'
+    env += '\0'
+
+    args = subprocess.list2cmdline(args)
+    # Not running the command in shell mode makes python26 hang when
+    # writing to hgweb output socket.
+    comspec = os.environ.get("COMSPEC", "cmd.exe")
+    args = comspec + " /c " + args
+
+    res = _kernel32.CreateProcessA(
+        None, args, None, None, False, _DETACHED_PROCESS,
+        env, os.getcwd(), ctypes.byref(si), ctypes.byref(pi))
+    if not res:
+        raise ctypes.WinError()
+
+    return pi.dwProcessId
--- a/mercurial/windows.py	Mon Feb 14 11:12:22 2011 +0100
+++ b/mercurial/windows.py	Mon Feb 14 11:12:26 2011 +0100
@@ -71,7 +71,7 @@
         return 'command' in os.environ.get('comspec', '')
 
 def openhardlinks():
-    return not _is_win_9x() and "win32api" in globals()
+    return not _is_win_9x()
 
 def system_rcpath():
     try:
@@ -106,10 +106,6 @@
     args = user and ("%s@%s" % (user, host)) or host
     return port and ("%s %s %s" % (args, pflag, port)) or args
 
-def testpid(pid):
-    '''return False if pid dead, True if running or not known'''
-    return True
-
 def set_flags(f, l, x):
     pass
 
@@ -208,12 +204,6 @@
             return executable
     return findexisting(os.path.expanduser(os.path.expandvars(command)))
 
-def set_signal_handler():
-    try:
-        set_signal_handler_win32()
-    except NameError:
-        pass
-
 def statfiles(files):
     '''Stat each file in files and yield stat or None if file does not exist.
     Cluster and cache stat per directory to minimize number of OS stat calls.'''
@@ -241,11 +231,6 @@
             cache = dircache.setdefault(dir, dmap)
         yield cache.get(base, None)
 
-def getuser():
-    '''return name of current user'''
-    raise error.Abort(_('user name not available - set USERNAME '
-                       'environment variable'))
-
 def username(uid=None):
     """Return the name of the user with the given uid.
 
@@ -335,37 +320,6 @@
         unlink(dst)
         os.rename(src, dst)
 
-def spawndetached(args):
-    # No standard library function really spawns a fully detached
-    # process under win32 because they allocate pipes or other objects
-    # to handle standard streams communications. Passing these objects
-    # to the child process requires handle inheritance to be enabled
-    # which makes really detached processes impossible.
-    class STARTUPINFO:
-        dwFlags = subprocess.STARTF_USESHOWWINDOW
-        hStdInput = None
-        hStdOutput = None
-        hStdError = None
-        wShowWindow = subprocess.SW_HIDE
-
-    args = subprocess.list2cmdline(args)
-    # Not running the command in shell mode makes python26 hang when
-    # writing to hgweb output socket.
-    comspec = os.environ.get("COMSPEC", "cmd.exe")
-    args = comspec + " /c " + args
-    hp, ht, pid, tid = subprocess.CreateProcess(
-        None, args,
-        # no special security
-        None, None,
-        # Do not inherit handles
-        0,
-        # DETACHED_PROCESS
-        0x00000008,
-        os.environ,
-        os.getcwd(),
-        STARTUPINFO())
-    return pid
-
 def gethgcmd():
     return [sys.executable] + sys.argv[:1]
 
@@ -380,10 +334,6 @@
     # Don't support groups on Windows for now
     raise KeyError()
 
-try:
-    # override functions with win32 versions if possible
-    from win32 import *
-except ImportError:
-    pass
+from win32 import *
 
 expandglobs = True