Figure 1     TLIST's Output

C:\> TLIST /t
System Process (0)
System (2)
 smss.exe (20)
  csrss.exe (30)
  WINLOGON.EXE (34)
  SERVICES.EXE (40)
   SPOOLSS.EXE (66)
   NCPHONE.EXE (77)
   RPCSS.EXE (80)
    mdm.exe (153)
   TAPISRV.EXE (84)
   RASMAN.EXE (102)
   PSTORES.EXE (110)
  LSASS.EXE (43)
  NDDEAGNT.EXE (46)
explorer.exe (140) Program Manager
 systray.exe (162)
 REMINDER.EXE (172)
 UpFront.exe (44)
 DDHELP.EXE (220)
 OUTLOOK.EXE (186) Inbox - Microsoft Outlook
  MAPISP32.EXE (211)
 MSDEV.EXE (223) Unfrag - Microsoft Developer Studio - [InfoViewer Topic]
 WINWORD.EXE (168) Microsoft Word - Q & A in Progress.DOC
 CMD.EXE (59) C:\WINNT\System32\cmd.exe - tlist -t 
  TLIST.EXE (196)
mswheel.exe (174)
TASKMGR.EXE (130)
RASMON.EXE (252) Dial-Up Networking Monitor

Figure 2   SmallApp.cpp


 /******************************************************************************
 Module name: SmallApp.cpp
 Written by:  Jeffrey Richter
 Purpose:     Spawn helper that kills everything in its Process Group.
 ******************************************************************************/
 
 //#define UNICODE
 //#define _UNICODE
 
 #define STRICT
 #include <Windows.h>
 #include <process.h>
 #include <tchar.h>
 #include <stdio.h>
 
 ///////////////////////////////////////////////////////////////////////////////
 
 extern "C" int  _tmain(int argc, TCHAR* argv[]) {
 
    // Make sure that we've been passed the right number of arguments
    if (argc < 3) {
       _tprintf(
          __TEXT("Usage: %s (InheritableEventHandle) (CommandLineToSpawn)\n"), 
          argv[0]);
       return(0);
    }
 
    // Construct the full command line
    TCHAR szCmdLine[MAX_PATH] = { 0 };
    for (int x = 2; x < argc; x++) {
       _tcscat(szCmdLine, argv[x]); 
       _tcscat(szCmdLine, __TEXT(" ")); 
    }
 
    STARTUPINFO         si = { sizeof(si) };
    PROCESS_INFORMATION pi = { 0 };
    DWORD dwExitCode = 0;
 
    HANDLE h[2];
    h[0] = (HANDLE) _ttoi(argv[1]);  // The inherited event handle
 
    // Spawn the other processes as part of this Process Group
    BOOL f = CreateProcess(NULL, szCmdLine, NULL, NULL, TRUE, 
                           0, NULL, NULL, &si, &pi);
 
    if (f) {
       CloseHandle(pi.hThread);
       h[1] = pi.hProcess;
 
       // Wait for the spawned-process to die or for the event
       // indicating that the processes should be forcibly killed.
       switch (WaitForMultipleObjects(2, h, FALSE, INFINITE)) {
          case WAIT_OBJECT_0 + 0:  // Force termination
             GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, 0);
             WaitForSingleObject(pi.hProcess, INFINITE);
             break;
 
          case WAIT_OBJECT_0 + 1:  // App terminated normally
             // Make its exit code our exit code
             GetExitCodeProcess(pi.hProcess, &dwExitCode);
             break;
       }
       CloseHandle(pi.hProcess);
    }
    CloseHandle(h[0]);   // Close the inherited event handle
    return(dwExitCode);
 }
 
 //////////////////////////////// End of File //////////////////////////////////

