236 lines
8.0 KiB
Python
236 lines
8.0 KiB
Python
# -*-python-*-
|
|
#
|
|
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
|
|
#
|
|
# By using this file, you agree to the terms and conditions set forth in
|
|
# the LICENSE.html file which can be found at the top level of the ViewVC
|
|
# distribution or at http://viewvc.org/license-1.html.
|
|
#
|
|
# For more information, visit http://viewvc.org/
|
|
#
|
|
# -----------------------------------------------------------------------
|
|
#
|
|
# Utilities for controlling processes and pipes on win32
|
|
#
|
|
# -----------------------------------------------------------------------
|
|
|
|
import os, sys, traceback, string, thread
|
|
try:
|
|
import win32api
|
|
except ImportError, e:
|
|
raise ImportError, str(e) + """
|
|
|
|
Did you install the Python for Windows Extensions?
|
|
|
|
http://sourceforge.net/projects/pywin32/
|
|
"""
|
|
|
|
import win32process, win32pipe, win32con
|
|
import win32event, win32file, winerror
|
|
import pywintypes, msvcrt
|
|
|
|
# Buffer size for spooling
|
|
SPOOL_BYTES = 4096
|
|
|
|
# File object to write error messages
|
|
SPOOL_ERROR = sys.stderr
|
|
#SPOOL_ERROR = open("m:/temp/error.txt", "wt")
|
|
|
|
def CommandLine(command, args):
|
|
"""Convert an executable path and a sequence of arguments into a command
|
|
line that can be passed to CreateProcess"""
|
|
|
|
cmd = "\"" + string.replace(command, "\"", "\"\"") + "\""
|
|
for arg in args:
|
|
cmd = cmd + " \"" + string.replace(arg, "\"", "\"\"") + "\""
|
|
return cmd
|
|
|
|
def CreateProcess(cmd, hStdInput, hStdOutput, hStdError):
|
|
"""Creates a new process which uses the specified handles for its standard
|
|
input, output, and error. The handles must be inheritable. 0 can be passed
|
|
as a special handle indicating that the process should inherit the current
|
|
process's input, output, or error streams, and None can be passed to discard
|
|
the child process's output or to prevent it from reading any input."""
|
|
|
|
# initialize new process's startup info
|
|
si = win32process.STARTUPINFO()
|
|
si.dwFlags = win32process.STARTF_USESTDHANDLES
|
|
|
|
if hStdInput == 0:
|
|
si.hStdInput = win32api.GetStdHandle(win32api.STD_INPUT_HANDLE)
|
|
else:
|
|
si.hStdInput = hStdInput
|
|
|
|
if hStdOutput == 0:
|
|
si.hStdOutput = win32api.GetStdHandle(win32api.STD_OUTPUT_HANDLE)
|
|
else:
|
|
si.hStdOutput = hStdOutput
|
|
|
|
if hStdError == 0:
|
|
si.hStdError = win32api.GetStdHandle(win32api.STD_ERROR_HANDLE)
|
|
else:
|
|
si.hStdError = hStdError
|
|
|
|
# create the process
|
|
phandle, pid, thandle, tid = win32process.CreateProcess \
|
|
( None, # appName
|
|
cmd, # commandLine
|
|
None, # processAttributes
|
|
None, # threadAttributes
|
|
1, # bInheritHandles
|
|
win32con.NORMAL_PRIORITY_CLASS, # dwCreationFlags
|
|
None, # newEnvironment
|
|
None, # currentDirectory
|
|
si # startupinfo
|
|
)
|
|
|
|
if hStdInput and hasattr(hStdInput, 'Close'):
|
|
hStdInput.Close()
|
|
|
|
if hStdOutput and hasattr(hStdOutput, 'Close'):
|
|
hStdOutput.Close()
|
|
|
|
if hStdError and hasattr(hStdError, 'Close'):
|
|
hStdError.Close()
|
|
|
|
return phandle, pid, thandle, tid
|
|
|
|
def CreatePipe(readInheritable, writeInheritable):
|
|
"""Create a new pipe specifying whether the read and write ends are
|
|
inheritable and whether they should be created for blocking or nonblocking
|
|
I/O."""
|
|
|
|
r, w = win32pipe.CreatePipe(None, SPOOL_BYTES)
|
|
if readInheritable:
|
|
r = MakeInheritedHandle(r)
|
|
if writeInheritable:
|
|
w = MakeInheritedHandle(w)
|
|
return r, w
|
|
|
|
def File2FileObject(pipe, mode):
|
|
"""Make a C stdio file object out of a win32 file handle"""
|
|
if string.find(mode, 'r') >= 0:
|
|
wmode = os.O_RDONLY
|
|
elif string.find(mode, 'w') >= 0:
|
|
wmode = os.O_WRONLY
|
|
if string.find(mode, 'b') >= 0:
|
|
wmode = wmode | os.O_BINARY
|
|
if string.find(mode, 't') >= 0:
|
|
wmode = wmode | os.O_TEXT
|
|
return os.fdopen(msvcrt.open_osfhandle(pipe.Detach(),wmode),mode)
|
|
|
|
def FileObject2File(fileObject):
|
|
"""Get the win32 file handle from a C stdio file object"""
|
|
return win32file._get_osfhandle(fileObject.fileno())
|
|
|
|
def DuplicateHandle(handle):
|
|
"""Duplicates a win32 handle."""
|
|
proc = win32api.GetCurrentProcess()
|
|
return win32api.DuplicateHandle(proc,handle,proc,0,0,win32con.DUPLICATE_SAME_ACCESS)
|
|
|
|
def MakePrivateHandle(handle, replace = 1):
|
|
"""Turn an inherited handle into a non inherited one. This avoids the
|
|
handle duplication that occurs on CreateProcess calls which can create
|
|
uncloseable pipes."""
|
|
|
|
### Could change implementation to use SetHandleInformation()...
|
|
|
|
flags = win32con.DUPLICATE_SAME_ACCESS
|
|
proc = win32api.GetCurrentProcess()
|
|
if replace: flags = flags | win32con.DUPLICATE_CLOSE_SOURCE
|
|
newhandle = win32api.DuplicateHandle(proc,handle,proc,0,0,flags)
|
|
if replace: handle.Detach() # handle was already deleted by the last call
|
|
return newhandle
|
|
|
|
def MakeInheritedHandle(handle, replace = 1):
|
|
"""Turn a private handle into an inherited one."""
|
|
|
|
### Could change implementation to use SetHandleInformation()...
|
|
|
|
flags = win32con.DUPLICATE_SAME_ACCESS
|
|
proc = win32api.GetCurrentProcess()
|
|
if replace: flags = flags | win32con.DUPLICATE_CLOSE_SOURCE
|
|
newhandle = win32api.DuplicateHandle(proc,handle,proc,0,1,flags)
|
|
if replace: handle.Detach() # handle was deleted by the last call
|
|
return newhandle
|
|
|
|
def MakeSpyPipe(readInheritable, writeInheritable, outFiles = None, doneEvent = None):
|
|
"""Return read and write handles to a pipe that asynchronously writes all of
|
|
its input to the files in the outFiles sequence. doneEvent can be None, or a
|
|
a win32 event handle that will be set when the write end of pipe is closed.
|
|
"""
|
|
|
|
if outFiles is None:
|
|
return CreatePipe(readInheritable, writeInheritable)
|
|
|
|
r, writeHandle = CreatePipe(0, writeInheritable)
|
|
if readInheritable is None:
|
|
readHandle, w = None, None
|
|
else:
|
|
readHandle, w = CreatePipe(readInheritable, 0)
|
|
|
|
thread.start_new_thread(SpoolWorker, (r, w, outFiles, doneEvent))
|
|
|
|
return readHandle, writeHandle
|
|
|
|
def SpoolWorker(srcHandle, destHandle, outFiles, doneEvent):
|
|
"""Thread entry point for implementation of MakeSpyPipe"""
|
|
try:
|
|
buffer = win32file.AllocateReadBuffer(SPOOL_BYTES)
|
|
|
|
while 1:
|
|
try:
|
|
#print >> SPOOL_ERROR, "Calling ReadFile..."; SPOOL_ERROR.flush()
|
|
hr, data = win32file.ReadFile(srcHandle, buffer)
|
|
#print >> SPOOL_ERROR, "ReadFile returned '%s', '%s'" % (str(hr), str(data)); SPOOL_ERROR.flush()
|
|
if hr != 0:
|
|
raise "win32file.ReadFile returned %i, '%s'" % (hr, data)
|
|
elif len(data) == 0:
|
|
break
|
|
except pywintypes.error, e:
|
|
#print >> SPOOL_ERROR, "ReadFile threw '%s'" % str(e); SPOOL_ERROR.flush()
|
|
if e.args[0] == winerror.ERROR_BROKEN_PIPE:
|
|
break
|
|
else:
|
|
raise e
|
|
|
|
#print >> SPOOL_ERROR, "Writing to %i file objects..." % len(outFiles); SPOOL_ERROR.flush()
|
|
for f in outFiles:
|
|
f.write(data)
|
|
#print >> SPOOL_ERROR, "Done writing to file objects."; SPOOL_ERROR.flush()
|
|
|
|
#print >> SPOOL_ERROR, "Writing to destination %s" % str(destHandle); SPOOL_ERROR.flush()
|
|
if destHandle:
|
|
#print >> SPOOL_ERROR, "Calling WriteFile..."; SPOOL_ERROR.flush()
|
|
hr, bytes = win32file.WriteFile(destHandle, data)
|
|
#print >> SPOOL_ERROR, "WriteFile() passed %i bytes and returned %i, %i" % (len(data), hr, bytes); SPOOL_ERROR.flush()
|
|
if hr != 0 or bytes != len(data):
|
|
raise "win32file.WriteFile() passed %i bytes and returned %i, %i" % (len(data), hr, bytes)
|
|
|
|
srcHandle.Close()
|
|
|
|
if doneEvent:
|
|
win32event.SetEvent(doneEvent)
|
|
|
|
if destHandle:
|
|
destHandle.Close()
|
|
|
|
except:
|
|
info = sys.exc_info()
|
|
SPOOL_ERROR.writelines(apply(traceback.format_exception, info), '')
|
|
SPOOL_ERROR.flush()
|
|
del info
|
|
|
|
def NullFile(inheritable):
|
|
"""Create a null file handle."""
|
|
if inheritable:
|
|
sa = pywintypes.SECURITY_ATTRIBUTES()
|
|
sa.bInheritHandle = 1
|
|
else:
|
|
sa = None
|
|
|
|
return win32file.CreateFile("nul",
|
|
win32file.GENERIC_READ | win32file.GENERIC_WRITE,
|
|
win32file.FILE_SHARE_READ | win32file.FILE_SHARE_WRITE,
|
|
sa, win32file.OPEN_EXISTING, 0, None)
|