/* *@@sourcefile procstat.c: * functions for querying process information. * This is an easy-to-use interface to the * messy 16-bit DosQProcStatus function. * * Usage: All OS/2 programs. * * Function prefixes (new with V0.81): * -- prc* Query Process helper functions * * Based on Kai Uwe Rommel's "dosqproc" package * available at Hobbes: * Kai Uwe Rommel - Wed 25-Mar-1992 * Sat 13-Aug-1994 * * Note: If you link against procstat.obj, you * need to import the following in your .DEF file: * + IMPORTS + DOSQPROCSTATUS = DOSCALLS.154 + DosQuerySysState = DOSCALLS.368 * * or linking will fail. * * Note: Version numbering in this file relates to XWorkplace version * numbering. * *@@header "helpers\procstat.h" */ /* * Copyright (C) 1992-1994 Kai Uwe Rommel. * Copyright (C) 1998-2014 Ulrich M”ller. * This file is part of the "XWorkplace helpers" source package. * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation, in version 2 as it comes in the * "COPYING" file of the XWorkplace main distribution. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #define OS2EMX_PLAIN_CHAR // this is needed for "os2emx.h"; if this is defined, // emx will define PSZ as _signed_ char, otherwise // as unsigned char #define INCL_DOSMODULEMGR #define INCL_DOSERRORS #define INCL_DOSPROCESS #include #include // already #include'd #include // already #include'd #include #include "setup.h" // code generation and debugging options #include "helpers\procstat.h" #pragma hdrstop /* *@@category: Helpers\Control program helpers\Process status\16-bit DosQProcStat */ /******************************************************************** * * DosQProcStat (16-bit) interface * ********************************************************************/ /* *@@ prc16GetInfo: * nifty interface to DosQProcStat (16-bit). * This returns the head of a newly * allocated buffer which has plenty * of pointers for subsequent browsing. * * Use prc16FreeInfo to free the buffer. * *@@added V0.9.3 (2000-05-05) [umoeller] *@@changed V0.9.10 (2001-04-08) [umoeller]: this returned != NULL even though item was freed, fixed *@@changed V0.9.10 (2001-04-08) [umoeller]: now using DosAllocMem, raised bufsize, changed prototype */ APIRET prc16GetInfo(PQPROCSTAT16 *ppps) // out: error, ptr can be NULL { APIRET arc = NO_ERROR; PQPROCSTAT16 pps = NULL; if (!ppps) return ERROR_INVALID_PARAMETER; // changed allocation V0.9.10 (2001-04-08) [umoeller]: // malloc didn't guarantee that the object did not // cross a 64K boundary, which could cause DosQProcStat // to fail... #define BUF_SIZE 0xFFFF // raised from 0x8000 if (!(arc = DosAllocMem((VOID**)&pps, BUF_SIZE, PAG_READ | PAG_WRITE | PAG_COMMIT | OBJ_TILE // 16-bit compatible, ignored really ))) { if (arc = DosQProcStatus(pps, BUF_SIZE)) { // error: DosFreeMem(pps); // V0.9.10 (2001-04-08) [umoeller] // and even worse, I forgot to set the return ptr // to NULL, so this was freed twice... I guess // this produced the crashes in WarpIN with the // KILLPROCESS attribute... V0.9.10 (2001-04-08) [umoeller] pps = NULL; } } *ppps = pps; return arc; } /* *@@ prc16FreeInfo: * frees memory allocated by prc16GetInfo. * *@@added V0.9.3 (2000-05-05) [umoeller] *@@changed V0.9.10 (2001-04-08) [umoeller]: now using DosFreeMem */ APIRET prc16FreeInfo(PQPROCSTAT16 pInfo) { if (!pInfo) return ERROR_INVALID_PARAMETER; return DosFreeMem(pInfo); } /* *@@ prc16FindProcessFromName: * searches the specified buffer for a process * with the specified name and returns a pointer * to its data within pInfo. * * Returns NULL if not found. * *@@added V0.9.3 (2000-05-05) [umoeller] */ PQPROCESS16 prc16FindProcessFromName(PQPROCSTAT16 pInfo, // in: from prc16GetInfo const char *pcszName) // in: e.g. "pmshell.exe" { PQPROCESS16 pProcess, pReturn = NULL; if (pInfo) { for ( pProcess = (PQPROCESS16)PTR(pInfo->ulProcesses, 0); pProcess->ulType != 3; pProcess = (PQPROCESS16)PTR(pProcess->ulThreadList, pProcess->usThreads * sizeof(QTHREAD16)) ) { CHAR szModuleName[CCHMAXPATH]; if (DosQueryModuleName(pProcess->usHModule, sizeof(szModuleName), szModuleName) == NO_ERROR) { // the module name is fully qualified, so find the // file name (after the last backslash) PSZ pLastBackslash = strrchr(szModuleName, '\\'); if (pLastBackslash) // found: if (stricmp(pLastBackslash + 1, pcszName) == 0) { // matches: pReturn = pProcess; break; } } } } return pReturn; } /* *@@ prc16FindProcessFromPID: * searches the specified buffer for a process * with the specified PID and returns a pointer * to its data within pInfo. * * Returns NULL if not found. * *V0.9.5 (2000-09-29) [umoeller] */ PQPROCESS16 prc16FindProcessFromPID(PQPROCSTAT16 pInfo, // in: from prc16GetInfo ULONG ulPID) // in: PID { PQPROCESS16 pProcess, pReturn = NULL; if (pInfo) { for ( pProcess = (PQPROCESS16)PTR(pInfo->ulProcesses, 0); pProcess->ulType != 3; pProcess = (PQPROCESS16)PTR(pProcess->ulThreadList, pProcess->usThreads * sizeof(QTHREAD16)) ) { if (pProcess->usPID == ulPID) { pReturn = pProcess; break; } } } return pReturn; } /******************************************************************** * * DosQProcStat (16-bit) helpers * ********************************************************************/ /* * prcReport16: * fill PRCPROCESS structure */ VOID prcReport16(PQPROCESS16 pProcess, PPRCPROCESS pprcp) { if (pProcess) { PQTHREAD16 pThread; int i; DosQueryModuleName(pProcess->usHModule, sizeof(pprcp->szModuleName), pprcp->szModuleName); // DosGetPrty(PRTYS_PROCESS, &(pprcp->usPriority), pProcess->usPID); // sum up CPU time for process for (pprcp->ulCPU = 0, i = 0, pThread = (PQTHREAD16)PTR(pProcess->ulThreadList, 0); i < pProcess->usThreads; i++, pThread++ ) { pprcp->ulCPU += (pThread->ulSysTime + pThread->ulUserTime); } pprcp->usPID = pProcess->usPID; pprcp->usParentPID = pProcess->usParentPID; pprcp->usThreads = pProcess->usThreads; pprcp->ulSID = pProcess->ulSID; pprcp->ulSessionType = pProcess->ulSessionType; pprcp->ulStatus = pProcess->ulStatus; } } /* *@@ prc16QueryProcessInfo: * this searches for a given process ID (usPID) and * fills a given PRCPROCESS structure with lots of * information about this process. * Returns FALSE upon errors, e.g. if no process * of that ID is found. */ BOOL prc16QueryProcessInfo(PQPROCSTAT16 pps, // in: from prc16GetInfo USHORT usPID, // in: PID to query PPRCPROCESS pprcp) // out: process info { BOOL rc = FALSE; if (pps) { PQPROCESS16 pProcess; for ( pProcess = (PQPROCESS16)PTR(pps->ulProcesses, 0); pProcess->ulType != 3; pProcess = (PQPROCESS16)PTR(pProcess->ulThreadList, pProcess->usThreads * sizeof(QTHREAD16)) ) { if (pProcess->usPID == usPID) { prcReport16(pProcess, pprcp); rc = TRUE; break; } } } return rc; } /* *@@ prc16ForEachProcess: * this calls a given callback func for each running * process. The callback must be a FNWP, which will be * passed the following parameters for each call: * -- HWND hwnd: like hwnd passed to this func * -- ULONG msg: like msg passed to this func * -- MPARAM mp1: like mp1 passed to this func * -- PPRCPROCESS: mp2 pointer to a PRCPROCESS struct for each process * * This function returns the number of running processes on the * system. If pfnwpCallback is NULL, only this number will be * returned, so you can use this as a process counter too. * *@@changed V0.9.10 (2001-04-16) [pr]: now using DosAllocMem */ ULONG prc16ForEachProcess(PFNWP pfnwpCallback, HWND hwnd, ULONG ulMsg, MPARAM mp1) { ULONG ulrc = 0; PQPROCSTAT16 pps; PQPROCESS16 pProcess; PRCPROCESS prcp; if (!DosAllocMem((PVOID*)&pps, BUF_SIZE, PAG_READ | PAG_WRITE | PAG_COMMIT | OBJ_TILE)) { if (!DosQProcStatus(pps, BUF_SIZE)) for ( pProcess = (PQPROCESS16)PTR(pps->ulProcesses, 0); pProcess->ulType != 3; pProcess = (PQPROCESS16)PTR(pProcess->ulThreadList, pProcess->usThreads * sizeof(QTHREAD16)) ) { if (pfnwpCallback) { prcReport16(pProcess, &prcp); (*pfnwpCallback)(hwnd, ulMsg, mp1, &prcp); } ulrc++; } DosFreeMem(pps); } return ulrc; } /* *@@ prc16QueryThreadCount: * returns the total number of running threads * in the given process. If pid == 0, the * total thread count for the system is returned. * *@@changed V0.9.9 (2001-03-07) [umoeller]: added pps param */ ULONG prc16QueryThreadCount(PQPROCSTAT16 pps, // in: from prc16GetInfo USHORT usPID) { ULONG ulrc = 0; if (pps) { if (usPID) { // process query: PQPROCESS16 pProcess; for ( pProcess = (PQPROCESS16)PTR(pps->ulProcesses, 0); pProcess->ulType != 3; pProcess = (PQPROCESS16)PTR(pProcess->ulThreadList, pProcess->usThreads * sizeof(QTHREAD16)) ) { if (pProcess->usPID == usPID) { ulrc = pProcess->usThreads; break; } } } else { // global query: PQGLOBAL16 pg; pg = (PQGLOBAL16)PTR(pps->ulGlobal, 0); ulrc = pg->ulThreads; } } return ulrc; } /* *@@ prc16QueryThreadInfo: * this searches for a given thread in a given process * and fills a given PRCTHREAD structure with lots of * information about that thread. * * Returns FALSE upon errors. * * Note: This function loops thru all processes which * are currently running and is therefore not terribly * fast. Use economically. * *@@changed V0.9.9 (2001-03-07) [umoeller]: added pps param */ BOOL prc16QueryThreadInfo(PQPROCSTAT16 pps, // in: from prc16GetInfo USHORT usPID, USHORT usTID, PPRCTHREAD pprct) { BOOL brc = FALSE; if (pps) { PQPROCESS16 pProcess; // find process: for ( pProcess = (PQPROCESS16)PTR(pps->ulProcesses, 0); pProcess->ulType != 3; pProcess = (PQPROCESS16)PTR(pProcess->ulThreadList, pProcess->usThreads * sizeof(QTHREAD16)) ) { if (pProcess->usPID == usPID) { PQTHREAD16 pThread; int i; // process found: find thread for ( i = 0, pThread = (PQTHREAD16)PTR(pProcess->ulThreadList, 0); i < pProcess->usThreads; i++, pThread++ ) { if (pThread->usTID == usTID) { // thread found: pprct->usTID = pThread->usTID; pprct->usThreadSlotID = pThread->usThreadSlotID; pprct->ulBlockID = pThread->ulBlockID; pprct->ulPriority = pThread->ulPriority; pprct->ulSysTime = pThread->ulSysTime; pprct->ulUserTime = pThread->ulUserTime; pprct->ucStatus = pThread->ucStatus; brc = TRUE; break; // thread-for loop } } // end for thread break; // process-for loop } } // end for process } return brc; } /* *@@ prcQueryPriority: * shortcut to prc16QueryThreadInfo if you want the priority only. * * Returns -1 upon errors. * * Note: This function loops thru all processes which * are currently running and is therefore not terribly * fast. Use economically. * *@@changed V0.9.9 (2001-03-07) [umoeller]: added pps param */ ULONG prc16QueryThreadPriority(PQPROCSTAT16 pps, // in: from prc16GetInfo USHORT usPID, USHORT usTID) { PRCTHREAD prct; ULONG ulrc = -1; if (prc16QueryThreadInfo(pps, usPID, usTID, &prct)) ulrc = prct.ulPriority; return ulrc; } /* *@@category: Helpers\Control program helpers\Process status\32-bit DosQuerySysState */ /******************************************************************** * * DosQuerySysState (32-bit) interface * ********************************************************************/ /* *@@ prc32GetInfo2: * nifty interface to DosQuerySysState, the 32-bit * version of DosQProcStat. * * This returns the head of a newly allocated buffer * which has plenty of pointers for subsequent browing. * * As opposed to prc32GetInfo, with this call you can * specify the information that would like to retrieve. * * Use prc32FreeInfo to free the buffer. * *@@added V1.0.1 (2003-01-10) [umoeller] */ PQTOPLEVEL32 prc32GetInfo2(ULONG fl, // in: QS32_* flags APIRET *parc) // out: error, ptr can be NULL { APIRET arc; PQTOPLEVEL32 pReturn = NULL; #define BUFSIZE (1024 * 1024) // 1 meg if (!(arc = DosAllocMem((PVOID*)&pReturn, BUFSIZE, PAG_READ | PAG_WRITE | PAG_COMMIT | OBJ_TILE))) { if (arc = DosQuerySysState(fl, fl, // this was missing V0.9.10 (2001-04-08) [umoeller] 0, 0, (PCHAR)pReturn, BUFSIZE)) { DosFreeMem(pReturn); pReturn = NULL; } } if (parc) *parc = arc; return pReturn; } /* *@@ prc32GetInfo: * nifty interface to DosQuerySysState, the 32-bit * version of DosQProcStat. * * This returns the head of a newly allocated buffer * which has plenty of pointers for subsequent browing. * * Use prc32FreeInfo to free the buffer. * *@@added V0.9.1 (2000-02-12) [umoeller] *@@changed V0.9.3 (2000-05-01) [umoeller]: now using DosAllocMem *@@changed V0.9.10 (2001-04-08) [umoeller]: fixed second QuerySysState param */ PQTOPLEVEL32 prc32GetInfo(APIRET *parc) // out: error, ptr can be NULL { return prc32GetInfo2(QS32_SUPPORTED, parc); } /* *@@ prc32FreeInfo: * frees the memory allocated by prc32GetInfo. * *@@added V0.9.1 (2000-02-12) [umoeller] *@@changed V0.9.3 (2000-05-01) [umoeller]: now using DosFreeMem */ VOID prc32FreeInfo(PQTOPLEVEL32 pInfo) { DosFreeMem(pInfo); } /* *@@ prc32FindProcessFromName: * *@@added V0.9.2 (2000-03-05) [umoeller] *@@changed V1.0.0 (2002-08-16) [pr]: optimized */ PQPROCESS32 prc32FindProcessFromName(PQTOPLEVEL32 pInfo, const char *pcszName) // in: e.g. "pmshell.exe" { PQPROCESS32 pProcThis = pInfo->pProcessData; while (pProcThis && pProcThis->ulRecType == 1) { PQTHREAD32 t = pProcThis->pThreads; PQMODULE32 pModule; if (pModule = prc32FindModule(pInfo, pProcThis->usHModule)) { // the module name is fully qualified, so find the // file name (after the last backslash) if (pModule->pcName) { PSZ pLastBackslash; if (pLastBackslash = strrchr(pModule->pcName, '\\')) // found: if (stricmp(pLastBackslash + 1, pcszName) == 0) // matches: break; } } // for next process, skip the threads info; // the next process block comes after the // threads t += pProcThis->usThreadCount; pProcThis = (PQPROCESS32)t; } if (pProcThis->ulRecType == 1) return pProcThis; return NULL; } /* *@@ prc32FindProcessFromPID: * *@@added V1.0.0 (2002-08-12) [umoeller] *@@changed V1.0.0 (2002-08-16) [pr]: optimized */ PQPROCESS32 prc32FindProcessFromPID(PQTOPLEVEL32 pInfo, ULONG pid) { PQPROCESS32 pProcThis = pInfo->pProcessData; while (pProcThis && pProcThis->ulRecType == 1) { PQTHREAD32 t = pProcThis->pThreads; if (pProcThis->usPID == pid) return pProcThis; // for next process, skip the threads info; // the next process block comes after the // threads t += pProcThis->usThreadCount; pProcThis = (PQPROCESS32)t; } return NULL; } /* *@@ prc32FindSem16: * attempts to find the specified 16-bit semaphore * in the specified info buffer. * * The return value points into the pInfo buffer. * Returns NULL if not found. * *@@added V0.9.1 (2000-02-12) [umoeller] */ PQS32SEM16 prc32FindSem16(PQTOPLEVEL32 pInfo, // in: as returned by prc32GetInfo USHORT usSemID) // in: as in QPROCESS32.pausSem16 { PQS32SEM16HEAD pSemHead = pInfo->pSem16Data; PQS32SEM16 // pSemThis = &pSemData->sema; pSemThis = &pSemHead->Sem16Rec; ULONG i = 0; while (pSemThis) { if (i == usSemID) return pSemThis; i++; pSemThis = pSemThis->pNext; } return NULL; } /* *@@ prc32FindSem32: * attempts to find the specified 32-bit semaphore * in the specified info buffer. This might fail * because the data isn't always complete. * * The return value points into the pInfo buffer. * Returns NULL if not found. * *@@added V0.9.1 (2000-02-12) [umoeller] */ PQS32SEM32 prc32FindSem32(PQTOPLEVEL32 pInfo, // in: as returned by prc32GetInfo USHORT usSemID) // in: as in QPROCESS32.pausSem16 { // PQSEM32STRUC32 pSemThis = pInfo->pSem32Data; /* while (pSemThis) { if (pSemThis->usIndex == usSemID) return pSemThis; pSemThis = pSemThis->pNext; } */ return NULL; } /* *@@ prc32FindShrMem: * attempts to find the specified shared memory * block description. * * The return value points into the pInfo buffer. * Returns NULL if not found. * *@@added V0.9.1 (2000-02-12) [umoeller] */ PQSHRMEM32 prc32FindShrMem(PQTOPLEVEL32 pInfo, // in: as returned by prc32GetInfo USHORT usShrMemID) // in: as in QPROCESS32.pausShrMems { PQSHRMEM32 pShrMem = pInfo->pShrMemData; while (pShrMem) { if (pShrMem->usHandle == usShrMemID) return pShrMem; pShrMem = pShrMem->pNext; } return NULL; } /* *@@ prc32FindModule: * attempts to find the specified module description. * * The return value points into the pInfo buffer. * Returns NULL if not found. * *@@added V0.9.1 (2000-02-12) [umoeller] */ PQMODULE32 prc32FindModule(PQTOPLEVEL32 pInfo, // in: as returned by prc32GetInfo USHORT usHModule) { PQMODULE32 pModule = pInfo->pModuleData; while (pModule) { if (pModule->usHModule == usHModule) return pModule; pModule = pModule->pNext; } return NULL; } /* *@@ prc32FindFileData: * * *@@added V0.9.1 (2000-02-12) [umoeller] */ PQFILEDATA32 prc32FindFileData(PQTOPLEVEL32 pInfo, // in: as returned by prc32GetInfo USHORT usFileID) // in: as in QPROCESS32.pausFds { PQFILEDATA32 pFile = pInfo->pFileData; while ( (pFile) && (pFile->ulRecType == 8) // this is necessary, we'll crash otherwise!! ) { ULONG ul; // for some reason, there is an array in the file struct, // so search the array for the SFN for (ul = 0; ul < pFile->ulCFiles; ul++) { if (pFile->paFiles[ul].usSFN == usFileID) return pFile; } pFile = pFile->pNext; } return NULL; } /* *@@ prc32KillProcessTree: * *@@added XWP V1.0.10 (2014-12-07) [pr] */ void prc32KillProcessTree(ULONG pid) { APIRET arc; PQTOPLEVEL32 pInfo = prc32GetInfo2(QS32_PROCESS, &arc); if (arc == NO_ERROR) { prc32KillProcessTree2(pInfo->pProcessData, pid); DosKillProcess(DKP_PROCESS, pid); prc32FreeInfo(pInfo); } } /* *@@ prc32KillProcessTree2: * *@@added XWP V1.0.10 (2014-12-07) [pr] */ void prc32KillProcessTree2(PQPROCESS32 pProcThis, ULONG pid) { while (pProcThis && pProcThis->ulRecType == 1) { PQTHREAD32 t = pProcThis->pThreads; if (pProcThis->usPPID == pid) { prc32KillProcessTree2(pProcThis, pProcThis->usPID); DosKillProcess(DKP_PROCESS, pProcThis->usPID); } // for next process, skip the threads info; // the next process block comes after the threads t += pProcThis->usThreadCount; pProcThis = (PQPROCESS32)t; } }