win32spawn.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. import os
  2. import threading
  3. import Queue
  4. # Windows import
  5. import win32file
  6. import win32pipe
  7. import win32api
  8. import win32con
  9. import win32security
  10. import win32process
  11. import win32event
  12. class Win32Spawn(object):
  13. def __init__(self, cmd, shell=False):
  14. self.queue = Queue.Queue()
  15. self.is_terminated = False
  16. self.wake_up_event = win32event.CreateEvent(None, 0, 0, None)
  17. exec_dir = os.getcwd()
  18. comspec = os.environ.get("COMSPEC", "cmd.exe")
  19. cmd = comspec + ' /c ' + cmd
  20. win32event.ResetEvent(self.wake_up_event)
  21. currproc = win32api.GetCurrentProcess()
  22. sa = win32security.SECURITY_ATTRIBUTES()
  23. sa.bInheritHandle = 1
  24. child_stdout_rd, child_stdout_wr = win32pipe.CreatePipe(sa, 0)
  25. child_stdout_rd_dup = win32api.DuplicateHandle(currproc, child_stdout_rd, currproc, 0, 0, win32con.DUPLICATE_SAME_ACCESS)
  26. win32file.CloseHandle(child_stdout_rd)
  27. child_stderr_rd, child_stderr_wr = win32pipe.CreatePipe(sa, 0)
  28. child_stderr_rd_dup = win32api.DuplicateHandle(currproc, child_stderr_rd, currproc, 0, 0, win32con.DUPLICATE_SAME_ACCESS)
  29. win32file.CloseHandle(child_stderr_rd)
  30. child_stdin_rd, child_stdin_wr = win32pipe.CreatePipe(sa, 0)
  31. child_stdin_wr_dup = win32api.DuplicateHandle(currproc, child_stdin_wr, currproc, 0, 0, win32con.DUPLICATE_SAME_ACCESS)
  32. win32file.CloseHandle(child_stdin_wr)
  33. startup_info = win32process.STARTUPINFO()
  34. startup_info.hStdInput = child_stdin_rd
  35. startup_info.hStdOutput = child_stdout_wr
  36. startup_info.hStdError = child_stderr_wr
  37. startup_info.dwFlags = win32process.STARTF_USESTDHANDLES
  38. cr_flags = 0
  39. cr_flags = win32process.CREATE_NEW_PROCESS_GROUP
  40. env = os.environ.copy()
  41. self.h_process, h_thread, dw_pid, dw_tid = win32process.CreateProcess(None, cmd, None, None, 1,
  42. cr_flags, env, os.path.abspath(exec_dir),
  43. startup_info)
  44. win32api.CloseHandle(h_thread)
  45. win32file.CloseHandle(child_stdin_rd)
  46. win32file.CloseHandle(child_stdout_wr)
  47. win32file.CloseHandle(child_stderr_wr)
  48. self.__child_stdout = child_stdout_rd_dup
  49. self.__child_stderr = child_stderr_rd_dup
  50. self.__child_stdin = child_stdin_wr_dup
  51. self.exit_code = -1
  52. def close(self):
  53. win32file.CloseHandle(self.__child_stdout)
  54. win32file.CloseHandle(self.__child_stderr)
  55. win32file.CloseHandle(self.__child_stdin)
  56. win32api.CloseHandle(self.h_process)
  57. win32api.CloseHandle(self.wake_up_event)
  58. def kill_subprocess():
  59. win32event.SetEvent(self.wake_up_event)
  60. def sleep(secs):
  61. win32event.ResetEvent(self.wake_up_event)
  62. timeout = int(1000 * secs)
  63. val = win32event.WaitForSingleObject(self.wake_up_event, timeout)
  64. if val == win32event.WAIT_TIMEOUT:
  65. return True
  66. else:
  67. # The wake_up_event must have been signalled
  68. return False
  69. def get(self, block=True, timeout=None):
  70. return self.queue.get(block=block, timeout=timeout)
  71. def qsize(self):
  72. return self.queue.qsize()
  73. def __wait_for_child(self):
  74. # kick off threads to read from stdout and stderr of the child process
  75. threading.Thread(target=self.__do_read, args=(self.__child_stdout, )).start()
  76. threading.Thread(target=self.__do_read, args=(self.__child_stderr, )).start()
  77. while True:
  78. # block waiting for the process to finish or the interrupt to happen
  79. handles = (self.wake_up_event, self.h_process)
  80. val = win32event.WaitForMultipleObjects(handles, 0, win32event.INFINITE)
  81. if val >= win32event.WAIT_OBJECT_0 and val < win32event.WAIT_OBJECT_0 + len(handles):
  82. handle = handles[val - win32event.WAIT_OBJECT_0]
  83. if handle == self.wake_up_event:
  84. win32api.TerminateProcess(self.h_process, 1)
  85. win32event.ResetEvent(self.wake_up_event)
  86. return False
  87. elif handle == self.h_process:
  88. # the process has ended naturally
  89. return True
  90. else:
  91. assert False, "Unknown handle fired"
  92. else:
  93. assert False, "Unexpected return from WaitForMultipleObjects"
  94. # Wait for job to finish. Since this method blocks, it can to be called from another thread.
  95. # If the application wants to kill the process, it should call kill_subprocess().
  96. def wait(self):
  97. if not self.__wait_for_child():
  98. # it's been killed
  99. result = False
  100. else:
  101. # normal termination
  102. self.exit_code = win32process.GetExitCodeProcess(self.h_process)
  103. result = self.exit_code == 0
  104. self.close()
  105. self.is_terminated = True
  106. return result
  107. # This method gets called on a worker thread to read from either a stderr
  108. # or stdout thread from the child process.
  109. def __do_read(self, handle):
  110. bytesToRead = 1024
  111. while 1:
  112. try:
  113. finished = 0
  114. hr, data = win32file.ReadFile(handle, bytesToRead, None)
  115. if data:
  116. self.queue.put_nowait(data)
  117. except win32api.error:
  118. finished = 1
  119. if finished:
  120. return
  121. def start_pipe(self):
  122. def worker(pipe):
  123. return pipe.wait()
  124. thrd = threading.Thread(target=worker, args=(self, ))
  125. thrd.start()