Figure 3   MainApp.cpp


 /******************************************************************************
 Module name: MainApp.cpp
 Written by:  Jeffrey Richter
 Purpose:     Runs a set of processes with the ability to kill them all.
 ******************************************************************************/
 
 //#define UNICODE
 //#define _UNICODE
 
 #define STRICT
 #include <Windows.h>
 #include <process.h>
 #include <tchar.h>
 #include <stdio.h>
 
 
 ///////////////////////////////////////////////////////////////////////////////
 
 // Event used to forcibly kill the spawned Process Group
 HANDLE g_heventTerminateProcessGroup = NULL;
 
 ///////////////////////////////////////////////////////////////////////////////
 
 unsigned int _stdcall StartProcessGroup(void* pv) {
 
    // Construct the path of our spawn-helper application
    TCHAR szCmdLine[MAX_PATH];
    GetModuleFileName(NULL, szCmdLine, MAX_PATH);
    *(_tcsrchr(szCmdLine, __TEXT('\\')) + 1) = 0; // truncate EXE filename
    _stprintf(_tcschr(szCmdLine, 0), __TEXT("SmallApp.exe %d"), 
       g_heventTerminateProcessGroup);
    
    // Our command-line arguments indicate the process that we want to run
    for (int x = 1; x < __argc; x++) {
       _tcscat(szCmdLine, __TEXT(" ")); 
       _tcscat(szCmdLine, __targv[x]); 
    }
 
    STARTUPINFO si = { sizeof(si) };
    si.dwFlags     = STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_HIDE;  // Processes in the Process Group are hidden
 
    PROCESS_INFORMATION pi;
 
    // Spawn the process (make sure it inherits our event handle)
    BOOL f = CreateProcess(NULL, szCmdLine, NULL, NULL, TRUE, 
                           CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
 
    DWORD dw = 0;  // Return value
 
    if (f) {
       // The process was sucessfully spawned, wait for it to terminate
       CloseHandle(pi.hThread);
       WaitForSingleObject(pi.hProcess, INFINITE);
       GetExitCodeProcess(pi.hProcess, &dw);  // It's exit code is ours
       CloseHandle(pi.hProcess);
 
       // If the message box is still visible, the process terminated normally
       // BEWARE: There is a potential thread synchronization problem here 
       // because the message box may not be visible yet. The possibility of 
       // this is so small that I've left the code as is for this example.
       HWND hwnd = FindWindow(NULL, __TEXT("Terminate Process Group"));
       if (IsWindow(hwnd)) {
          // Force the message box to close
          EndDialog(hwnd, IDCANCEL);
       } else {
          // The message box is not up. the process group was forcibly killed
       }
    } else {
       // Couldn't spawn the process group application
    }
    return(dw);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 extern "C" int WINAPI _tWinMain(HINSTANCE hinst, HINSTANCE hinstExePrev, 
                                 LPTSTR pszCmdLine, int nShowCmd) {
 
    DWORD dwThreadId;
 
    // Create an inheritable event handle to use as our IPC mechanism
    // to terminate the processes in the process group
    // NOTE: Keep this handle for the entire lifetime of this application
    g_heventTerminateProcessGroup = CreateEvent(NULL, TRUE, FALSE, NULL);
    SetHandleInformation(g_heventTerminateProcessGroup, 
                         HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
 
    // Spawn the processes in the process group.
    ResetEvent(g_heventTerminateProcessGroup);   // Do this before each spawn
    HANDLE hThread = (HANDLE) _beginthreadex(NULL, 0, StartProcessGroup, 
                                             NULL, 0, (UINT*) &dwThreadId);
 
    // Put up a message box waiting for the process group to terminate
    // If the user clicks OK, we will terminate the process group
    int n = MessageBox(NULL, __TEXT("Press OK to kill the Process Group."),
                       __TEXT("Terminate Process Group"), MB_OK);
    if (n == IDOK) {
        // Set the event which will kill the Process Group.
        SetEvent(g_heventTerminateProcessGroup);
        WaitForSingleObject(hThread, INFINITE);
    } else {
        // The other thread killed the Process Group and forced the
        // message box to die with a return code of IDCANCEL
    }
 
    // The spawned processes have terminated, clean up
    CloseHandle(hThread);
 
    // When this application is terminating, close the event
    CloseHandle(g_heventTerminateProcessGroup);
     
    return(0);
 }
 
 //////////////////////////////// End of File //////////////////////////////////