win_subprocess.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. import ctypes
  2. import subprocess
  3. import _subprocess
  4. import os
  5. from ctypes import byref, windll, c_char_p, c_wchar_p, c_void_p, \
  6. Structure, sizeof, c_wchar, WinError
  7. from ctypes.wintypes import BYTE, WORD, LPWSTR, BOOL, DWORD, LPVOID, \
  8. HANDLE
  9. ##
  10. ## Types
  11. ##
  12. CREATE_UNICODE_ENVIRONMENT = 0x00000400
  13. LPCTSTR = c_char_p
  14. LPTSTR = c_wchar_p
  15. LPSECURITY_ATTRIBUTES = c_void_p
  16. LPBYTE = ctypes.POINTER(BYTE)
  17. class STARTUPINFOW(Structure):
  18. _fields_ = [
  19. ("cb", DWORD), ("lpReserved", LPWSTR),
  20. ("lpDesktop", LPWSTR), ("lpTitle", LPWSTR),
  21. ("dwX", DWORD), ("dwY", DWORD),
  22. ("dwXSize", DWORD), ("dwYSize", DWORD),
  23. ("dwXCountChars", DWORD), ("dwYCountChars", DWORD),
  24. ("dwFillAtrribute", DWORD), ("dwFlags", DWORD),
  25. ("wShowWindow", WORD), ("cbReserved2", WORD),
  26. ("lpReserved2", LPBYTE), ("hStdInput", HANDLE),
  27. ("hStdOutput", HANDLE), ("hStdError", HANDLE),
  28. ]
  29. LPSTARTUPINFOW = ctypes.POINTER(STARTUPINFOW)
  30. class PROCESS_INFORMATION(Structure):
  31. _fields_ = [
  32. ("hProcess", HANDLE), ("hThread", HANDLE),
  33. ("dwProcessId", DWORD), ("dwThreadId", DWORD),
  34. ]
  35. LPPROCESS_INFORMATION = ctypes.POINTER(PROCESS_INFORMATION)
  36. class DUMMY_HANDLE(ctypes.c_void_p):
  37. def __init__(self, *a, **kw):
  38. super(DUMMY_HANDLE, self).__init__(*a, **kw)
  39. self.closed = False
  40. def Close(self):
  41. if not self.closed:
  42. windll.kernel32.CloseHandle(self)
  43. self.closed = True
  44. def __int__(self):
  45. return self.value
  46. CreateProcessW = windll.kernel32.CreateProcessW
  47. CreateProcessW.argtypes = [
  48. LPCTSTR, LPTSTR, LPSECURITY_ATTRIBUTES,
  49. LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCTSTR,
  50. LPSTARTUPINFOW, LPPROCESS_INFORMATION,
  51. ]
  52. CreateProcessW.restype = BOOL
  53. ##
  54. ## Patched functions/classes
  55. ##
  56. def CreateProcess(executable, args, _p_attr, _t_attr,
  57. inherit_handles, creation_flags, env, cwd,
  58. startup_info):
  59. """Create a process supporting unicode executable and args for win32
  60. Python implementation of CreateProcess using CreateProcessW for Win32
  61. """
  62. si = STARTUPINFOW(
  63. dwFlags=startup_info.dwFlags,
  64. wShowWindow=startup_info.wShowWindow,
  65. cb=sizeof(STARTUPINFOW),
  66. ## XXXvlab: not sure of the casting here to ints.
  67. hStdInput=int(startup_info.hStdInput),
  68. hStdOutput=int(startup_info.hStdOutput),
  69. hStdError=int(startup_info.hStdError),
  70. )
  71. wenv = None
  72. if env is not None:
  73. ## LPCWSTR seems to be c_wchar_p, so let's say CWSTR is c_wchar
  74. env = (unicode("").join([
  75. unicode("%s=%s\0") % (k, v)
  76. for k, v in env.items()])) + unicode("\0")
  77. wenv = (c_wchar * len(env))()
  78. wenv.value = env
  79. pi = PROCESS_INFORMATION()
  80. creation_flags |= CREATE_UNICODE_ENVIRONMENT
  81. if CreateProcessW(executable, args, None, None,
  82. inherit_handles, creation_flags,
  83. wenv, cwd, byref(si), byref(pi)):
  84. return (DUMMY_HANDLE(pi.hProcess), DUMMY_HANDLE(pi.hThread),
  85. pi.dwProcessId, pi.dwThreadId)
  86. raise WinError()
  87. class Popen(subprocess.Popen):
  88. """This superseeds Popen and corrects a bug in cPython 2.7 implem"""
  89. def _execute_child(self, args, executable, preexec_fn, close_fds,
  90. cwd, env, universal_newlines,
  91. startupinfo, creationflags, shell, to_close,
  92. p2cread, p2cwrite,
  93. c2pread, c2pwrite,
  94. errread, errwrite):
  95. """Code from part of _execute_child from Python 2.7 (9fbb65e)
  96. There are only 2 little changes concerning the construction of
  97. the the final string in shell mode: we preempt the creation of
  98. the command string when shell is True, because original function
  99. will try to encode unicode args which we want to avoid to be able to
  100. sending it as-is to ``CreateProcess``.
  101. """
  102. if not isinstance(args, subprocess.types.StringTypes):
  103. args = subprocess.list2cmdline(args)
  104. if startupinfo is None:
  105. startupinfo = subprocess.STARTUPINFO()
  106. if shell:
  107. startupinfo.dwFlags |= _subprocess.STARTF_USESHOWWINDOW
  108. startupinfo.wShowWindow = _subprocess.SW_HIDE
  109. comspec = os.environ.get("COMSPEC", unicode("cmd.exe"))
  110. args = unicode('{} /c "{}"').format(comspec, args)
  111. if (_subprocess.GetVersion() >= 0x80000000 or
  112. os.path.basename(comspec).lower() == "command.com"):
  113. w9xpopen = self._find_w9xpopen()
  114. args = unicode('"%s" %s') % (w9xpopen, args)
  115. creationflags |= _subprocess.CREATE_NEW_CONSOLE
  116. super(Popen, self)._execute_child(args, executable,
  117. preexec_fn, close_fds, cwd, env, universal_newlines,
  118. startupinfo, creationflags, False, to_close, p2cread,
  119. p2cwrite, c2pread, c2pwrite, errread, errwrite)
  120. _subprocess.CreateProcess = CreateProcess