home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 11 Util / 11-Util.zip / KILLEM.ZIP / KILLEM.C < prev    next >
Text File  |  1992-07-06  |  31KB  |  677 lines

  1. /**********************************************************************
  2.  * MODULE NAME :  killem.c               AUTHOR:  Rick Fishman        *
  3.  * DATE WRITTEN:  10-24-91                                            *
  4.  *                                                                    *
  5.  * DESCRIPTION:                                                       *
  6.  *                                                                    *
  7.  *  This program kills the specified process(s). It uses the          *
  8.  *  undocumented function DosQProcStatus to get a buffer filled with  *
  9.  *  information related to the current state of the system. It then   *
  10.  *  performs the following using the buffer:                          *
  11.  *                                                                    *
  12.  *  1. Get the relevant process information for all active pids into  *
  13.  *     an array.                                                      *
  14.  *  2. Go thru the module information table. For each module found,   *
  15.  *     see if the module name matches those given on the command      *
  16.  *     line. If so, compare its module reference number against all   *
  17.  *     module reference numbers associated with the active pids. If   *
  18.  *     any match, issue a DosKillProcess against the matching pids.   *
  19.  *                                                                    *
  20.  * UPDATES:                                                           *
  21.  *                                                                    *
  22.  *   5/21/92 - increased buffer to 32k from 16k because of Trap D     *
  23.  *             on LAN.                                                *
  24.  *   5/21/92 - changed compare of process name to buffer so that if   *
  25.  *             'e' is specified on command line, processes that end   *
  26.  *             in 'e' will not be killed.                             *
  27.  *   6/08/92 - added commandline support for process ids. Now Ver 2.1.*
  28.  *                                                                    *
  29.  **********************************************************************/
  30.  
  31.  
  32. /*********************************************************************/
  33. /*------- Include relevant sections of the OS/2 header files --------*/
  34. /*********************************************************************/
  35.  
  36. #define INCL_DOSERRORS
  37. #define INCL_DOSPROCESS
  38.  
  39. /**********************************************************************/
  40. /*----------------------------- INCLUDES -----------------------------*/
  41. /**********************************************************************/
  42.  
  43. #include <os2.h>
  44. #include <ctype.h>
  45. #include <process.h>
  46. #include <stdarg.h>
  47. #include <stdio.h>
  48. #include <string.h>
  49. #include <stdlib.h>
  50. #include "procstat.h"
  51.  
  52. /*********************************************************************/
  53. /*------------------- APPLICATION DEFINITIONS -----------------------*/
  54. /*********************************************************************/
  55.  
  56. #define COPYRIGHT_INFO         "KillEm.exe, 32-bit, Version 2.1\n"             \
  57.                                "Copyright (c) Code Blazers, Inc 1991. All "    \
  58.                                "rights reserved.\n"
  59.  
  60. #define USAGE_INFO             "\nUsage: KillEm processname-or-PID "           \
  61.                                "processname-or-PID ...\n"                      \
  62.                                "\n*** PID must be in decimal ***\n"
  63.  
  64. #define OUT_OF_MEMORY_MSG      "\nOut of memory!\n"
  65.  
  66. #define BUFFER_SIZE             0x8000   //** 5/21/92 changed to 32k
  67.  
  68. /**********************************************************************/
  69. /*---------------------------- STRUCTURES ----------------------------*/
  70. /**********************************************************************/
  71.  
  72. typedef struct _ACTIVEPID           // INFO ON AN ACTIVE PROCESS
  73. {
  74.     USHORT  hModRef;                // It's module reference handle
  75.     PID     pid;                    // It's Process Id
  76.  
  77. } ACTIVEPID, *PACTIVEPID;
  78.  
  79.  
  80. /**********************************************************************/
  81. /*----------------------- FUNCTION PROTOTYPES ------------------------*/
  82. /**********************************************************************/
  83.  
  84. INT    main             ( INT argc, PSZ szArg[] );
  85. VOID   KillEm           ( INT iProcessesToKill );
  86. INT    CompareActivePids( const void *pActivePid1, const void *pActivePid2 );
  87. BOOL   ShouldBeKilled   ( PSZ szProcessName, INT iProcessesToKill );
  88. VOID   FindAndKill      ( PMODINFO pmi );
  89. ULONG  KillProcess      ( PID pid, PSZ szProcessName );
  90. ULONG  Init             ( INT argc, PSZ szArg[], PINT piProcessesToKill );
  91. INT    StoreCmdLinePids ( INT argc, PSZ szArg[] );
  92. BOOL   IsStringNumeric  ( PSZ szString );
  93. ULONG  CopyProcessName  ( PSZ szProcessName, INT iArrayIndex );
  94. VOID   UpCaseNames      ( PMODINFO pmi );
  95. ULONG  BuildActivePidTbl( PPROCESSINFO ppi );
  96. VOID   Term             ( ULONG ulExitCode, INT iProcNameEntries );
  97.  
  98. /**********************************************************************/
  99. /*------------------------ GLOBAL VARIABLES --------------------------*/
  100. /**********************************************************************/
  101.  
  102. USHORT      usActiveProcesses;      // Number of active processes
  103.  
  104. INT         iPidCount;              // Number of commandline process ids
  105.  
  106. ACTIVEPID   *aActivePid;            // Array of active processes
  107.  
  108. PSZ         *pszProcess;            // Array of command-line process names
  109.  
  110. PID         apid[ 50 ];             // Array of cmdline process ids
  111.  
  112. PBUFFHEADER pbh;                    // Pointer to buffer header structure
  113.  
  114. /**********************************************************************/
  115. /*------------------------------ MAIN --------------------------------*/
  116. /*                                                                    */
  117. /*  MAIN DRIVER FOR PROGRAM.                                          */
  118. /*                                                                    */
  119. /*  INPUT: number of command-line arguments,                          */
  120. /*         command-line argument array                                */
  121. /*                                                                    */
  122. /*  1. Perform program initialization which will issue the            */
  123. /*     DosQProcStatus call and obtain the buffer of information.      */
  124. /*  2. Use the buffer to kill processes that match the names given    */
  125. /*     on the command-line.                                           */
  126. /*  3. Perform program termination.                                   */
  127. /*                                                                    */
  128. /*  OUTPUT: nothing                                                   */
  129. /*                                                                    */
  130. /*--------------------------------------------------------------------*/
  131. /**********************************************************************/
  132. INT main( INT argc, PSZ szArg[] )
  133. {
  134.     INT    iProcessesToKill;
  135.     ULONG  ulExitCode = Init( argc, szArg, &iProcessesToKill );
  136.  
  137.     if( ulExitCode == NO_ERROR )
  138.         KillEm( iProcessesToKill );
  139.  
  140.     Term( ulExitCode, iProcessesToKill );
  141.  
  142.     return 0;
  143. }
  144.  
  145. /**********************************************************************/
  146. /*------------------------------ KillEm ------------------------------*/
  147. /*                                                                    */
  148. /*  FIND PROCESSES NAMED ON COMMAND-LINE AND KILL THEM                */
  149. /*                                                                    */
  150. /*  INPUT: number of processes to attempt to kill                     */
  151. /*                                                                    */
  152. /*  1. Kill all process ids that were specified on the commandline.   */
  153. /*  2. If there were process names on the commandline:                */
  154. /*     A. Get the address of the beginning of the module info section.*/
  155. /*     B. For each module information block, check if this is one's   */
  156. /*        module name matches any process names given on the command  */
  157. /*        line. If it does, kill all active pids whose module handle  */
  158. /*        matches its module handle.                                  */
  159. /*                                                                    */
  160. /*  OUTPUT: nothing                                                   */
  161. /*                                                                    */
  162. /*--------------------------------------------------------------------*/
  163. /**********************************************************************/
  164. VOID KillEm( INT iProcessesToKill )
  165. {
  166.     PMODINFO pmi;
  167.     INT      i;
  168.     ULONG    ulRetCode;
  169.  
  170.     for( i = 0; i < iPidCount; i++ )
  171.     {
  172.         ulRetCode = DosKillProcess( DKP_PROCESS, apid[ i ] );
  173.  
  174.         if( ulRetCode )
  175.             if( ulRetCode == ERROR_INVALID_PROCID )
  176.                 printf( "\nPid %u not found", apid[ i ] );
  177.             else
  178.                 printf( "\nFound Pid %u but DosKillProcess failed (RC=%u)",
  179.                         apid[ i ], ulRetCode );
  180.         else
  181.             printf( "\nFound pid %u and Killed it", apid[ i ] );
  182.  
  183.         fflush( stdout );
  184.     }
  185.  
  186.     if( pbh )
  187.     {
  188.         pmi = pbh->pmi;
  189.  
  190.         while( pmi )
  191.         {
  192.             if( ShouldBeKilled( pmi->szModName, iProcessesToKill ) )
  193.                 FindAndKill( pmi );
  194.  
  195.             pmi = pmi->pNext;
  196.         }
  197.     }
  198. }
  199.  
  200. /**********************************************************************/
  201. /*------------------------- ShouldBeKilled ---------------------------*/
  202. /*                                                                    */
  203. /*  DETERMINE WHETHER OR NOT WE SHOULD KILL THIS PROCESS.             */
  204. /*                                                                    */
  205. /*  INPUT: process name,                                              */
  206. /*         number of processes we are attempting to kill              */
  207. /*                                                                    */
  208. /*  1. For each process name given on the command line, see if it     */
  209. /*     is a substring of the process name passed as a parameter.      */
  210. /*     The passed module name could be a fully qualified file name.   */
  211. /*                                                                    */
  212. /*  OUTPUT: TRUE if we should kill it, FALSE if not                   */
  213. /*                                                                    */
  214. /*--------------------------------------------------------------------*/
  215. /**********************************************************************/
  216. BOOL ShouldBeKilled( PSZ szProcessName, INT iProcessesToKill )
  217. {
  218.     BOOL fProcessFound = FALSE;
  219.     PSZ  szFound;
  220.     INT  i;
  221.  
  222.     for( i = 0; i < iProcessesToKill; i++ )
  223.     {
  224.         //** 5/21/92 bug fix
  225.  
  226.         if( (szFound = strstr( szProcessName, pszProcess[ i ] )) &&
  227.             ( szFound == szProcessName || *(szFound - 1) == '\\' ) )
  228.         {
  229.             fProcessFound = TRUE;
  230.  
  231.             break;
  232.         }
  233.     }
  234.  
  235.     return fProcessFound;
  236. }
  237.  
  238. /**********************************************************************/
  239. /*--------------------------- FindAndKill ----------------------------*/
  240. /*                                                                    */
  241. /*  FIND ALL PROCESSES FOR A MODULE HANDLE AND KILL THEM              */
  242. /*                                                                    */
  243. /*  INPUT: pointer to module info block                               */
  244. /*                                                                    */
  245. /*  1. For each active pid, match it's module handle against the      */
  246. /*     module handle of the passed module information block. If it    */
  247. /*     matches, kill that process.                                    */
  248. /*                                                                    */
  249. /*  NOTE: The active array table is sorted in ascending module handle */
  250. /*        order.                                                      */
  251. /*                                                                    */
  252. /*  OUTPUT: nothing                                                   */
  253. /*                                                                    */
  254. /*--------------------------------------------------------------------*/
  255. /**********************************************************************/
  256. VOID FindAndKill( PMODINFO pmi )
  257. {
  258.     USHORT         usKilled = 0;
  259.     register INT   i;
  260.  
  261.     for( i = 0; i < usActiveProcesses; i++ )
  262.     {
  263.         if( aActivePid[ i ].hModRef > pmi->hMod )
  264.             break;
  265.  
  266.         if( aActivePid[ i ].hModRef == pmi->hMod )
  267.             if( KillProcess( aActivePid[ i ].pid, pmi->szModName ) == NO_ERROR )
  268.                 usKilled++;
  269.     }
  270.  
  271.     if( !usKilled )
  272.         printf( "\nCould not find PID for %s!\n", pmi->szModName );
  273. }
  274.  
  275. /**********************************************************************/
  276. /*--------------------------- KillProcess ----------------------------*/
  277. /*                                                                    */
  278. /*  KILL A PROCESS BY PID                                             */
  279. /*                                                                    */
  280. /*  INPUT: process id,                                                */
  281. /*         process name                                               */
  282. /*                                                                    */
  283. /*  1. Issue a DosKillProcess for the passed process id.              */
  284. /*                                                                    */
  285. /*  OUTPUT: return code from DosKillProcess                           */
  286. /*                                                                    */
  287. /*--------------------------------------------------------------------*/
  288. /**********************************************************************/
  289. ULONG KillProcess( PID pid, PSZ szProcessName )
  290. {
  291.     ULONG ulRetCode = DosKillProcess( DKP_PROCESS, pid );
  292.  
  293.     if( ulRetCode )
  294.     {
  295.         if( ulRetCode != ERROR_INVALID_PROCID )
  296.             printf( "\nFound %s (pid %u) but DosKillProcess failed (RC=%u)",
  297.                     szProcessName, pid, ulRetCode );
  298.     }
  299.     else
  300.         printf( "\nFound %s (pid %u) and Killed it", szProcessName, pid );
  301.  
  302.     fflush( stdout );
  303.  
  304.     return ulRetCode;
  305. }
  306.  
  307. /**********************************************************************/
  308. /*------------------------------ Init --------------------------------*/
  309. /*                                                                    */
  310. /*  PERFORM PROGRAM INITIALIZATION                                    */
  311. /*                                                                    */
  312. /*  INPUT: number of command-line arguments,                          */
  313. /*         command-line argument array,                               */
  314. /*         address of int storing count of process names to kill      */
  315. /*                                                                    */
  316. /*  1. Print copyright notice.                                        */
  317. /*  2. If no process names were specified on the command line, exit.  */
  318. /*  3. Allocate memory for an array of pointers to process name       */
  319. /*     strings. We will use this array during the main program loop   */
  320. /*     to identify process names to kill.                             */
  321. /*  4. Allocate memory for each string in the array.                  */
  322. /*  5. Copy the process names from the command line into the memory   */
  323. /*     we just allocated.                                             */
  324. /*  6. Alocate memory for the output from DosQProcStatus.             */
  325. /*  7. Make the DosQProcStatus call.                                  */
  326. /*  8. Uppercase the names in the module information section to make  */
  327. /*     string compares easier during main program logic.              */
  328. /*  9. Build an array of information related to active processes.     */
  329. /*                                                                    */
  330. /*  OUTPUT: exit code                                                 */
  331. /*                                                                    */
  332. /*--------------------------------------------------------------------*/
  333. /**********************************************************************/
  334. ULONG Init( INT argc, PSZ szArg[], PINT piProcessNamesToKill )
  335. {
  336.     ULONG   ulExitCode = NO_ERROR;
  337.     INT     iPszCount = StoreCmdLinePids( argc, szArg );
  338.     INT     iPszArraySize = iPszCount * sizeof( PSZ );
  339.  
  340.     *piProcessNamesToKill = iPszCount;
  341.  
  342.     (void) printf( COPYRIGHT_INFO );
  343.  
  344.     if( iPszCount )
  345.     {
  346.         // Allocate memory for the array of ASCIIZ process names passed on the
  347.         // command line. We can't just use the commandline because we may need
  348.         // to add .EXE to the end of the process name. So we allocate our own
  349.         // array of strings so we have enough room for that extension if
  350.         // necessary.
  351.  
  352.         pszProcess = (PSZ *) malloc( iPszArraySize );
  353.  
  354.         if( pszProcess )
  355.         {
  356.             INT i, iNext = 0;
  357.  
  358.             (void) memset( pszProcess, 0, iPszArraySize );
  359.  
  360.             for( i = 1; i < argc && ulExitCode == NO_ERROR; i++ )
  361.                 if( !IsStringNumeric( szArg[ i ] ) )
  362.                 {
  363.                     ulExitCode = CopyProcessName( szArg[ i ], iNext );
  364.  
  365.                     iNext++;
  366.                 }
  367.         }
  368.  
  369.         if( ulExitCode == NO_ERROR )
  370.             if( !(pbh = malloc( BUFFER_SIZE )) )
  371.             {
  372.                 printf( OUT_OF_MEMORY_MSG );
  373.  
  374.                 ulExitCode = ERROR_NOT_ENOUGH_MEMORY;
  375.             }
  376.  
  377.         if( ulExitCode == NO_ERROR )
  378.         {
  379.             USHORT usRetCode = DosQProcStatus( pbh, BUFFER_SIZE );
  380.  
  381.             if( usRetCode )
  382.             {
  383.                 printf( "\nDosQProcStatus failed. RC: %u.", usRetCode );
  384.  
  385.                 ulExitCode = (ULONG) usRetCode;
  386.             }
  387.         }
  388.  
  389.         if( ulExitCode == NO_ERROR )
  390.         {
  391.             UpCaseNames( pbh->pmi );
  392.  
  393.             ulExitCode = BuildActivePidTbl( pbh->ppi );
  394.         }
  395.     }
  396.     else if( argc <= 1 )
  397.     {
  398.         printf( USAGE_INFO );
  399.  
  400.         ulExitCode = ERROR_INVALID_PARAMETER;
  401.     }
  402.  
  403.     return ulExitCode;
  404. }
  405.  
  406. /**********************************************************************/
  407. /*----------------------- StoreCmdLinePids ---------------------------*/
  408. /*                                                                    */
  409. /*  STORE THE PIDS FOUND ON THE COMMANDLINE.                          */
  410. /*                                                                    */
  411. /*  INPUT: number of command-line arguments,                          */
  412. /*         command-line argument array                                */
  413. /*                                                                    */
  414. /*  1. If the argument is numeric, store it in an array that          */
  415. /*     contains pids to kill. Otherwise increment the count of        */
  416. /*     process names found on the command line.                       */
  417. /*                                                                    */
  418. /*  OUTPUT: number of process names found on the command-line         */
  419. /*                                                                    */
  420. /*--------------------------------------------------------------------*/
  421. /**********************************************************************/
  422. INT StoreCmdLinePids( INT argc, PSZ szArg[] )
  423. {
  424.     INT i, iProcessNames = 0;
  425.  
  426.     if( argc > 1 )
  427.         for( i = 1; i < argc; i++ )
  428.             if( IsStringNumeric( szArg[ i ] ) )
  429.                 apid[ iPidCount++ ] = atoi( szArg[ i ] );
  430.             else
  431.                 iProcessNames++;
  432.  
  433.     return iProcessNames;
  434. }
  435.  
  436. /**********************************************************************/
  437. /*----------------------- IsStringNumeric  ---------------------------*/
  438. /*                                                                    */
  439. /*  CHECK IF A STRING IS NUMERIC.                                     */
  440. /*                                                                    */
  441. /*  INPUT: string                                                     */
  442. /*                                                                    */
  443. /*  1. Check if the string contains all digits.                       */
  444. /*                                                                    */
  445. /*  OUTPUT: TRUE or FALSE if numeric or not                           */
  446. /*                                                                    */
  447. /*--------------------------------------------------------------------*/
  448. /**********************************************************************/
  449. BOOL IsStringNumeric( PSZ szString )
  450. {
  451.     BOOL fNumeric = TRUE;
  452.  
  453.     while( *szString && fNumeric )
  454.     {
  455.         if( !isdigit( *szString ) )
  456.             fNumeric = FALSE;
  457.  
  458.         szString++;
  459.     }
  460.  
  461.     return fNumeric;
  462. }
  463.  
  464. /**********************************************************************/
  465. /*-------------------------- CopyProcessName -------------------------*/
  466. /*                                                                    */
  467. /*  COPY A COMMANDLINE PROCESS NAME TO OUR ARRAY OF PROCESS NAMES.    */
  468. /*                                                                    */
  469. /*  INPUT: pointer to commandline process name,                       */
  470. /*         index into process-name array                              */
  471. /*                                                                    */
  472. /*  1. Allocate memory for the process-name string.                   */
  473. /*  2. Copy the process name from the command line into the memory    */
  474. /*     we just allocated. If the process name has no .EXE extension,  */
  475. /*     force it on.                                                   */
  476. /*                                                                    */
  477. /*  OUTPUT: exit code                                                 */
  478. /*                                                                    */
  479. /*--------------------------------------------------------------------*/
  480. /**********************************************************************/
  481. ULONG CopyProcessName( PSZ szProcessName, INT iArrayIndex )
  482. {
  483.     ULONG   ulExitCode = NO_ERROR;
  484.     USHORT  usStringLen;
  485.     PCH     pchExtension;
  486.  
  487.     // Calculate memory needed for the process name. Make sure the
  488.     // memory size is large enough to include an EXE extension,
  489.     // especially if we need to add one.
  490.  
  491.     usStringLen = strlen( szProcessName ) + 1;
  492.  
  493.     if( !(pchExtension = strchr( szProcessName, '.' )) )
  494.         usStringLen += 4;
  495.  
  496.     pszProcess[ iArrayIndex ] = malloc( usStringLen );
  497.  
  498.     if( pszProcess[ iArrayIndex ] )
  499.     {
  500.         (void) strcpy( pszProcess[ iArrayIndex ], szProcessName );
  501.  
  502.         (void) strupr( pszProcess[ iArrayIndex ] );
  503.  
  504.         if( !pchExtension )
  505.             (void) strcat( pszProcess[ iArrayIndex ], ".EXE" );
  506.     }
  507.     else
  508.     {
  509.         printf( OUT_OF_MEMORY_MSG );
  510.  
  511.         ulExitCode = ERROR_NOT_ENOUGH_MEMORY;
  512.     }
  513.  
  514.     return ulExitCode;
  515. }
  516.  
  517. /**********************************************************************/
  518. /*---------------------------- UpCaseNames ---------------------------*/
  519. /*                                                                    */
  520. /*  MAKE PROCESS NAMES IN MODULE TABLE UPPERCASE.                     */
  521. /*                                                                    */
  522. /*  INPUT: pointer to module table                                    */
  523. /*                                                                    */
  524. /*  1. Upcase all names in the module information section to make it  */
  525. /*     easy on the module name string compare.                        */
  526. /*                                                                    */
  527. /*  OUTPUT: nothing                                                   */
  528. /*                                                                    */
  529. /*--------------------------------------------------------------------*/
  530. /**********************************************************************/
  531. VOID UpCaseNames( PMODINFO pmi )
  532. {
  533.     while( pmi )
  534.     {
  535.         (void) strupr( pmi->szModName );
  536.  
  537.         pmi = pmi->pNext;
  538.     }
  539. }
  540.  
  541. /**********************************************************************/
  542. /*------------------------ BuildActivePidTbl -------------------------*/
  543. /*                                                                    */
  544. /*  BUILD AN ARRAY OF ACTIVE PROCESSES USING THE PROCESS INFO SECTION */
  545. /*  OF THE DosQProcStatus BUFFER.                                     */
  546. /*                                                                    */
  547. /*  INPUT: pointer to ProcessInfo section of buffer                   */
  548. /*                                                                    */
  549. /*  1. Get a count of active processes.                               */
  550. /*  2. Allocate memory for the ActiveProcess table.                   */
  551. /*  3. Store information about each active process in the table.      */
  552. /*  4. Sort the table in ascending module number order.               */
  553. /*                                                                    */
  554. /*  OUTPUT: exit code                                                 */
  555. /*                                                                    */
  556. /*--------------------------------------------------------------------*/
  557. /**********************************************************************/
  558. ULONG BuildActivePidTbl( PPROCESSINFO ppi )
  559. {
  560.     PPROCESSINFO ppiLocal = ppi;
  561.     ULONG        ulExitCode = NO_ERROR;
  562.  
  563.     // Count the number of processes in the process info section. The process
  564.     // count in the summary record is not reliable (2/17/92 - version 6.177)
  565.  
  566.     usActiveProcesses = 0;
  567.  
  568.     while( ppiLocal->ulEndIndicator != PROCESS_END_INDICATOR )
  569.     {
  570.         usActiveProcesses++;
  571.  
  572.         // Next PROCESSINFO struct found by taking the address of the first
  573.         // thread control block of the current PROCESSINFO structure and
  574.         // adding the size of a THREADINFO structure times the number of
  575.         // threads
  576.  
  577.         ppiLocal = (PPROCESSINFO) (ppiLocal->ptiFirst+ppiLocal->usThreadCount );
  578.     }
  579.  
  580.     if( !(aActivePid = malloc( usActiveProcesses * sizeof( ACTIVEPID ) )) )
  581.     {
  582.         printf( OUT_OF_MEMORY_MSG );
  583.  
  584.         ulExitCode = ERROR_NOT_ENOUGH_MEMORY;
  585.     }
  586.     else
  587.     {
  588.         register INT i;
  589.  
  590.         for( i = 0; i < usActiveProcesses; i++ )
  591.         {
  592.             aActivePid[ i ].hModRef = ppi->hModRef;
  593.  
  594.             aActivePid[ i ].pid = (PID) ppi->pid;
  595.  
  596.             ppi = (PPROCESSINFO) (ppi->ptiFirst + ppi->usThreadCount);
  597.         }
  598.  
  599.         qsort( aActivePid, usActiveProcesses, sizeof( ACTIVEPID ),
  600.                CompareActivePids );
  601.     }
  602.  
  603.     return ulExitCode;
  604. }
  605.  
  606. /**********************************************************************/
  607. /*------------------------ CompareActivePids -------------------------*/
  608. /*                                                                    */
  609. /*  COMPARE FUNCTION FOR THE QSORT OF THE ACTIVE PID ARRAY. SORTS     */
  610. /*  THE ARRAY IN MODULE HANDLE ORDER.                                 */
  611. /*                                                                    */
  612. /*  INPUT: pointer to first element for compare,                      */
  613. /*         pointer to second element of compare                       */
  614. /*                                                                    */
  615. /*  1. Do the compare.                                                */
  616. /*                                                                    */
  617. /*  OUTPUT: < 0 means first < second                                  */
  618. /*          = 0 means first = second                                  */
  619. /*          > 0 means first > second                                  */
  620. /*                                                                    */
  621. /*--------------------------------------------------------------------*/
  622. /**********************************************************************/
  623. INT CompareActivePids( const void *pActivePid1, const void *pActivePid2 )
  624. {
  625.     if( ((PACTIVEPID)pActivePid1)->hModRef < ((PACTIVEPID)pActivePid2)->hModRef )
  626.         return -1;
  627.     else
  628.     if( ((PACTIVEPID)pActivePid1)->hModRef > ((PACTIVEPID)pActivePid2)->hModRef )
  629.         return +1;
  630.     else
  631.         return 0;
  632. }
  633.  
  634. /**********************************************************************/
  635. /*------------------------------ Term --------------------------------*/
  636. /*                                                                    */
  637. /*  PERFORM PROGRAM TERMINATION                                       */
  638. /*                                                                    */
  639. /*  INPUT: exit code,                                                 */
  640. /*         number of process names entered on the command line        */
  641. /*                                                                    */
  642. /*  1. Free the array of process name strings that we allocated       */
  643. /*     during program initialization.                                 */
  644. /*  2. Free the ActiveProcess array.                                  */
  645. /*  3. Free the buffer allocated for the DosQProcStatus output.       */
  646. /*  4. Return the exit code to the operating system.                  */
  647. /*                                                                    */
  648. /*  OUTPUT: nothing                                                   */
  649. /*                                                                    */
  650. /*--------------------------------------------------------------------*/
  651. /**********************************************************************/
  652. VOID Term( ULONG ulExitCode, INT iProcessNameEntries )
  653. {
  654.     if( pszProcess )
  655.     {
  656.         register INT i;
  657.  
  658.         for( i = 0; i < iProcessNameEntries; i++ )
  659.             if( pszProcess[ i ] )
  660.                 free( pszProcess[ i ] );
  661.  
  662.         free( pszProcess );
  663.     }
  664.  
  665.     if( aActivePid )
  666.         free( aActivePid );
  667.  
  668.     if( pbh )
  669.         free( pbh );
  670.  
  671.     DosExit( EXIT_PROCESS, ulExitCode );
  672. }
  673.  
  674. /**********************************************************************
  675.  *                       END OF SOURCE CODE                           *
  676.  **********************************************************************/
  677.