D
Daniel Clark
I have a Windows command line based application that only shuts down
cleanly if it sees "CTRL-C" on the console. I need to automate the
running of this application, but still allow the user sitting at the
machine to cancel the process cleanly if he/she needs to. In Unix this
would be a tiny shell script that used "kill -15", but under Windows
there does not seem to be an easy way to do this, at least that I can
find.
Below is a test program, based on CreateProcess.py from "Python
Programming on Win32". The
win32api.GenerateConsoleCtrlEvent(win32con.CTRL_C_EVENT, pid) lines
don't seem to do anything. What they should do is nothing in the case
of notepad, and exit out of the dir builtin process in the case of the
cmd.exe process.
Any ideas on how to make this work?
# CreateProcessDc.py
#
# Demo of creating two processes using the CreateProcess API,
# then waiting for the processes to terminate.
import win32process
import win32event
import win32con
import win32api
import time
# Create a process specified by commandLine, and
# The process' window should be at position rect
# Returns the handle to the new process.
def CreateMyProcess(commandLine, rect):
# Create a STARTUPINFO object
si = win32process.STARTUPINFO()
# Set the position in the startup info.
si.dwX, si.dwY, si.dwXSize, si.dwYSize = rect
# And indicate which of the items are valid.
si.dwFlags = win32process.STARTF_USEPOSITION | \
win32process.STARTF_USESIZE
# Set Creation Flags
CreationFlags = win32process.CREATE_NEW_CONSOLE | \
win32process.CREATE_NEW_PROCESS_GROUP | \
win32process.NORMAL_PRIORITY_CLASS
# Rest of startup info is default, so we leave it alone.
# Create the process.
info = win32process.CreateProcess(
None, # AppName
commandLine, # Command line
None, # Process Security
None, # ThreadSecurity
0, # Inherit Handles?
CreationFlags,
None, # New environment
None, # Current directory
win32process.STARTUPINFO()) # startup info.
##si) # startup info.
# Return the handle to the process.
# Recall info is a tuple of (hProcess, hThread, processId,
threadId)
return (info[0], info[2])
def RunEm():
pids = []
handles = []
# First get the screen size to calculate layout.
screenX = win32api.GetSystemMetrics(win32con.SM_CXSCREEN)
screenY = win32api.GetSystemMetrics(win32con.SM_CYSCREEN)
# First instance will be on the left hand side of the screen.
rect = 0, 0, screenX/2, screenY
handle, pid = CreateMyProcess("notepad", rect)
handles.append(handle)
pids.append(pid)
# Second instance of Notepad will be on the right hand side.
rect = screenX/2+1, 0, screenX/2, screenY
cmd2 = "cmd /k dir /s/p c:\\"
handle, pid = CreateMyProcess(cmd2, rect)
handles.append(handle)
pids.append(pid)
# Now we have the processes, wait for them both
# to terminate.
# Rather than waiting the whole time, we loop 10 times,
# waiting for one second each time, printing a message
# each time around the loop
countdown = range(1,10)
countdown.reverse()
for i in countdown:
print "Waiting %d seconds for apps to close" % i
rc = win32event.WaitForMultipleObjects(
handles, # Objects to wait for.
1, # Wait for them all
1000) # timeout in milli-seconds.
if rc == win32event.WAIT_OBJECT_0:
# Our processes closed!
print "Our processes closed in time."
break
# else just continue around the loop.
else:
# We didn't break out of the for loop!
print "Giving up waiting - sending CTRL-C to processes"
for pid in pids:
try:
print "Sending CTRL-C to process with pid: " +
str(pid)
win32api.GenerateConsoleCtrlEvent(win32con.CTRL_C_EVENT, pid)
win32api.GenerateConsoleCtrlEvent(win32con.CTRL_C_EVENT, pid)
win32api.GenerateConsoleCtrlEvent(win32con.CTRL_C_EVENT, pid)
win32api.GenerateConsoleCtrlEvent(win32con.CTRL_C_EVENT, pid)
win32api.GenerateConsoleCtrlEvent(win32con.CTRL_C_EVENT, pid)
except win32api.error:
pass
print "Waiting 10 seconds, then going to terminate processes"
time.sleep(10)
print "Giving up waiting - killing processes"
for handle in handles:
try:
win32process.TerminateProcess(handle, 0)
except win32process.error:
# This one may have already stopped.
pass
if __name__=='__main__':
RunEm()
cleanly if it sees "CTRL-C" on the console. I need to automate the
running of this application, but still allow the user sitting at the
machine to cancel the process cleanly if he/she needs to. In Unix this
would be a tiny shell script that used "kill -15", but under Windows
there does not seem to be an easy way to do this, at least that I can
find.
Below is a test program, based on CreateProcess.py from "Python
Programming on Win32". The
win32api.GenerateConsoleCtrlEvent(win32con.CTRL_C_EVENT, pid) lines
don't seem to do anything. What they should do is nothing in the case
of notepad, and exit out of the dir builtin process in the case of the
cmd.exe process.
Any ideas on how to make this work?
# CreateProcessDc.py
#
# Demo of creating two processes using the CreateProcess API,
# then waiting for the processes to terminate.
import win32process
import win32event
import win32con
import win32api
import time
# Create a process specified by commandLine, and
# The process' window should be at position rect
# Returns the handle to the new process.
def CreateMyProcess(commandLine, rect):
# Create a STARTUPINFO object
si = win32process.STARTUPINFO()
# Set the position in the startup info.
si.dwX, si.dwY, si.dwXSize, si.dwYSize = rect
# And indicate which of the items are valid.
si.dwFlags = win32process.STARTF_USEPOSITION | \
win32process.STARTF_USESIZE
# Set Creation Flags
CreationFlags = win32process.CREATE_NEW_CONSOLE | \
win32process.CREATE_NEW_PROCESS_GROUP | \
win32process.NORMAL_PRIORITY_CLASS
# Rest of startup info is default, so we leave it alone.
# Create the process.
info = win32process.CreateProcess(
None, # AppName
commandLine, # Command line
None, # Process Security
None, # ThreadSecurity
0, # Inherit Handles?
CreationFlags,
None, # New environment
None, # Current directory
win32process.STARTUPINFO()) # startup info.
##si) # startup info.
# Return the handle to the process.
# Recall info is a tuple of (hProcess, hThread, processId,
threadId)
return (info[0], info[2])
def RunEm():
pids = []
handles = []
# First get the screen size to calculate layout.
screenX = win32api.GetSystemMetrics(win32con.SM_CXSCREEN)
screenY = win32api.GetSystemMetrics(win32con.SM_CYSCREEN)
# First instance will be on the left hand side of the screen.
rect = 0, 0, screenX/2, screenY
handle, pid = CreateMyProcess("notepad", rect)
handles.append(handle)
pids.append(pid)
# Second instance of Notepad will be on the right hand side.
rect = screenX/2+1, 0, screenX/2, screenY
cmd2 = "cmd /k dir /s/p c:\\"
handle, pid = CreateMyProcess(cmd2, rect)
handles.append(handle)
pids.append(pid)
# Now we have the processes, wait for them both
# to terminate.
# Rather than waiting the whole time, we loop 10 times,
# waiting for one second each time, printing a message
# each time around the loop
countdown = range(1,10)
countdown.reverse()
for i in countdown:
print "Waiting %d seconds for apps to close" % i
rc = win32event.WaitForMultipleObjects(
handles, # Objects to wait for.
1, # Wait for them all
1000) # timeout in milli-seconds.
if rc == win32event.WAIT_OBJECT_0:
# Our processes closed!
print "Our processes closed in time."
break
# else just continue around the loop.
else:
# We didn't break out of the for loop!
print "Giving up waiting - sending CTRL-C to processes"
for pid in pids:
try:
print "Sending CTRL-C to process with pid: " +
str(pid)
win32api.GenerateConsoleCtrlEvent(win32con.CTRL_C_EVENT, pid)
win32api.GenerateConsoleCtrlEvent(win32con.CTRL_C_EVENT, pid)
win32api.GenerateConsoleCtrlEvent(win32con.CTRL_C_EVENT, pid)
win32api.GenerateConsoleCtrlEvent(win32con.CTRL_C_EVENT, pid)
win32api.GenerateConsoleCtrlEvent(win32con.CTRL_C_EVENT, pid)
except win32api.error:
pass
print "Waiting 10 seconds, then going to terminate processes"
time.sleep(10)
print "Giving up waiting - killing processes"
for handle in handles:
try:
win32process.TerminateProcess(handle, 0)
except win32process.error:
# This one may have already stopped.
pass
if __name__=='__main__':
RunEm()