June 1998
Download Jun98Win32Code.exe (6KB)
Jeffrey Richter wrote Advanced Windows, Third Edition (Microsoft Press, 1998) and Windows 95: A Developer's Guide (M&T Books, 1995). Jeff is a consultant and teaches Win32 programming courses (http://www.jeffreyrichter.com.)
Q My application, MainApp, spawns another process, WorkerApp, to do some additional work. When WorkerApp is running, it may spawn even more child processes that do even more work. While MainApp is waiting for WorkerApp and its children to complete, the user may want to stop this additional work from continuing. To do this, I have MainApp kill WorkerApp, but this is not sufficientWorkerApp's child processes must terminate as well.
Irfan Shariff
A I'm quite surprised that I haven't been asked this question before. This seems like a common thing to want to do, but Win32® doesn't offer an easy way to accomplish it. The Win32 specification states that no relationship exists between a parent process and any of its child processes once a child process has been created. Many other operating systems support a parent/child relationship policy, so if you kill a process, the system automatically kills all of the process's descendant processes.
|
|
th32ParentProcessID is the ID of the process that created the process I'm looking at. Now I can walk the set of processes in the system and determine which are descendants of the main process that I want to kill. I can then call OpenProcess, TerminateProcess, and CloseHandle for each of these descendant processes. I think you'll agree that this is not such a horrible solution.
However, Windows NT® 4.0 does not support the ToolHelp functions. Since these functions are not part of the Win32 specification, you would not be able to get your application to work correctly using this method on Windows NT 4.0. On the other hand, the ToolHelp functions have been so useful, Microsoft has seen fit to include them in Windows NT 5.0. If the ToolHelp functions don't work on Windows NT 4.0 or earlier, what can you do on Windows NT today? Well, the TLIST.EXE tool in the Windows NT Resource Kit makes calls to undocumented functions to produce a tree showing the parent/child relationships between processes (note that this tool doesn't run on Windows 95). Figure 1 shows an example of TLIST's output. You can see that Explorer.exe (with process ID 140) is the parent of CMD.EXE (process ID 59), which is the parent of TLIST.EXE (process ID 196). When your application runs on Windows NT, you could spawn TLIST.EXE and redirect its output to a file or memory buffer. Then you could parse the contents of this buffer to get the descendant process information and kill all the child processes. Of course, this requires your customers to have TLIST.EXE installed because Microsoft doesn't allow you to redistribute this utility. There is another problem with both of the methods shown: there is no guarantee that the process is still running when you get the process IDs back. One of the child processes could terminate and a completely different process that has nothing to do with your application could start and be assigned a process ID that was previously used by your child process. This error would cause your application to terminate some other process, making your users very upset. You could get around this problem by writing more complicated code, but a better solution that works on both Windows 95 and Windows NT would be great. Every console process runs in a process group. A process group is a set of processes that receives a notification when special events occur such as the user pressing Ctrl+C or Ctrl+Break, the user closing a console window, a system logoff, or a system shutdown event. If you have several console processes running in a single process group, then all of the processes in the group receive this notification. To solve your process-control problem, all you have to do is make sure that all of the processes you are spawning are console applications running in a single process group. Then you can use the GenerateConsoleCtrlEvent function to force a Ctrl+Break notification to all of these processes: |
|
By default, when a console process receives a Ctrl+Break notification, the process terminates itself. What could
be easier?
Now let's put all of this information together. First, in your MainApp application, spawn the new process by calling CreateProcess, passing the DETACHED_PROCESS, CREATE_NEW_CONSOLE, or CREATE_NEW_PROCESS_ GROUP flag. All of these flags cause the system to create a new process group that contains only the newly spawned process. Any processes spawned from this new process will be part of the same process group. (If a grandchild process is a GUI application or a console application spawned using the DETACHED_PROCESS, CREATE_NEW_CONSOLE, or CREATE_NEW_PROCESS_GROUP flag, then my solution fails.) The process ID of the first process in a process group is used to notify all of the processes within a single process group. You will need to pass this process ID as the second parameter to the GenerateConsoleCtrlEvent function to identify which process group will receive the event notification. You would think that MainApp just has to call GenerateConsoleCtrlEvent when you want to terminate all the processes in the process group. Unfortunately, it's not quite that simple. You see, GenerateConsoleCtrlEvent has a small feature that complicates this issue a little more. If you read the documentation for GenerateConsoleCtrlEvent closely, you'll see the following statement: "The GenerateConsoleCtrlEvent function sends a specified signal to a console process group that shares the console associated with the calling process." This additional requirement means the Main-App program will not be able to call GenerateConsoleCtrlEvent successfully because it is running in a different process group. To solve this problem you must write another small application, SmallApp. MainApp will spawn SmallApp in its own process group, and SmallApp will be responsible for spawning any additional processes. You will need to set up some other type of communication mechanism between MainApp and SmallApp. When MainApp wants to terminate the process group, it will use a well-defined IPC mechanism (like an event kernel object) that tells SmallApp to kill its own process group. Figures 2 and 3 show all of the steps necessary to make MainApp and SmallApp do the right thing. The code is commented, so I won't go into any additional details about it here. By the way, Developer Studio ships with a small application called VCSPAWN.EXE that exists for exactly this purpose. By the way, Microsoft provided a much better way of solving this problem with Window NT 5.0. The solution comes courtesy of the all-new Job kernel object. A Job object allows multiple processes to be grouped together and treated as a single entity. So if you were to rewrite MainApp using Job objects, you would do the following:
There are several more cool things that you can do with Job objects on Windows NT 5.0. I strongly encourage you to examine the documentation and prepare for the future. Have a question about programming in Win32? Send your questions via email to Jeffrey Richter from his website at http://www.jeffreyrichter.com. |
From the June 1998 issue of Microsoft Systems Journal.