This program displays a listview with information on all running processes, retrieved from NtQuerySystemInformation. It will probably only run on Win2K or greater.
The columns can be sorted and also reordered (via drag and drop). You can track/untrack a certain process by selecting a row, which will be shown in yellow. Data that has changed inbetween refreshes will be shown in green or blue, depending on if the data has decreased or increased.
You may do whatever you want with the code. Enjoy.
The columns can be sorted and also reordered (via drag and drop). You can track/untrack a certain process by selecting a row, which will be shown in yellow. Data that has changed inbetween refreshes will be shown in green or blue, depending on if the data has decreased or increased.
You may do whatever you want with the code. Enjoy.

Code:
#PBFORMS CREATED V1.51 #COMPILE EXE #DIM ALL #OPTION VERSION5 'Win2000(NT5), WinXP(NT5.1), WinVista(NT6.0) #INCLUDE "WIN32API.INC" $Program = "Task Manager Emulator" #PBFORMS BEGIN INCLUDES %USEMACROS = 1 #IF NOT %DEF(%WINAPI) #INCLUDE "WIN32API.INC" #ENDIF #IF NOT %DEF(%COMMCTRL_INC) #INCLUDE "COMMCTRL.INC" #ENDIF #INCLUDE "PBForms.INC" #PBFORMS END INCLUDES #PBFORMS BEGIN CONSTANTS %IDC_Listview = 1001 %IDD_TaskManDialog = 101 #PBFORMS END CONSTANTS DECLARE CALLBACK FUNCTION ShowTaskManDialogProc() DECLARE FUNCTION ShowTaskManDialog(BYVAL hParent AS DWORD) AS LONG #PBFORMS DECLARATIONS %TIMERID = 2000 %WM_REFRESHLISTVIEW = 2001 'message to the dialog to refresh the listview %ListviewColumnCount = 24 '-- assign a number to each column, numbering them in the desired starting order: 'FYI: the listview will be initialized with %LVS_EX_HEADERDRAGDROP to allow the user to rearrange the columns via drag and drop %ColumnNumber_Name = 0 %ColumnNumber_ID = 1 %ColumnNumber_ParentProcessID = 2 %ColumnNumber_CreateTime = 3 %ColumnNumber_RunningTime = 4 %ColumnNumber_UserTime = 5 %ColumnNumber_KernelTime = 6 %ColumnNumber_TotalCPUTime = 7 %ColumnNumber_CPUPercent = 8 %ColumnNumber_BasePriority = 9 %ColumnNumber_HandleCount = 10 %ColumnNumber_NumberOfThreads = 11 %ColumnNumber_PeakVirtualSize = 12 %ColumnNumber_VirtualSize = 13 %ColumnNumber_PageFaultCount = 14 %ColumnNumber_PeakWorkingSetSize = 15 %ColumnNumber_WorkingSetSize = 16 %ColumnNumber_PrivatePageCount = 17 %ColumnNumber_ReadOperationCount = 18 %ColumnNumber_WriteOperationCount = 19 %ColumnNumber_OtherOperationCount = 20 %ColumnNumber_ReadTransferCount = 21 %ColumnNumber_WriteTransferCount = 22 %ColumnNumber_OtherTransferCount = 23 DECLARE FUNCTION NtQuerySystemInformation LIB "ntdll.dll" ALIAS "NtQuerySystemInformation" (BYVAL SystemInformationClass AS DWORD, BYVAL lpvBuffer AS DWORD, BYVAL dwBufferSize AS DWORD, BYVAL unFlag2 AS DWORD) AS LONG %SystemProcessesAndThreadsInformation = 5 'the SystemInformationClass number to send to NtQuerySystemInformation 'NtQuerySystemInformation return values: %STATUS_SUCCESS = &h00000000 %STATUS_INFO_LENGTH_MISMATCH = &hC0000004 TYPE CLIENT_ID 'used in the SYSTEM_THREAD_INFORMATION struct UniqueProcess AS DWORD UniqueThread AS DWORD END TYPE TYPE UNICODE_STRING 'used in the SYSTEM_PROCESS_INFORMATION struct Length AS WORD 'USHORT - Length in bytes of string in Buffer MaximumLength AS WORD 'USHORT - Maximum length in bytes of Buffer pBuffer AS DWORD 'PWSTR - Pointer to unicode string END TYPE TYPE VM_COUNTERS 'used in the SYSTEM_PROCESS_INFORMATION struct (VM_COUNTERS_EX includes PrivatePageCount) PeakVirtualSize AS DWORD 'SIZE_T VirtualSize AS DWORD 'SIZE_T - this is NOT the same as Virtual Memory Size in Task Manager (according to Process Explorer) PageFaultCount AS DWORD 'ULONG PeakWorkingSetSize AS DWORD 'SIZE_T WorkingSetSize AS DWORD 'SIZE_T QuotaPeakPagedPoolUsage AS DWORD 'SIZE_T QuotaPagedPoolUsage AS DWORD 'SIZE_T QuotaPeakNonPagedPoolUsage AS DWORD 'SIZE_T QuotaNonPagedPoolUsage AS DWORD 'SIZE_T PagefileUsage AS DWORD 'SIZE_T PeakPagefileUsage AS DWORD 'SIZE_T PrivatePageCount AS DWORD 'SIZE_T <-- this "extra" item seems to be needed, or else the IO_COUNTERS will not line up correctly END TYPE TYPE IO_COUNTERS 'used in the SYSTEM_PROCESS_INFORMATION struct ReadOperationCount AS QUAD 'LARGE_INTEGER WriteOperationCount AS QUAD 'LARGE_INTEGER OtherOperationCount AS QUAD 'LARGE_INTEGER ReadTransferCount AS QUAD 'LARGE_INTEGER WriteTransferCount AS QUAD 'LARGE_INTEGER OtherTransferCount AS QUAD 'LARGE_INTEGER END TYPE TYPE SYSTEM_THREAD_INFORMATION 'used in the SYSTEM_PROCESS_INFORMATION struct ftKernelTime AS QUAD 'FILETIME 'LARGE_INTEGER - time spent in kernel mode ftUserTime AS QUAD 'FILETIME 'LARGE_INTEGER - time spent in user mode ftCreationTime AS QUAD 'FILETIME 'LARGE_INTEGER - thread creation time dwWaitTime AS DWORD 'ULONG - wait time StartAddress AS DWORD 'PVOID - start address ClientID AS CLIENT_ID 'thread and process IDs Priority AS LONG 'KPRIORITY - dynamic priority BasePriority AS LONG 'KPRIORITY - base priority dwContextSwitchCount AS DWORD 'ULONG - number of context switches STATE AS LONG 'current state WaitReason AS LONG 'wait reason END TYPE TYPE SYSTEM_PROCESS_INFORMATION NextEntryOffset AS DWORD 'ULONG - offset to the next entry NumberOfThreads AS DWORD 'ULONG - number of threads dwReserved1 AS QUAD 'reserved dwReserved2 AS QUAD 'reserved dwReserved3 AS QUAD 'reserved CreateTime AS QUAD 'LARGE_INTEGER / FILETIME - process creation time UserTime AS QUAD 'LARGE_INTEGER / FILETIME - time spent in user mode KernelTime AS QUAD 'LARGE_INTEGER / FILETIME - time spent in kernel mode ProcessName AS UNICODE_STRING 'process name BasePriority AS LONG 'KPRIORITY - base process priority ProcessID AS DWORD 'ULONG - process identifier ParentProcessID AS DWORD 'ULONG - parent process identifier HandleCount AS DWORD 'ULONG - number of handles SessionID AS DWORD 'reserved? UniqueProcessKey AS DWORD 'reserved? VMCounters AS VM_COUNTERS 'virtual memory counters IoCounters AS IO_COUNTERS 'i/o counters (NT 4.0 does not have this!) 'DWORD dCommitCharge; // bytes '<--- does this go here? don't know; don't care, for now, since SYSTEM_THREAD_INFORMATION isn't used by this program anyway Threads(1) AS SYSTEM_THREAD_INFORMATION 'threads (not sure if this is right..) 'Threads AS SYSTEM_THREAD_INFORMATION 'threads (maybe this is right?) END TYPE '-- process data arrays - these store the raw data gathered from NtQuerySystemInformation: GLOBAL gProcess_NumberOfThreads() AS DWORD GLOBAL gProcess_CreateTime() AS QUAD GLOBAL gProcess_UserTime() AS QUAD GLOBAL gProcess_KernelTime() AS QUAD GLOBAL gProcess_Name() AS STRING GLOBAL gProcess_BasePriority() AS LONG GLOBAL gProcess_ID() AS DWORD GLOBAL gProcess_ParentProcessID() AS DWORD GLOBAL gProcess_HandleCount() AS DWORD GLOBAL gProcess_PeakVirtualSize() AS DWORD GLOBAL gProcess_VirtualSize() AS DWORD GLOBAL gProcess_PageFaultCount() AS DWORD GLOBAL gProcess_PeakWorkingSetSize() AS DWORD GLOBAL gProcess_WorkingSetSize() AS DWORD GLOBAL gProcess_QuotaPeakPagedPoolUsage() AS DWORD GLOBAL gProcess_QuotaPagedPoolUsage() AS DWORD GLOBAL gProcess_QuotaPeakNonPagedPoolUsage() AS DWORD GLOBAL gProcess_QuotaNonPagedPoolUsage() AS DWORD GLOBAL gProcess_PagefileUsage() AS DWORD GLOBAL gProcess_PeakPagefileUsage() AS DWORD GLOBAL gProcess_PrivatePageCount() AS DWORD GLOBAL gProcess_ReadOperationCount() AS QUAD GLOBAL gProcess_WriteOperationCount() AS QUAD GLOBAL gProcess_OtherOperationCount() AS QUAD GLOBAL gProcess_ReadTransferCount() AS QUAD GLOBAL gProcess_WriteTransferCount() AS QUAD GLOBAL gProcess_OtherTransferCount() AS QUAD '-- sorting arrays: GLOBAL gProcess_Index() AS LONG GLOBAL gSortedIndex() AS LONG GLOBAL gColumnSortDirection() AS LONG 'toggles between 0 and -1 (ascending and descending) for each column. '-- calculated arrays - how long a process has been running, and its CPU usage: GLOBAL gProcess_RunningTime() AS QUAD GLOBAL gProcess_TotalCPUTime() AS QUAD GLOBAL gProcess_TotalCPUTime_Delta() AS QUAD GLOBAL gProcess_CPUPercent() AS SINGLE '-- delta arrays - these are used to indicate changes (via color) in the listview: ' LONGs are used in place of DWORDs since deltas can be positive or negative. GLOBAL gProcess_NumberOfThreads_Delta() AS LONG 'DWORD GLOBAL gProcess_UserTime_Delta() AS QUAD GLOBAL gProcess_KernelTime_Delta() AS QUAD GLOBAL gProcess_CPUPercent_Delta() AS SINGLE GLOBAL gProcess_BasePriority_Delta() AS LONG GLOBAL gProcess_HandleCount_Delta() AS LONG 'DWORD GLOBAL gProcess_PeakVirtualSize_Delta() AS LONG 'DWORD GLOBAL gProcess_VirtualSize_Delta() AS LONG 'DWORD GLOBAL gProcess_PageFaultCount_Delta() AS LONG 'DWORD GLOBAL gProcess_PeakWorkingSetSize_Delta() AS LONG 'DWORD GLOBAL gProcess_WorkingSetSize_Delta() AS LONG 'DWORD GLOBAL gProcess_QuotaPeakPagedPoolUsage_Delta() AS LONG 'DWORD GLOBAL gProcess_QuotaPagedPoolUsage_Delta() AS LONG 'DWORD GLOBAL gProcess_QuotaPeakNonPagedPoolUsage_Delta() AS LONG 'DWORD GLOBAL gProcess_QuotaNonPagedPoolUsage_Delta() AS LONG 'DWORD GLOBAL gProcess_PagefileUsage_Delta() AS LONG 'DWORD GLOBAL gProcess_PeakPagefileUsage_Delta() AS LONG 'DWORD GLOBAL gProcess_PrivatePageCount_Delta() AS LONG 'DWORD GLOBAL gProcess_ReadOperationCount_Delta() AS QUAD GLOBAL gProcess_WriteOperationCount_Delta() AS QUAD GLOBAL gProcess_OtherOperationCount_Delta() AS QUAD GLOBAL gProcess_ReadTransferCount_Delta() AS QUAD GLOBAL gProcess_WriteTransferCount_Delta() AS QUAD GLOBAL gProcess_OtherTransferCount_Delta() AS QUAD '============================================================================================================ '== PBMAIN ================================================================================================== '============================================================================================================ FUNCTION PBMAIN() LOCAL hModule AS DWORD hModule = GetModuleHandle("NTDLL.DLL") IF hModule = %NULL THEN MSGBOX "GetModuleHandle(NTDLL.DLL) failed.", %MB_ICONERROR OR %MB_SYSTEMMODAL, $Program EXIT FUNCTION ELSEIF GetProcAddress(hModule, "NtQuerySystemInformation") = %NULL THEN 'is NtQuerySystemInformation unavailable for this computer? MSGBOX "This program will not run on this computer, because NtQuerySystemInformation is unavailable.", %MB_ICONERROR OR %MB_SYSTEMMODAL, $Program EXIT FUNCTION END IF PBFormsInitComCtls (%ICC_WIN95_CLASSES OR %ICC_DATE_CLASSES OR %ICC_INTERNET_CLASSES) ShowTaskManDialog %HWND_DESKTOP END FUNCTION '============================================================================================================ '== CONVERTSECONDSINTOFORMATTEDSTRING ======================================================================= '============================================================================================================ ' Converts Elapsed Seconds to Elapsed Time (DDdHHhMMmSSsZZZms) This function is probably overkill (FORMAT$ could be used instead, but this function is faster) FUNCTION ConvertSecondsIntoFormattedString (BYVAL timediff AS DOUBLE) AS STRING LOCAL milliseconds, seconds, minutes, hours, days AS LONG LOCAL sTime AS STRING LOCAL a AS BYTE PTR 'sTime = "00d00h00m00s" ' 12 chars sTime = "00d00h00m00s000ms" ' 17 chars 'sTime = "00:00:00:00.000" ' 15 chars a = STRPTR(sTime) seconds = FIX(timediff) 'total seconds minutes = seconds \ 60 'total mins hours = minutes \ 60 'total hrs days = hours \ 24 'total days milliseconds = (timediff - seconds) * 1000 '0-999 ms seconds = seconds - minutes * 60 '0-60 seconds minutes = minutes - hours * 60 '0-59 minutes hours = hours - days * 24 '0-23 hrs IF days > 0 THEN IF days < 10 THEN @a[0] = 32 'CHR$(32) = space ELSE '@a[0] = FIX(days\10) + 48 @a[0] = days \ 10 + 48 'CHR$(48) thru CHR$(57) = numbers 0 thru 10 END IF @a[1] = days MOD 10 + 48 'CHR$(48) thru CHR$(57) = numbers 0 thru 10 ELSE 'equals 0 @a[0] = 32 'CHR$(32) = space @a[1] = 32 'CHR$(32) = space @a[2] = 32 'CHR$(32) = space END IF IF hours > 0 THEN IF hours < 10 THEN IF days > 0 THEN @a[3] = 48 'CHR$(48) = number 0 ELSE @a[3] = 32 'CHR$(32) = space END IF ELSE '@a[3] = FIX(hours\10) + 48 @a[3] = hours \ 10 + 48 'CHR$(48) thru CHR$(57) = numbers 0 thru 10 END IF @a[4] = hours MOD 10 + 48 'CHR$(48) thru CHR$(57) = numbers 0 thru 10 ELSEIF days > 0 THEN @a[3] = 48 'CHR$(48) = number 0 @a[4] = 48 'CHR$(48) = number 0 ELSE @a[3] = 32 'CHR$(32) = space @a[4] = 32 'CHR$(32) = space @a[5] = 32 'CHR$(32) = space END IF IF minutes > 0 THEN IF minutes < 10 THEN IF hours > 0 OR days > 0 THEN @a[6] = 48 'CHR$(48) = number 0 ELSE @a[6] = 32 'CHR$(32) = space END IF ELSE '@a[6] = FIX(minutes\10) + 48 @a[6] = minutes \ 10 + 48 'CHR$(48) thru CHR$(57) = numbers 0 thru 10 END IF @a[7] = minutes MOD 10 + 48 'CHR$(48) thru CHR$(57) = numbers 0 thru 10 ELSEIF hours > 0 OR days > 0 THEN @a[6] = 48 'CHR$(48) = number 0 @a[7] = 48 'CHR$(48) = number 0 ELSE @a[6] = 32 'CHR$(32) = space @a[7] = 32 'CHR$(32) = space @a[8] = 32 'CHR$(32) = space END IF IF seconds > 0 THEN IF seconds < 10 THEN IF minutes > 0 OR hours > 0 OR days > 0 THEN @a[9] = 48 'CHR$(48) = number 0 ELSE @a[9] = 32 'CHR$(32) = space END IF ELSE '@a[9] = FIX(seconds\10) + 48 @a[9] = seconds \ 10 + 48 'CHR$(48) thru CHR$(57) = numbers 0 thru 10 END IF @a[10] = seconds MOD 10 + 48 'CHR$(48) thru CHR$(57) = numbers 0 thru 10 ELSEIF minutes > 0 OR hours > 0 OR days > 0 THEN @a[9] = 48 'CHR$(48) = number 0 @a[10] = 48 'CHR$(48) = number 0 ELSE @a[9] = 32 'CHR$(32) = space @a[10] = 32 'CHR$(32) = space @a[11] = 32 'CHR$(32) = space END IF IF milliseconds < 100 THEN IF seconds > 0 OR minutes > 0 OR hours > 0 OR days > 0 THEN @a[12] = 48 'CHR$(48) = number 0 ELSE @a[12] = 32 'CHR$(32) = space END IF IF milliseconds < 10 THEN IF seconds > 0 OR minutes > 0 OR hours > 0 OR days > 0 THEN @a[13] = 48 'CHR$(48) = number 0 ELSE @a[13] = 32 'CHR$(32) = space END IF @a[14] = milliseconds + 48 'CHR$(48) thru CHR$(57) = numbers 0 thru 10 ELSE '@a[13] = FIX(milliseconds\10) + 48 @a[13] = milliseconds \ 10 + 48 'CHR$(48) thru CHR$(57) = numbers 0 thru 10 END IF ELSE '@a[12] = FIX(milliseconds\100) + 48 'hundreds '@a[13] = FIX((milliseconds - FIX(milliseconds\100) * 100) \ 10) + 48 'tens @a[12] = milliseconds \ 100 + 48 'hundreds @a[13] = (milliseconds - (milliseconds \ 100) * 100) \ 10 + 48 'CHR$(48) thru CHR$(57) = numbers 0 thru 10 'tens END IF @a[14] = milliseconds MOD 10 + 48 'CHR$(48) thru CHR$(57) = numbers 0 thru 10 'ones FUNCTION = sTime END FUNCTION '============================================================================================================ '== ShowTaskManDialogProc =================================================================================== '============================================================================================================ CALLBACK FUNCTION ShowTaskManDialogProc() STATIC hListview AS DWORD LOCAL x, row, col AS LONG LOCAL LVI AS LV_ITEM LOCAL szBuf AS ASCIIZ * 1000 LOCAL pNMLV AS NM_LISTVIEW PTR 'for detecting listview double-click (notify) LOCAL lpLvCd AS NMLVCUSTOMDRAW PTR 'for custom drawing the listview cells (BG color, etc) '-- for custom listview font: LOCAL lf AS LOGFONT STATIC hListviewFont AS LONG LOCAL hDC AS DWORD '-- for process tracking: STATIC static_PIDtotrack AS DWORD '-- index of column to sort by: STATIC static_ColumnToSortBy AS LONG '-- for calling NtQuerySystemInformation: LOCAL NtStatus AS LONG LOCAL pSysProcInfo AS SYSTEM_PROCESS_INFORMATION POINTER LOCAL SystemInformationLength AS DWORD STATIC static_ReturnLength AS DWORD LOCAL pBuffer AS DWORD PTR LOCAL sBuffer AS STRING '-- for converting a process's start time into a readable date/time format: LOCAL FT, LT, CurrentFT AS FILETIME LOCAL ST AS SYSTEMTIME LOCAL CurrentTime AS QUAD '-- a total count of all processes currently running: LOCAL ProcessCount AS LONG LOCAL CurrentCPUPercent AS SINGLE '-- used to calculate current CPU usage for each process: LOCAL previndex AS LONG LOCAL TotalCPUTimeIncreaseOfAllProcesses AS QUAD LOCAL CPUpercentage AS SINGLE SELECT CASE AS LONG CBMSG CASE %WM_NOTIFY SELECT CASE LOWRD(CBWPARAM) CASE %IDC_Listview pNMLV = CBLPARAM SELECT CASE @pNMLV.hdr.code CASE %NM_CLICK 'left click '-- read the PID from the row's lParam (better than reading from the PID column, which could potentially change position): lvi.mask = %LVIF_PARAM lvi.iItem = @pNMLV.iItem '@pNMLV.iItem = row clicked on (0 based) lvi.iSubItem = 0 IF SendMessage(hListview, %LVM_GETITEM, 0, VARPTR(lvi)) = %TRUE THEN 'success? IF static_PIDtotrack = lvi.lParam THEN 'already tracking? static_PIDtotrack = -1 'stop tracking ELSE static_PIDtotrack = lvi.lParam 'track this row/PID END IF '-- refresh the listview (InvalidateRect + UpdateWindow doesn't flicker like CONTROL REDRAW does): 'CONTROL REDRAW CBHNDL, %IDC_TopicsListview InvalidateRect(hListview, BYVAL 0, %FALSE) UpdateWindow(hListview) END IF CASE %NM_RCLICK 'right click 'not used in this program (yet) CASE %LVN_COLUMNCLICK 'user clicked on a column header ' @pNMLV.iItem = row clicked on (0 based) ----> THIS WILL ALWAYS BE -1 FOR %LVN_COLUMNCLICK ' @pNMLV.iSubItem = column clicked on (0 based) static_ColumnToSortBy = @pNMLV.isubItem '-- reverse sorting direction: gColumnSortDirection(static_ColumnToSortBy) = NOT gColumnSortDirection(static_ColumnToSortBy) '-- refresh the listview: CALL PostMessage(CBHNDL, %WM_REFRESHLISTVIEW, 0, 0) CASE %NM_CUSTOMDRAW 'use custom drawing in the topics listview lpLvCd = CBLPARAM SELECT CASE @lpLvCd.nmcd.dwDrawStage CASE %CDDS_PREPAINT 'Before the painting cycle begins. (this is the first message received) FUNCTION = %CDRF_NOTIFYITEMDRAW 'tell Windows we want messages before and after drawing an item. (%CDDS_ITEMPREPAINT OR %CDDS_SUBITEM) and (%CDDS_ITEMPOSTPAINT OR %CDDS_SUBITEM) notifications CASE %CDDS_ITEMPREPAINT 'Before an item is drawn. (this is the second message received) FUNCTION = %CDRF_NOTIFYSUBITEMDRAW 'tell Windows we want messages before and after drawing a sub-item. 'FUNCTION = %CDRF_NEWFONT 'return this if we changed the font CASE %CDDS_SUBITEM OR %CDDS_ITEMPREPAINT 'this message indicates that drawing is about to occur for a subitem (an individual cell in the listview) 'This notification is received only if the listview is in report mode and it returned %CDRF_NOTIFYSUBITEMREDRAW for %CDDS_ITEMPREPAINT ' @lpLvCd.iSubItem = column (0 based) ' @lpLvCd.nmcd.dwItemSpec = row (0 based) 'IF static_PIDtotrack = gProcess_ID(@lpLvCd.nmcd.dwItemSpec) THEN IF static_PIDtotrack = gProcess_ID(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) THEN '-- highlight the process that's being tracked: @lpLvCd.clrText = %BLACK @lpLvCd.clrTextBk = %YELLOW 'ELSEIF @lpLvCd.iSubItem = 0 THEN 'is this cell in the first column? (iSubItem is the column's position) ' @lpLvCd.clrText = %BLACK ' @lpLvCd.clrTextBk = RGB(158,190,189) ELSE 'a cell NOT in the first column and NOT in a row containing a process being watched IF (@lpLvCd.nmcd.dwItemSpec MOD 2) = 0 THEN 'odd row @lpLvCd.clrText = %BLACK @lpLvCd.clrTextBk = %WHITE ELSE 'even row @lpLvCd.clrText = %BLACK @lpLvCd.clrTextBk = RGB(197,235,245) 'RGB(197,216,215) END IF END IF '-- indicate delta changes (over-ruling the selected color): IF @lpLvCd.iSubItem = %ColumnNumber_UserTime AND gProcess_UserTime_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) <> 0 THEN @lpLvCd.clrText = %BLACK @lpLvCd.clrTextBk = IIF&(gProcess_UserTime_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) > 0, %CYAN, %GREEN) ELSEIF @lpLvCd.iSubItem = %ColumnNumber_KernelTime AND gProcess_KernelTime_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) <> 0 THEN @lpLvCd.clrText = %BLACK @lpLvCd.clrTextBk = IIF&(gProcess_KernelTime_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) > 0, %CYAN, %GREEN) ELSEIF @lpLvCd.iSubItem = %ColumnNumber_TotalCPUTime AND gProcess_TotalCPUTime_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) <> 0 THEN @lpLvCd.clrText = %BLACK @lpLvCd.clrTextBk = IIF&(gProcess_TotalCPUTime_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) > 0, %CYAN, %GREEN) ELSEIF @lpLvCd.iSubItem = %ColumnNumber_CPUPercent AND gProcess_CPUPercent(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) <> 0 THEN @lpLvCd.clrText = %BLACK @lpLvCd.clrTextBk = IIF&(gProcess_CPUPercent_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) > 0, %CYAN, %GREEN) ELSEIF @lpLvCd.iSubItem = %ColumnNumber_BasePriority AND gProcess_BasePriority_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) <> 0 THEN @lpLvCd.clrText = %BLACK @lpLvCd.clrTextBk = IIF&(gProcess_BasePriority_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) > 0, %CYAN, %GREEN) ELSEIF @lpLvCd.iSubItem = %ColumnNumber_HandleCount AND gProcess_HandleCount_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) <> 0 THEN @lpLvCd.clrText = %BLACK @lpLvCd.clrTextBk = IIF&(gProcess_HandleCount_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) > 0, %CYAN, %GREEN) ELSEIF @lpLvCd.iSubItem = %ColumnNumber_NumberOfThreads AND gProcess_NumberOfThreads_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) <> 0 THEN @lpLvCd.clrText = %BLACK @lpLvCd.clrTextBk = IIF&(gProcess_NumberOfThreads_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) > 0, %CYAN, %GREEN) ELSEIF @lpLvCd.iSubItem = %ColumnNumber_PeakVirtualSize AND gProcess_PeakVirtualSize_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) <> 0 THEN @lpLvCd.clrText = %BLACK @lpLvCd.clrTextBk = IIF&(gProcess_PeakVirtualSize_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) > 0, %CYAN, %GREEN) ELSEIF @lpLvCd.iSubItem = %ColumnNumber_VirtualSize AND gProcess_VirtualSize_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) <> 0 THEN @lpLvCd.clrText = %BLACK @lpLvCd.clrTextBk = IIF&(gProcess_VirtualSize(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) > 0, %CYAN, %GREEN) ELSEIF @lpLvCd.iSubItem = %ColumnNumber_PageFaultCount AND gProcess_PageFaultCount_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) <> 0 THEN @lpLvCd.clrText = %BLACK @lpLvCd.clrTextBk = IIF&(gProcess_PageFaultCount_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) > 0, %CYAN, %GREEN) ELSEIF @lpLvCd.iSubItem = %ColumnNumber_PeakWorkingSetSize AND gProcess_PeakWorkingSetSize_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) <> 0 THEN @lpLvCd.clrText = %BLACK @lpLvCd.clrTextBk = IIF&(gProcess_PeakWorkingSetSize_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) > 0, %CYAN, %GREEN) ELSEIF @lpLvCd.iSubItem = %ColumnNumber_WorkingSetSize AND gProcess_WorkingSetSize_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) <> 0 THEN @lpLvCd.clrText = %BLACK @lpLvCd.clrTextBk = IIF&(gProcess_WorkingSetSize_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) > 0, %CYAN, %GREEN) ELSEIF @lpLvCd.iSubItem = %ColumnNumber_PrivatePageCount AND gProcess_PrivatePageCount_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) <> 0 THEN @lpLvCd.clrText = %BLACK @lpLvCd.clrTextBk = IIF&(gProcess_PrivatePageCount_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) > 0, %CYAN, %GREEN) ELSEIF @lpLvCd.iSubItem = %ColumnNumber_ReadOperationCount AND gProcess_ReadOperationCount_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) <> 0 THEN @lpLvCd.clrText = %BLACK @lpLvCd.clrTextBk = IIF&(gProcess_ReadOperationCount_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) > 0, %CYAN, %GREEN) ELSEIF @lpLvCd.iSubItem = %ColumnNumber_WriteOperationCount AND gProcess_WriteOperationCount_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) <> 0 THEN @lpLvCd.clrText = %BLACK @lpLvCd.clrTextBk = IIF&(gProcess_WriteOperationCount_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) > 0, %CYAN, %GREEN) ELSEIF @lpLvCd.iSubItem = %ColumnNumber_OtherOperationCount AND gProcess_OtherOperationCount_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) <> 0 THEN @lpLvCd.clrText = %BLACK @lpLvCd.clrTextBk = IIF&(gProcess_OtherOperationCount_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) > 0, %CYAN, %GREEN) ELSEIF @lpLvCd.iSubItem = %ColumnNumber_ReadTransferCount AND gProcess_ReadTransferCount_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) <> 0 THEN @lpLvCd.clrText = %BLACK @lpLvCd.clrTextBk = IIF&(gProcess_ReadTransferCount_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) > 0, %CYAN, %GREEN) ELSEIF @lpLvCd.iSubItem = %ColumnNumber_WriteTransferCount AND gProcess_WriteTransferCount_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) <> 0 THEN @lpLvCd.clrText = %BLACK @lpLvCd.clrTextBk = IIF&(gProcess_WriteTransferCount_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) > 0, %CYAN, %GREEN) ELSEIF @lpLvCd.iSubItem = %ColumnNumber_OtherTransferCount AND gProcess_OtherTransferCount_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) <> 0 THEN @lpLvCd.clrText = %BLACK @lpLvCd.clrTextBk = IIF&(gProcess_OtherTransferCount_Delta(gSortedIndex(@lpLvCd.nmcd.dwItemSpec)) > 0, %CYAN, %GREEN) END IF '-- change the font (NOTE - the row height will not match the font size unless %WM_SETFONT was called during %WM_INITDIALOG): IF hListviewFont THEN SelectObject(@lpLvCd.nmcd.hDC, hListviewFont) 'hListviewFont should be STATIC (initialized during %WM_INITDIALOG) FUNCTION = %CDRF_NEWFONT 'MS says to return %CDRF_NEWFONT if the color or font was changed END SELECT '@lpLvCd.nmcd.dwDrawStage - current drawing stage for %NM_CUSTOMDRAW END SELECT '@pNMLV.hdr.code - message sent to the control END SELECT 'LOWRD(CBWPARAM) - control ID '------------------------------------------------------------------------------------------------------ CASE %WM_TIMER IF CBWPARAM = %TIMERID THEN PostMessage(CBHNDL, %WM_REFRESHLISTVIEW, 0, 0) '------------------------------------------------------------------------------------------------------ CASE %WM_REFRESHLISTVIEW 'this message is sent from %WM_INITDIALOG and %WM_TIMER, so the listview can refresh at startup or during the timer DO ' the very first call to NtQuerySystemInformation can have SystemInformationLength set to 0. ' when the buffer is too small, %STATUS_INFO_LENGTH_MISMATCH will be returned along with the required buffer size (static_ReturnLength). ' we can then call NtQuerySystemInformation again with a bigger buffer size. the result should then be %STATUS_SUCCESS. ' static_ReturnLength is STATIC so it will be remembered inbetween updates. ' %STATUS_INFO_LENGTH_MISMATCH should only occur during startup, and when the total process count reaches a new maximum. SystemInformationLength = static_ReturnLength '<-- should one be added to the static_ReturnLength to account for an ending $NUL? don't know. but for now, don't worry about it :) sBuffer = NUL$(static_ReturnLength) pBuffer = STRPTR(sBuffer) NtStatus = NtQuerySystemInformation(%SystemProcessesAndThreadsInformation, pBuffer, SystemInformationLength, VARPTR(static_ReturnLength)) LOOP WHILE NtStatus = %STATUS_INFO_LENGTH_MISMATCH 'it should never need to loop more than once '-- if NtStatus is not %STATUS_SUCCESS, then something is wrong, so we'll just abort: IF NtStatus <> %STATUS_SUCCESS THEN MSGBOX "NtQuerySystemInformation failed.", %MB_ICONERROR OR %MB_SYSTEMMODAL, $Program EXIT FUNCTION END IF '-- at this point, we can assume that we have successfully retrieved the information. ' now set the struct's pointer to point to the returned buffer. ' in a sense, the struct acts as a template which is moved along the buffer as directed by @pSysProcInfo.NextEntryOffset, allowing us to look at the data for each process. pSysProcInfo = pBuffer '-- get the current time (as FILETIME): it'll be used to calculate how long a process has been running (optional column; not important at all) GetSystemTimeAsFileTime(CurrentFT) CurrentTime = MAK(QUAD, CurrentFT.dwLowDateTime, CurrentFT.dwHighDateTime) '-- reset these. they'll be recalculated during the loop: RESET ProcessCount, TotalCPUTimeIncreaseOfAllProcesses '-- loop through all the processes: DO '-- increase the size of the arrays as needed (note that they are zero based, and they never need to shrink): IF ProcessCount > UBOUND(gProcess_Index) THEN REDIM PRESERVE gProcess_Index(ProcessCount) AS LONG REDIM PRESERVE gSortedIndex(ProcessCount) AS LONG REDIM PRESERVE gProcess_NumberOfThreads(ProcessCount) AS GLOBAL DWORD REDIM PRESERVE gProcess_CreateTime(ProcessCount) AS GLOBAL QUAD REDIM PRESERVE gProcess_UserTime(ProcessCount) AS GLOBAL QUAD REDIM PRESERVE gProcess_KernelTime(ProcessCount) AS GLOBAL QUAD REDIM PRESERVE gProcess_TotalCPUTime(ProcessCount) AS GLOBAL QUAD 'calculated value REDIM PRESERVE gProcess_RunningTime(ProcessCount) AS GLOBAL QUAD 'calculated value REDIM PRESERVE gProcess_CPUPercent(ProcessCount) AS GLOBAL SINGLE 'calculated value REDIM PRESERVE gProcess_Name(ProcessCount) AS GLOBAL STRING REDIM PRESERVE gProcess_BasePriority(ProcessCount) AS GLOBAL LONG REDIM PRESERVE gProcess_ID(ProcessCount) AS GLOBAL DWORD REDIM PRESERVE gProcess_ParentProcessID(ProcessCount) AS GLOBAL DWORD REDIM PRESERVE gProcess_HandleCount(ProcessCount) AS GLOBAL DWORD '-- memory data: REDIM PRESERVE gProcess_PeakVirtualSize(ProcessCount) AS GLOBAL DWORD REDIM PRESERVE gProcess_VirtualSize(ProcessCount) AS GLOBAL DWORD REDIM PRESERVE gProcess_PageFaultCount(ProcessCount) AS GLOBAL DWORD REDIM PRESERVE gProcess_PeakWorkingSetSize(ProcessCount) AS GLOBAL DWORD REDIM PRESERVE gProcess_WorkingSetSize(ProcessCount) AS GLOBAL DWORD REDIM PRESERVE gProcess_QuotaPeakPagedPoolUsage(ProcessCount) AS GLOBAL DWORD REDIM PRESERVE gProcess_QuotaPagedPoolUsage(ProcessCount) AS GLOBAL DWORD REDIM PRESERVE gProcess_QuotaPeakNonPagedPoolUsage(ProcessCount) AS GLOBAL DWORD REDIM PRESERVE gProcess_QuotaNonPagedPoolUsage(ProcessCount) AS GLOBAL DWORD REDIM PRESERVE gProcess_PagefileUsage(ProcessCount) AS GLOBAL DWORD REDIM PRESERVE gProcess_PeakPagefileUsage(ProcessCount) AS GLOBAL DWORD REDIM PRESERVE gProcess_PrivatePageCount(ProcessCount) AS GLOBAL DWORD '-- I/O data: REDIM PRESERVE gProcess_ReadOperationCount(ProcessCount) AS GLOBAL QUAD REDIM PRESERVE gProcess_WriteOperationCount(ProcessCount) AS GLOBAL QUAD REDIM PRESERVE gProcess_OtherOperationCount(ProcessCount) AS GLOBAL QUAD REDIM PRESERVE gProcess_ReadTransferCount(ProcessCount) AS GLOBAL QUAD REDIM PRESERVE gProcess_WriteTransferCount(ProcessCount) AS GLOBAL QUAD REDIM PRESERVE gProcess_OtherTransferCount(ProcessCount) AS GLOBAL QUAD '== deltas (optional; these are used to indicate changes in the listview) ======================================== REDIM PRESERVE gProcess_NumberOfThreads_Delta(ProcessCount) AS GLOBAL LONG 'DWORD REDIM PRESERVE gProcess_UserTime_Delta(ProcessCount) AS GLOBAL QUAD REDIM PRESERVE gProcess_KernelTime_Delta(ProcessCount) AS GLOBAL QUAD REDIM PRESERVE gProcess_TotalCPUTime_Delta(ProcessCount) AS GLOBAL QUAD REDIM PRESERVE gProcess_CPUPercent_Delta(ProcessCount) AS GLOBAL SINGLE REDIM PRESERVE gProcess_BasePriority_Delta(ProcessCount) AS GLOBAL LONG REDIM PRESERVE gProcess_HandleCount_Delta(ProcessCount) AS GLOBAL LONG 'DWORD '-- memory data: REDIM PRESERVE gProcess_PeakVirtualSize_Delta(ProcessCount) AS GLOBAL LONG 'DWORD REDIM PRESERVE gProcess_VirtualSize_Delta(ProcessCount) AS GLOBAL LONG 'DWORD REDIM PRESERVE gProcess_PageFaultCount_Delta(ProcessCount) AS GLOBAL LONG 'DWORD REDIM PRESERVE gProcess_PeakWorkingSetSize_Delta(ProcessCount) AS GLOBAL LONG 'DWORD REDIM PRESERVE gProcess_WorkingSetSize_Delta(ProcessCount) AS GLOBAL LONG 'DWORD REDIM PRESERVE gProcess_QuotaPeakPagedPoolUsage_Delta(ProcessCount) AS GLOBAL LONG 'DWORD REDIM PRESERVE gProcess_QuotaPagedPoolUsage_Delta(ProcessCount) AS GLOBAL LONG 'DWORD REDIM PRESERVE gProcess_QuotaPeakNonPagedPoolUsage_Delta(ProcessCount) AS GLOBAL LONG 'DWORD REDIM PRESERVE gProcess_QuotaNonPagedPoolUsage_Delta(ProcessCount) AS GLOBAL LONG 'DWORD REDIM PRESERVE gProcess_PagefileUsage_Delta(ProcessCount) AS GLOBAL LONG 'DWORD REDIM PRESERVE gProcess_PeakPagefileUsage_Delta(ProcessCount) AS GLOBAL LONG 'DWORD REDIM PRESERVE gProcess_PrivatePageCount_Delta(ProcessCount) AS GLOBAL LONG 'DWORD '-- I/O data: REDIM PRESERVE gProcess_ReadOperationCount_Delta(ProcessCount) AS GLOBAL QUAD REDIM PRESERVE gProcess_WriteOperationCount_Delta(ProcessCount) AS GLOBAL QUAD REDIM PRESERVE gProcess_OtherOperationCount_Delta(ProcessCount) AS GLOBAL QUAD REDIM PRESERVE gProcess_ReadTransferCount_Delta(ProcessCount) AS GLOBAL QUAD REDIM PRESERVE gProcess_WriteTransferCount_Delta(ProcessCount) AS GLOBAL QUAD REDIM PRESERVE gProcess_OtherTransferCount_Delta(ProcessCount) AS GLOBAL QUAD '== backup copies of arrays that may have a delta ================================================================== 'NOTE: the PID will never have a delta, but it's required for obtaining the index to all the other PREVIOUS_ arrays ' the CreateTime won't have a delta either, but it's used as a safety measure for verifying the correct process (PIDs are recycled by Windows) REDIM PRESERVE PREVIOUS_NumberOfThreads(ProcessCount) AS STATIC DWORD REDIM PRESERVE PREVIOUS_CreateTime(ProcessCount) AS STATIC QUAD REDIM PRESERVE PREVIOUS_UserTime(ProcessCount) AS STATIC QUAD REDIM PRESERVE PREVIOUS_KernelTime(ProcessCount) AS STATIC QUAD REDIM PRESERVE PREVIOUS_TotalCPUTime(ProcessCount) AS STATIC QUAD 'calculated value REDIM PRESERVE PREVIOUS_RunningTime(ProcessCount) AS STATIC QUAD 'calculated value REDIM PRESERVE PREVIOUS_CPUPercent(ProcessCount) AS STATIC SINGLE 'calculated value REDIM PRESERVE PREVIOUS_BasePriority(ProcessCount) AS STATIC LONG REDIM PRESERVE PREVIOUS_ID(ProcessCount) AS STATIC DWORD REDIM PRESERVE PREVIOUS_HandleCount(ProcessCount) AS STATIC DWORD '-- memory data: REDIM PRESERVE PREVIOUS_PeakVirtualSize(ProcessCount) AS STATIC DWORD REDIM PRESERVE PREVIOUS_VirtualSize(ProcessCount) AS STATIC DWORD REDIM PRESERVE PREVIOUS_PageFaultCount(ProcessCount) AS STATIC DWORD REDIM PRESERVE PREVIOUS_PeakWorkingSetSize(ProcessCount) AS STATIC DWORD REDIM PRESERVE PREVIOUS_WorkingSetSize(ProcessCount) AS STATIC DWORD REDIM PRESERVE PREVIOUS_QuotaPeakPagedPoolUsage(ProcessCount) AS STATIC DWORD REDIM PRESERVE PREVIOUS_QuotaPagedPoolUsage(ProcessCount) AS STATIC DWORD REDIM PRESERVE PREVIOUS_QuotaPeakNonPagedPoolUsage(ProcessCount) AS STATIC DWORD REDIM PRESERVE PREVIOUS_QuotaNonPagedPoolUsage(ProcessCount) AS STATIC DWORD REDIM PRESERVE PREVIOUS_PagefileUsage(ProcessCount) AS STATIC DWORD REDIM PRESERVE PREVIOUS_PeakPagefileUsage(ProcessCount) AS STATIC DWORD REDIM PRESERVE PREVIOUS_PrivatePageCount(ProcessCount) AS STATIC DWORD '-- I/O data: REDIM PRESERVE PREVIOUS_ReadOperationCount(ProcessCount) AS STATIC QUAD REDIM PRESERVE PREVIOUS_WriteOperationCount(ProcessCount) AS STATIC QUAD REDIM PRESERVE PREVIOUS_OtherOperationCount(ProcessCount) AS STATIC QUAD REDIM PRESERVE PREVIOUS_ReadTransferCount(ProcessCount) AS STATIC QUAD REDIM PRESERVE PREVIOUS_WriteTransferCount(ProcessCount) AS STATIC QUAD REDIM PRESERVE PREVIOUS_OtherTransferCount(ProcessCount) AS STATIC QUAD END IF 'gProcess_Index() is used for sorting all the arrays: gProcess_Index(ProcessCount) = ProcessCount gProcess_ID(ProcessCount) = @pSysProcInfo.ProcessID gProcess_ParentProcessID(ProcessCount) = @pSysProcInfo.ParentProcessID gProcess_NumberOfThreads(ProcessCount) = @pSysProcInfo.NumberOfThreads gProcess_CreateTime(ProcessCount) = @pSysProcInfo.CreateTime gProcess_UserTime(ProcessCount) = @pSysProcInfo.UserTime gProcess_KernelTime(ProcessCount) = @pSysProcInfo.KernelTime gProcess_TotalCPUTime(ProcessCount) = @pSysProcInfo.UserTime + @pSysProcInfo.KernelTime gProcess_BasePriority(ProcessCount) = @pSysProcInfo.BasePriority gProcess_HandleCount(ProcessCount) = @pSysProcInfo.HandleCount '-- memory data: gProcess_PeakVirtualSize(ProcessCount) = @pSysProcInfo.VMCounters.PeakVirtualSize gProcess_VirtualSize(ProcessCount) = @pSysProcInfo.VMCounters.VirtualSize gProcess_PageFaultCount(ProcessCount) = @pSysProcInfo.VMCounters.PageFaultCount gProcess_PeakWorkingSetSize(ProcessCount) = @pSysProcInfo.VMCounters.PeakWorkingSetSize gProcess_WorkingSetSize(ProcessCount) = @pSysProcInfo.VMCounters.WorkingSetSize gProcess_QuotaPeakPagedPoolUsage(ProcessCount) = @pSysProcInfo.VMCounters.QuotaPeakPagedPoolUsage gProcess_QuotaPagedPoolUsage(ProcessCount) = @pSysProcInfo.VMCounters.QuotaPagedPoolUsage gProcess_QuotaPeakNonPagedPoolUsage(ProcessCount) = @pSysProcInfo.VMCounters.QuotaPeakNonPagedPoolUsage gProcess_QuotaNonPagedPoolUsage(ProcessCount) = @pSysProcInfo.VMCounters.QuotaNonPagedPoolUsage gProcess_PagefileUsage(ProcessCount) = @pSysProcInfo.VMCounters.PagefileUsage gProcess_PeakPagefileUsage(ProcessCount) = @pSysProcInfo.VMCounters.PeakPagefileUsage gProcess_PrivatePageCount(ProcessCount) = @pSysProcInfo.VMCounters.PrivatePageCount '-- I/O data: gProcess_ReadOperationCount(ProcessCount) = @pSysProcInfo.IOCounters.ReadOperationCount gProcess_WriteOperationCount(ProcessCount) = @pSysProcInfo.IOCounters.WriteOperationCount gProcess_OtherOperationCount(ProcessCount) = @pSysProcInfo.IOCounters.OtherOperationCount gProcess_ReadTransferCount(ProcessCount) = @pSysProcInfo.IOCounters.ReadTransferCount gProcess_WriteTransferCount(ProcessCount) = @pSysProcInfo.IOCounters.WriteTransferCount gProcess_OtherTransferCount(ProcessCount) = @pSysProcInfo.IOCounters.OtherTransferCount '-- calculate how long the process has been running: IF @pSysProcInfo.CreateTime > 0 THEN 'does this process have a CreateTime? (nearly all of them should) gProcess_RunningTime(ProcessCount) = CurrentTime - @pSysProcInfo.CreateTime ELSE 'System Idle Process and System will not have a valid CreateTime gProcess_RunningTime(ProcessCount) = 0 'instead of 0, it could potentially be set to the value of CurrentTime - SystemUpTime (would need to retrieve the uptime though) END IF '-- if the process ID is not 0 then use WStrToAnsi (or ACODE$ and PEEK$) to convert the provided name from Unicode to ANSI.. IF @pSysProcInfo.ProcessID > 0 THEN gProcess_Name(ProcessCount) = ACODE$(PEEK$(@pSysProcInfo.ProcessName.pBuffer, @pSysProcInfo.ProcessName.Length)) ELSE 'the idle process ID is 0, and won't have a name, so give it one.. gProcess_Name(ProcessCount) = "System Idle Process" END IF '-- search for a previous array index for this process: ' it will usually be the same index as ProcessCount, but when processes are opened/closed sometimes the data will get auto of sync between refreshes ' using the PREVIOUS_ arrays guarantees that all the data matches up correctly for each process. otherwise, we'd sometimes end up with weird numbers, such as negative CPU times, etc.. ARRAY SCAN PREVIOUS_ID(), = @pSysProcInfo.ProcessID, TO previndex 'this returns an index relative to the LBOUND of the array IF previndex > 0 AND PREVIOUS_CreateTime(previndex - 1) = @pSysProcInfo.CreateTime THEN 'was the ProcessID found, and does the creation time match across both refreshes? (PIDs get recycled) DECR previndex 'subtract 1 from the previndex because the arrays are 0 based, and ARRAY SCAN returned a 1 based result '-- calculate the deltas for each array that may have one (ProcessID, ProcessName, etc will never have a delta): gProcess_NumberOfThreads_Delta(ProcessCount) = @pSysProcInfo.NumberOfThreads - PREVIOUS_NumberOfThreads(previndex) gProcess_UserTime_Delta(ProcessCount) = @pSysProcInfo.UserTime - PREVIOUS_UserTime(previndex) gProcess_KernelTime_Delta(ProcessCount) = @pSysProcInfo.KernelTime - PREVIOUS_KernelTime(previndex) gProcess_TotalCPUTime_Delta(ProcessCount) = @pSysProcInfo.UserTime + @pSysProcInfo.KernelTime - PREVIOUS_TotalCPUTime(previndex) gProcess_BasePriority_Delta(ProcessCount) = @pSysProcInfo.BasePriority - PREVIOUS_BasePriority(previndex) gProcess_HandleCount_Delta(ProcessCount) = @pSysProcInfo.HandleCount - PREVIOUS_HandleCount(previndex) '-- memory data: gProcess_PeakVirtualSize_Delta(ProcessCount) = @pSysProcInfo.VMCounters.PeakVirtualSize - PREVIOUS_PeakVirtualSize(previndex) gProcess_VirtualSize_Delta(ProcessCount) = @pSysProcInfo.VMCounters.VirtualSize - PREVIOUS_VirtualSize(previndex) gProcess_PageFaultCount_Delta(ProcessCount) = @pSysProcInfo.VMCounters.PageFaultCount - PREVIOUS_PageFaultCount(previndex) gProcess_PeakWorkingSetSize_Delta(ProcessCount) = @pSysProcInfo.VMCounters.PeakWorkingSetSize - PREVIOUS_PeakWorkingSetSize(previndex) gProcess_WorkingSetSize_Delta(ProcessCount) = @pSysProcInfo.VMCounters.WorkingSetSize - PREVIOUS_WorkingSetSize(previndex) gProcess_QuotaPeakPagedPoolUsage_Delta(ProcessCount) = @pSysProcInfo.VMCounters.QuotaPeakPagedPoolUsage - PREVIOUS_QuotaPeakPagedPoolUsage(previndex) gProcess_QuotaPagedPoolUsage_Delta(ProcessCount) = @pSysProcInfo.VMCounters.QuotaPagedPoolUsage - PREVIOUS_QuotaPagedPoolUsage(previndex) gProcess_QuotaPeakNonPagedPoolUsage_Delta(ProcessCount) = @pSysProcInfo.VMCounters.QuotaPeakNonPagedPoolUsage - PREVIOUS_QuotaPeakNonPagedPoolUsage(previndex) gProcess_QuotaNonPagedPoolUsage_Delta(ProcessCount) = @pSysProcInfo.VMCounters.QuotaNonPagedPoolUsage - PREVIOUS_QuotaNonPagedPoolUsage(previndex) gProcess_PagefileUsage_Delta(ProcessCount) = @pSysProcInfo.VMCounters.PagefileUsage - PREVIOUS_PagefileUsage(previndex) gProcess_PeakPagefileUsage_Delta(ProcessCount) = @pSysProcInfo.VMCounters.PeakPagefileUsage - PREVIOUS_PeakPagefileUsage(previndex) gProcess_PrivatePageCount_Delta(ProcessCount) = @pSysProcInfo.VMCounters.PrivatePageCount - PREVIOUS_PrivatePageCount(previndex) '-- I/O data: gProcess_ReadOperationCount_Delta(ProcessCount) = @pSysProcInfo.IOCounters.ReadOperationCount - PREVIOUS_ReadOperationCount(previndex) gProcess_WriteOperationCount_Delta(ProcessCount) = @pSysProcInfo.IOCounters.WriteOperationCount - PREVIOUS_WriteOperationCount(previndex) gProcess_OtherOperationCount_Delta(ProcessCount) = @pSysProcInfo.IOCounters.OtherOperationCount - PREVIOUS_OtherOperationCount(previndex) gProcess_ReadTransferCount_Delta(ProcessCount) = @pSysProcInfo.IOCounters.ReadTransferCount - PREVIOUS_ReadTransferCount(previndex) gProcess_WriteTransferCount_Delta(ProcessCount) = @pSysProcInfo.IOCounters.WriteTransferCount - PREVIOUS_WriteTransferCount(previndex) gProcess_OtherTransferCount_Delta(ProcessCount) = @pSysProcInfo.IOCounters.OtherTransferCount - PREVIOUS_OtherTransferCount(previndex) ELSE 'if the ProcessID was NOT found, then the process may have just started running and there is no previous data for it yet gProcess_NumberOfThreads_Delta(ProcessCount) = @pSysProcInfo.NumberOfThreads gProcess_UserTime_Delta(ProcessCount) = @pSysProcInfo.UserTime gProcess_KernelTime_Delta(ProcessCount) = @pSysProcInfo.KernelTime gProcess_TotalCPUTime_Delta(ProcessCount) = @pSysProcInfo.UserTime + @pSysProcInfo.KernelTime gProcess_BasePriority_Delta(ProcessCount) = @pSysProcInfo.BasePriority gProcess_HandleCount_Delta(ProcessCount) = @pSysProcInfo.HandleCount '-- memory data: gProcess_PeakVirtualSize_Delta(ProcessCount) = @pSysProcInfo.VMCounters.PeakVirtualSize gProcess_VirtualSize_Delta(ProcessCount) = @pSysProcInfo.VMCounters.VirtualSize gProcess_PageFaultCount_Delta(ProcessCount) = @pSysProcInfo.VMCounters.PageFaultCount gProcess_PeakWorkingSetSize_Delta(ProcessCount) = @pSysProcInfo.VMCounters.PeakWorkingSetSize gProcess_WorkingSetSize_Delta(ProcessCount) = @pSysProcInfo.VMCounters.WorkingSetSize gProcess_QuotaPeakPagedPoolUsage_Delta(ProcessCount) = @pSysProcInfo.VMCounters.QuotaPeakPagedPoolUsage gProcess_QuotaPagedPoolUsage_Delta(ProcessCount) = @pSysProcInfo.VMCounters.QuotaPagedPoolUsage gProcess_QuotaPeakNonPagedPoolUsage_Delta(ProcessCount) = @pSysProcInfo.VMCounters.QuotaPeakNonPagedPoolUsage gProcess_QuotaNonPagedPoolUsage_Delta(ProcessCount) = @pSysProcInfo.VMCounters.QuotaNonPagedPoolUsage gProcess_PagefileUsage_Delta(ProcessCount) = @pSysProcInfo.VMCounters.PagefileUsage gProcess_PeakPagefileUsage_Delta(ProcessCount) = @pSysProcInfo.VMCounters.PeakPagefileUsage gProcess_PrivatePageCount_Delta(ProcessCount) = @pSysProcInfo.VMCounters.PrivatePageCount '-- I/O data: gProcess_ReadOperationCount_Delta(ProcessCount) = @pSysProcInfo.IOCounters.ReadOperationCount gProcess_WriteOperationCount_Delta(ProcessCount) = @pSysProcInfo.IOCounters.WriteOperationCount gProcess_OtherOperationCount_Delta(ProcessCount) = @pSysProcInfo.IOCounters.OtherOperationCount gProcess_ReadTransferCount_Delta(ProcessCount) = @pSysProcInfo.IOCounters.ReadTransferCount gProcess_WriteTransferCount_Delta(ProcessCount) = @pSysProcInfo.IOCounters.WriteTransferCount gProcess_OtherTransferCount_Delta(ProcessCount) = @pSysProcInfo.IOCounters.OtherTransferCount END IF '-- keep a running total (for this refresh cycle) of the CPU time deltas for all processes: ' this value is used to calculate CPU percentages. TotalCPUTimeIncreaseOfAllProcesses = TotalCPUTimeIncreaseOfAllProcesses + gProcess_TotalCPUTime_Delta(ProcessCount) '-- exit when there's no more process entries: IF @pSysProcInfo.NextEntryOffset = 0 THEN EXIT LOOP ELSE 'otherwise, increment ProcessCount, and advance to the data struct of the next process: INCR ProcessCount pSysProcInfo = pSysProcInfo + @pSysProcInfo.NextEntryOffset END IF LOOP 'loop through all the process data returned by NtQuerySystemInformation '-- now that all the deltas have been calculated in the previous loop, we can make backup copies of the important data ' this is so each process's data can be identified during the next refresh, and delta values can be calculated ' and here, we can now use TotalCPUTimeIncreaseOfAllProcesses to calculate CPU percentages FOR x = 0 TO ProcessCount 'NOTE: the PID and CreateTime won't have deltas, but they are required to search for processes PREVIOUS_ID(x) = gProcess_ID(x) PREVIOUS_NumberOfThreads(x) = gProcess_NumberOfThreads(x) PREVIOUS_CreateTime(x) = gProcess_CreateTime(x) PREVIOUS_UserTime(x) = gProcess_UserTime(x) PREVIOUS_KernelTime(x) = gProcess_KernelTime(x) PREVIOUS_TotalCPUTime(x) = gProcess_TotalCPUTime(x) PREVIOUS_BasePriority(x) = gProcess_BasePriority(x) PREVIOUS_HandleCount(x) = gProcess_HandleCount(x) '-- memory data: PREVIOUS_PeakVirtualSize(x) = gProcess_PeakVirtualSize(x) PREVIOUS_VirtualSize(x) = gProcess_VirtualSize(x) PREVIOUS_PageFaultCount(x) = gProcess_PageFaultCount(x) PREVIOUS_PeakWorkingSetSize(x) = gProcess_PeakWorkingSetSize(x) PREVIOUS_WorkingSetSize(x) = gProcess_WorkingSetSize(x) PREVIOUS_QuotaPeakPagedPoolUsage(x) = gProcess_QuotaPeakPagedPoolUsage(x) PREVIOUS_QuotaPagedPoolUsage(x) = gProcess_QuotaPagedPoolUsage(x) PREVIOUS_QuotaPeakNonPagedPoolUsage(x) = gProcess_QuotaPeakNonPagedPoolUsage(x) PREVIOUS_QuotaNonPagedPoolUsage(x) = gProcess_QuotaNonPagedPoolUsage(x) PREVIOUS_PagefileUsage(x) = gProcess_PagefileUsage(x) PREVIOUS_PeakPagefileUsage(x) = gProcess_PeakPagefileUsage(x) PREVIOUS_PrivatePageCount(x) = gProcess_PrivatePageCount(x) '-- I/O data: PREVIOUS_ReadOperationCount(x) = gProcess_ReadOperationCount(x) PREVIOUS_WriteOperationCount(x) = gProcess_WriteOperationCount(x) PREVIOUS_OtherOperationCount(x) = gProcess_OtherOperationCount(x) PREVIOUS_ReadTransferCount(x) = gProcess_ReadTransferCount(x) PREVIOUS_WriteTransferCount(x) = gProcess_WriteTransferCount(x) PREVIOUS_OtherTransferCount(x) = gProcess_OtherTransferCount(x) '-- calculate the current CPU usage for each process: ' CPU % = (the CPU time increase of this process since the last refresh) divided by (the total CPU time increase from all processes since the last refresh) multipied by 100 CPUpercentage = gProcess_TotalCPUTime_Delta(x) / TotalCPUTimeIncreaseOfAllProcesses * 100 ' gProcess_CPUPercent_Delta(x) = the change of CPU % for this process. could be positive or negative. gProcess_CPUPercent_Delta(x) = CPUpercentage - gProcess_CPUPercent(x) gProcess_CPUPercent(x) = CPUpercentage '-- if this is the System Idle Process (PID=0) then use it to find the system's current CPU usage: IF gProcess_ID(x) = 0 THEN CurrentCPUPercent = 100 - gProcess_CPUPercent(x) NEXT '-- sort one of the arrays (gProcess_Index will be used later to sort the other arrays, keeping the data lined up correctly): ' remember that the sorted array should NOT use gProcess_Index! it should use the regular index instead, because it's already sorted. ' actually, that's not quite true since gSortedIndex is being used now.. IF static_ColumnToSortBy = %ColumnNumber_Name THEN IF gColumnSortDirection(static_ColumnToSortBy) = 0 THEN ARRAY SORT gProcess_Name() FOR ProcessCount + 1, COLLATE UCASE, TAGARRAY gProcess_Index(), ASCEND IF gColumnSortDirection(static_ColumnToSortBy) = -1 THEN ARRAY SORT gProcess_Name() FOR ProcessCount + 1, COLLATE UCASE, TAGARRAY gProcess_Index(), DESCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_ID THEN IF gColumnSortDirection(static_ColumnToSortBy) = 0 THEN ARRAY SORT gProcess_ID() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), ASCEND IF gColumnSortDirection(static_ColumnToSortBy) = -1 THEN ARRAY SORT gProcess_ID() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), DESCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_ParentProcessID THEN IF gColumnSortDirection(static_ColumnToSortBy) = 0 THEN ARRAY SORT gProcess_ParentProcessID() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), ASCEND IF gColumnSortDirection(static_ColumnToSortBy) = -1 THEN ARRAY SORT gProcess_ParentProcessID() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), DESCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_CreateTime THEN IF gColumnSortDirection(static_ColumnToSortBy) = 0 THEN ARRAY SORT gProcess_CreateTime() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), ASCEND IF gColumnSortDirection(static_ColumnToSortBy) = -1 THEN ARRAY SORT gProcess_CreateTime() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), DESCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_RunningTime THEN IF gColumnSortDirection(static_ColumnToSortBy) = 0 THEN ARRAY SORT gProcess_RunningTime() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), ASCEND IF gColumnSortDirection(static_ColumnToSortBy) = -1 THEN ARRAY SORT gProcess_RunningTime() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), DESCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_UserTime THEN IF gColumnSortDirection(static_ColumnToSortBy) = 0 THEN ARRAY SORT gProcess_UserTime() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), ASCEND IF gColumnSortDirection(static_ColumnToSortBy) = -1 THEN ARRAY SORT gProcess_UserTime() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), DESCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_KernelTime THEN IF gColumnSortDirection(static_ColumnToSortBy) = 0 THEN ARRAY SORT gProcess_KernelTime() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), ASCEND IF gColumnSortDirection(static_ColumnToSortBy) = -1 THEN ARRAY SORT gProcess_KernelTime() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), DESCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_TotalCPUTime THEN IF gColumnSortDirection(static_ColumnToSortBy) = 0 THEN ARRAY SORT gProcess_TotalCPUTime() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), ASCEND IF gColumnSortDirection(static_ColumnToSortBy) = -1 THEN ARRAY SORT gProcess_TotalCPUTime() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), DESCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_CPUPercent THEN IF gColumnSortDirection(static_ColumnToSortBy) = 0 THEN ARRAY SORT gProcess_CPUPercent() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), ASCEND IF gColumnSortDirection(static_ColumnToSortBy) = -1 THEN ARRAY SORT gProcess_CPUPercent() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), DESCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_BasePriority THEN IF gColumnSortDirection(static_ColumnToSortBy) = 0 THEN ARRAY SORT gProcess_BasePriority() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), ASCEND IF gColumnSortDirection(static_ColumnToSortBy) = -1 THEN ARRAY SORT gProcess_BasePriority() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), DESCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_HandleCount THEN IF gColumnSortDirection(static_ColumnToSortBy) = 0 THEN ARRAY SORT gProcess_HandleCount() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), ASCEND IF gColumnSortDirection(static_ColumnToSortBy) = -1 THEN ARRAY SORT gProcess_HandleCount() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), DESCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_NumberOfThreads THEN IF gColumnSortDirection(static_ColumnToSortBy) = 0 THEN ARRAY SORT gProcess_NumberOfThreads() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), ASCEND IF gColumnSortDirection(static_ColumnToSortBy) = -1 THEN ARRAY SORT gProcess_NumberOfThreads() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), DESCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_PeakVirtualSize THEN IF gColumnSortDirection(static_ColumnToSortBy) = 0 THEN ARRAY SORT gProcess_PeakVirtualSize() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), ASCEND IF gColumnSortDirection(static_ColumnToSortBy) = -1 THEN ARRAY SORT gProcess_PeakVirtualSize() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), DESCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_VirtualSize THEN IF gColumnSortDirection(static_ColumnToSortBy) = 0 THEN ARRAY SORT gProcess_VirtualSize() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), ASCEND IF gColumnSortDirection(static_ColumnToSortBy) = -1 THEN ARRAY SORT gProcess_VirtualSize() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), DESCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_PageFaultCount THEN IF gColumnSortDirection(static_ColumnToSortBy) = 0 THEN ARRAY SORT gProcess_PageFaultCount() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), ASCEND IF gColumnSortDirection(static_ColumnToSortBy) = -1 THEN ARRAY SORT gProcess_PageFaultCount() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), DESCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_PeakWorkingSetSize THEN IF gColumnSortDirection(static_ColumnToSortBy) = 0 THEN ARRAY SORT gProcess_PeakWorkingSetSize() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), ASCEND IF gColumnSortDirection(static_ColumnToSortBy) = -1 THEN ARRAY SORT gProcess_PeakWorkingSetSize() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), DESCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_WorkingSetSize THEN IF gColumnSortDirection(static_ColumnToSortBy) = 0 THEN ARRAY SORT gProcess_WorkingSetSize() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), ASCEND IF gColumnSortDirection(static_ColumnToSortBy) = -1 THEN ARRAY SORT gProcess_WorkingSetSize() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), DESCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_PrivatePageCount THEN IF gColumnSortDirection(static_ColumnToSortBy) = 0 THEN ARRAY SORT gProcess_PrivatePageCount() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), ASCEND IF gColumnSortDirection(static_ColumnToSortBy) = -1 THEN ARRAY SORT gProcess_PrivatePageCount() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), DESCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_ReadOperationCount THEN IF gColumnSortDirection(static_ColumnToSortBy) = 0 THEN ARRAY SORT gProcess_ReadOperationCount() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), ASCEND IF gColumnSortDirection(static_ColumnToSortBy) = -1 THEN ARRAY SORT gProcess_ReadOperationCount() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), DESCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_WriteOperationCount THEN IF gColumnSortDirection(static_ColumnToSortBy) = 0 THEN ARRAY SORT gProcess_WriteOperationCount() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), ASCEND IF gColumnSortDirection(static_ColumnToSortBy) = -1 THEN ARRAY SORT gProcess_WriteOperationCount() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), DESCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_OtherOperationCount THEN IF gColumnSortDirection(static_ColumnToSortBy) = 0 THEN ARRAY SORT gProcess_OtherOperationCount() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), ASCEND IF gColumnSortDirection(static_ColumnToSortBy) = -1 THEN ARRAY SORT gProcess_OtherOperationCount() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), DESCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_ReadTransferCount THEN IF gColumnSortDirection(static_ColumnToSortBy) = 0 THEN ARRAY SORT gProcess_ReadTransferCount() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), ASCEND IF gColumnSortDirection(static_ColumnToSortBy) = -1 THEN ARRAY SORT gProcess_ReadTransferCount() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), DESCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_WriteTransferCount THEN IF gColumnSortDirection(static_ColumnToSortBy) = 0 THEN ARRAY SORT gProcess_WriteTransferCount() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), ASCEND IF gColumnSortDirection(static_ColumnToSortBy) = -1 THEN ARRAY SORT gProcess_WriteTransferCount() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), DESCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_OtherTransferCount THEN IF gColumnSortDirection(static_ColumnToSortBy) = 0 THEN ARRAY SORT gProcess_OtherTransferCount() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), ASCEND IF gColumnSortDirection(static_ColumnToSortBy) = -1 THEN ARRAY SORT gProcess_OtherTransferCount() FOR ProcessCount + 1, TAGARRAY gProcess_Index(), DESCEND END IF '-- make a copy of the sorted index, so the array that was sorted can be UNsorted (makes things easier when filling the listview): RESET gSortedIndex() 'maybe not necessary to reset, but do it anyway FOR x = 0 TO ProcessCount gSortedIndex(x) = gProcess_Index(x) NEXT '-- UNsort the array that was sorted: IF static_ColumnToSortBy = %ColumnNumber_Name THEN ARRAY SORT gProcess_Index() FOR ProcessCount + 1, TAGARRAY gProcess_Name(), ASCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_ID THEN ARRAY SORT gProcess_Index() FOR ProcessCount + 1, TAGARRAY gProcess_ID(), ASCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_ParentProcessID THEN ARRAY SORT gProcess_Index() FOR ProcessCount + 1, TAGARRAY gProcess_ParentProcessID(), ASCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_CreateTime THEN ARRAY SORT gProcess_Index() FOR ProcessCount + 1, TAGARRAY gProcess_CreateTime(), ASCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_RunningTime THEN ARRAY SORT gProcess_Index() FOR ProcessCount + 1, TAGARRAY gProcess_RunningTime(), ASCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_UserTime THEN ARRAY SORT gProcess_Index() FOR ProcessCount + 1, TAGARRAY gProcess_UserTime(), ASCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_KernelTime THEN ARRAY SORT gProcess_Index() FOR ProcessCount + 1, TAGARRAY gProcess_KernelTime(), ASCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_TotalCPUTime THEN ARRAY SORT gProcess_Index() FOR ProcessCount + 1, TAGARRAY gProcess_TotalCPUTime(), ASCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_CPUPercent THEN ARRAY SORT gProcess_Index() FOR ProcessCount + 1, TAGARRAY gProcess_CPUPercent(), ASCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_BasePriority THEN ARRAY SORT gProcess_Index() FOR ProcessCount + 1, TAGARRAY gProcess_BasePriority(), ASCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_HandleCount THEN ARRAY SORT gProcess_Index() FOR ProcessCount + 1, TAGARRAY gProcess_HandleCount(), ASCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_NumberOfThreads THEN ARRAY SORT gProcess_Index() FOR ProcessCount + 1, TAGARRAY gProcess_NumberOfThreads(), ASCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_PeakVirtualSize THEN ARRAY SORT gProcess_Index() FOR ProcessCount + 1, TAGARRAY gProcess_PeakVirtualSize(), ASCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_VirtualSize THEN ARRAY SORT gProcess_Index() FOR ProcessCount + 1, TAGARRAY gProcess_VirtualSize(), ASCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_PageFaultCount THEN ARRAY SORT gProcess_Index() FOR ProcessCount + 1, TAGARRAY gProcess_PageFaultCount(), ASCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_PeakWorkingSetSize THEN ARRAY SORT gProcess_Index() FOR ProcessCount + 1, TAGARRAY gProcess_PeakWorkingSetSize(), ASCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_WorkingSetSize THEN ARRAY SORT gProcess_Index() FOR ProcessCount + 1, TAGARRAY gProcess_WorkingSetSize(), ASCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_PrivatePageCount THEN ARRAY SORT gProcess_Index() FOR ProcessCount + 1, TAGARRAY gProcess_PrivatePageCount(), ASCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_ReadOperationCount THEN ARRAY SORT gProcess_Index() FOR ProcessCount + 1, TAGARRAY gProcess_ReadOperationCount(), ASCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_WriteOperationCount THEN ARRAY SORT gProcess_Index() FOR ProcessCount + 1, TAGARRAY gProcess_WriteOperationCount(), ASCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_OtherOperationCount THEN ARRAY SORT gProcess_Index() FOR ProcessCount + 1, TAGARRAY gProcess_OtherOperationCount(), ASCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_ReadTransferCount THEN ARRAY SORT gProcess_Index() FOR ProcessCount + 1, TAGARRAY gProcess_ReadTransferCount(), ASCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_WriteTransferCount THEN ARRAY SORT gProcess_Index() FOR ProcessCount + 1, TAGARRAY gProcess_WriteTransferCount(), ASCEND ELSEIF static_ColumnToSortBy = %ColumnNumber_OtherTransferCount THEN ARRAY SORT gProcess_Index() FOR ProcessCount + 1, TAGARRAY gProcess_OtherTransferCount(), ASCEND END IF '-- no need to waste time updating the window if it's not visible: 'if IsWindowVisible(CBHNDL) = %FALSE then exit function '-- add one to the ProcessCount to go from 0 to 1 based: DIALOG SET TEXT CBHNDL, $Program & " - " & FORMAT$(ProcessCount + 1) & " processes - CPU " & FORMAT$(ROUND(CurrentCPUPercent, 2)) & "%" '& _ '-- temporarily prevent the listview from redrawing itself (reduces flicker while deleting/adding items): SendMessage(hListview, %WM_SETREDRAW, %FALSE, 0) '-- remember the current position of the scrollbars: LOCAL LvSbPosX AS LONG LOCAL LvSbPosY AS LONG LOCAL LvRcSize AS RECT SendMessage(hListView, %LVM_GETITEMRECT, BYVAL %NULL, BYVAL VARPTR(LvRcSize)) LvSbPosX = GetScrollPos(hListView, %SB_HORZ) 'Get horizontal scrollbar position LvSbPosY = GetScrollPos(hListView, %SB_VERT) * (LvRcSize.nBottom - LvRcSize.nTop) 'Get vertical scrollbar position '-- remember how many rows there are before using %LVM_DELETEALLITEMS: LOCAL PreviousNumberOfRowsInListview AS LONG PreviousNumberOfRowsInListview = SendMessage(hListview, %LVM_GETITEMCOUNT, 0, 0) DECR PreviousNumberOfRowsInListview 'subtract one to convert from 1 based to 0 based (ProcessCount is also 0 based) '-- clear the listview (necessary to remove processes from the end rows, when the process count decreases): SendMessage(hListview, %LVM_DELETEALLITEMS, 0, 0) '-- set the item count (the number of items that the list-view control will ultimately contain): ' this message is NOT required but it can potentially make adding a lot of items go faster. I don't see any difference. SendMessage(hListview, %LVM_SETITEMCOUNT, ProcessCount + 1, %LVSICF_NOINVALIDATEALL OR %LVSICF_NOSCROLL) '%LVSICF_NOSCROLL doesn't seem to help '-- fill the listview: FOR row = 0 TO ProcessCount LVI.iItem = row '0 based LVI.pszText = VARPTR(szBuf) LVI.cchTextMax = SIZEOF(szBuf) FOR col = 0 TO %ListviewColumnCount - 1 IF col = %ColumnNumber_Name THEN 'first column means that a new row/item must be inserted szBuf = gProcess_Name(gSortedIndex(row)) ELSEIF col = %ColumnNumber_ID THEN szBuf = FORMAT$(gProcess_ID(gSortedIndex(row))) ELSEIF col = %ColumnNumber_ParentProcessID THEN szBuf = FORMAT$(gProcess_ParentProcessID(gSortedIndex(row))) ELSEIF col = %ColumnNumber_CreateTime THEN '-- turn the QUAD createtime into FILETIME so it can be converted into a standard date/time format: IF gProcess_CreateTime(gSortedIndex(row)) > 0 THEN FT.dwLowDateTime = LO(DWORD, gProcess_CreateTime(gSortedIndex(row))) FT.dwHighDateTime = HI(DWORD, gProcess_CreateTime(gSortedIndex(row))) IF FileTimeToLocalFileTime(FT, LT) <> 0 THEN 'success? IF FileTimeToSystemTime(LT, ST) <> 0 THEN 'success? szBuf = FORMAT$(ST.wMonth) & "/" & FORMAT$(ST.wDay) & "/" & FORMAT$(ST.wYear) & " @ " & FORMAT$(ST.wHour, "00") & ":" & FORMAT$(ST.wMinute, "00") & ":" & FORMAT$(ST.wSecond, "00") END IF END IF ELSE 'System Idle Process and System will not have a valid CreateTime szBuf = "N/A" END IF 'szBuf = FORMAT$(gProcess_CreateTime(gSortedIndex(row))) ELSEIF col = %ColumnNumber_RunningTime THEN IF gProcess_RunningTime(gSortedIndex(row)) > 0 THEN szBuf = TRIM$(ConvertSecondsIntoFormattedString(gProcess_RunningTime(gSortedIndex(row)) / 10000000)) 'there are 10 million 100ns intervals in a second ELSE 'System Idle Process and System will always be 0 szBuf = "N/A" END IF ELSEIF col = %ColumnNumber_UserTime THEN szBuf = TRIM$(ConvertSecondsIntoFormattedString(gProcess_UserTime(gSortedIndex(row)) / 10000000)) 'there are 10 million 100ns intervals in a second ELSEIF col = %ColumnNumber_KernelTime THEN szBuf = TRIM$(ConvertSecondsIntoFormattedString(gProcess_KernelTime(gSortedIndex(row)) / 10000000)) 'there are 10 million 100ns intervals in a second ELSEIF col = %ColumnNumber_TotalCPUTime THEN szBuf = TRIM$(ConvertSecondsIntoFormattedString(gProcess_TotalCPUTime(gSortedIndex(row)) / 10000000)) 'there are 10 million 100ns intervals in a second ELSEIF col = %ColumnNumber_CPUPercent THEN szBuf = FORMAT$(ROUND(gProcess_CPUPercent(gSortedIndex(row)), 2), "* 0.00") & "%" ELSEIF col = %ColumnNumber_BasePriority THEN IF gProcess_BasePriority(gSortedIndex(row)) > 0 THEN szBuf = FORMAT$(gProcess_BasePriority(gSortedIndex(row)), "* #,") ELSE 'System Idle Process will always have a priority of 0 szBuf = "N/A" END IF ELSEIF col = %ColumnNumber_HandleCount THEN szBuf = FORMAT$(gProcess_HandleCount(gSortedIndex(row)), "* #,") ELSEIF col = %ColumnNumber_NumberOfThreads THEN szBuf = FORMAT$(gProcess_NumberOfThreads(gSortedIndex(row)), "* #,") ELSEIF col = %ColumnNumber_PeakVirtualSize THEN szBuf = FORMAT$(gProcess_PeakVirtualSize(gSortedIndex(row)) / 1024, "* #,") & " K" ELSEIF col = %ColumnNumber_VirtualSize THEN szBuf = FORMAT$(gProcess_VirtualSize(gSortedIndex(row)) / 1024, "* #,") & " K" ELSEIF col = %ColumnNumber_PageFaultCount THEN szBuf = FORMAT$(gProcess_PageFaultCount(gSortedIndex(row)), "* #,") ELSEIF col = %ColumnNumber_PeakWorkingSetSize THEN szBuf = FORMAT$(gProcess_PeakWorkingSetSize(gSortedIndex(row)) / 1024, "* #,") & " K" ELSEIF col = %ColumnNumber_WorkingSetSize THEN szBuf = FORMAT$(gProcess_WorkingSetSize(gSortedIndex(row)) / 1024, "* #,") & " K" ELSEIF col = %ColumnNumber_PrivatePageCount THEN szBuf = FORMAT$(gProcess_PrivatePageCount(gSortedIndex(row)), "* #,") ELSEIF col = %ColumnNumber_ReadOperationCount THEN szBuf = FORMAT$(gProcess_ReadOperationCount(gSortedIndex(row)), "* #,") ELSEIF col = %ColumnNumber_WriteOperationCount THEN szBuf = FORMAT$(gProcess_WriteOperationCount(gSortedIndex(row)), "* #,") ELSEIF col = %ColumnNumber_OtherOperationCount THEN szBuf = FORMAT$(gProcess_OtherOperationCount(gSortedIndex(row)), "* #,") ELSEIF col = %ColumnNumber_ReadTransferCount THEN szBuf = FORMAT$(gProcess_ReadTransferCount(gSortedIndex(row)), "* #,") ELSEIF col = %ColumnNumber_WriteTransferCount THEN szBuf = FORMAT$(gProcess_WriteTransferCount(gSortedIndex(row)), "* #,") ELSEIF col = %ColumnNumber_OtherTransferCount THEN szBuf = FORMAT$(gProcess_OtherTransferCount(gSortedIndex(row)), "* #,") END IF IF col = 0 THEN 'first column means that a new row/item must be inserted LVI.mask = %LVIF_TEXT OR %LVIF_PARAM 'OR %LVIF_STATE LVI.iSubItem = 0 'this must be zero when inserting a new item LVI.lParam = gProcess_ID(gSortedIndex(row)) 'store the PID so the process can be identified when selecting a row IF SendMessage(hListview, %LVM_INSERTITEM, 0, VARPTR(LVI)) = -1 THEN MSGBOX "LVM_INSERTITEM failed!", %MB_ICONERROR OR %MB_SYSTEMMODAL, $Program ELSE LVI.mask = %LVIF_TEXT LVI.iSubItem = col '0 based IF SendMessage(hListview, %LVM_SETITEM, 0, VARPTR(LVI)) = %FALSE THEN MSGBOX "LVM_SETITEM failed!", %MB_ICONERROR OR %MB_SYSTEMMODAL, $Program END IF 'this is a failed attempt at trying to not use %LVM_DELETEALLITEMS, and instead reuse existing rows: ' %LVM_DELETEALLITEMS is the reason why the scrollbars reset each time. not deleting all items fixes that issue ' but it ulimately ends up causing flicker.. don't know why. ' IF col = 0 THEN 'first column means that a new row/item must be inserted ' IF row <= PreviousNumberOfRowsInListview THEN 'are there enough rows available in the listview to add this item? ' LVI.mask = %LVIF_TEXT ' LVI.iSubItem = col '0 based ' IF SendMessage(hListview, %LVM_SETITEM, 0, VARPTR(LVI)) = %FALSE THEN MSGBOX "LVM_SETITEM failed!", %MB_ICONERROR OR %MB_SYSTEMMODAL, $Program ' else 'not enough rows. need to add another one ' INCR PreviousNumberOfRowsInListview ' LVI.mask = %LVIF_TEXT OR %LVIF_PARAM OR %LVIF_STATE ' LVI.iSubItem = 0 'this must be zero when inserting a new item ' IF SendMessage(hListview, %LVM_INSERTITEM, 0, VARPTR(LVI)) = -1 THEN MSGBOX "LVM_INSERTITEM failed!", %MB_ICONERROR OR %MB_SYSTEMMODAL, $Program ' END IF ' ELSE ' LVI.mask = %LVIF_TEXT ' LVI.iSubItem = col '0 based ' IF SendMessage(hListview, %LVM_SETITEM, 0, VARPTR(LVI)) = %FALSE THEN MSGBOX "LVM_SETITEM failed!", %MB_ICONERROR OR %MB_SYSTEMMODAL, $Program ' END IF NEXT 'col NEXT 'row '-- allow the listview to redraw, now that the items are done being updated: SendMessage(hListview, %WM_SETREDRAW, %TRUE, 0) '-- set horizontal and vertical scrollbar position (%WM_SETREDRAW must be %TRUE before this point, or it may not scroll correctly): ' unfortunately, this will cause flicker, but it's necessary if we want to retain the original scroll position. SendMessage(hListView, %LVM_SCROLL, LvSbPosX, LvSbPosY) '-- tell Windows to NOT do any erasing/drawing for the client area of the window behind the listview: ' this is required in order to prevent the listbox from flickering. ' for this program, the listview covers the entire client area, so we can just pass an empty RECT; other programs may need to get the RECT occupied by the listview ValidateRect(CBHNDL, BYVAL 0) '-- tell Windows that the listview needs to be redrawn: ' this MUST come AFTER the window's client area behind the listview has been validated! ValidateRect(CBHNDL, BYVAL 0) ' we need to determine if the listview's background should be erased.. '-- are there extra rows at the bottom? (has the number of running processes decreased since the last refresh?) IF PreviousNumberOfRowsInListview > ProcessCount THEN LOCAL ItemPT AS POINTL LOCAL ListviewRECT AS RECTL GetClientRect(hListview, ListviewRECT) SendMessage(hListView, %LVM_GETITEMPOSITION, ProcessCount + 1, VARPTR(ItemPT)) '-- does the row immediately after the last item appear above the bottom of the listview? IF ItemPT.y < ListviewRECT.nBottom THEN 'if yes, then the Erase flag needs to be %TRUE so the extra row(s) will be cleared (this will cause flicker, but it has to be done..) InvalidateRect(hListview, BYVAL 0, %TRUE) 'send %TRUE so the listview's background will be erased (clearing the extra bottom rows that we don't want to see anymore) ELSE 'if no, then we do not need to erase the background (no flicker! yay!) InvalidateRect(hListview, BYVAL 0, %FALSE) 'send %FALSE to reduce flickering (the listview's background will NOT be erased) END IF ELSE InvalidateRect(hListview, BYVAL 0, %FALSE) 'send %FALSE to reduce flickering (the listview's background will NOT be erased) END IF '------------------------------------------------------------------------------------------------------ CASE %WM_INITDIALOG hListview = GetDlgItem(CBHNDL, %IDC_Listview) '-- change the font for the listview. this only adjusts the row height; the font itself will not be changed (that must be done in %NM_CUSTOMDRAW): hDC = GetDC(hListview) 'get DC to the listview. Use ReleaseDC once it's no longer needed. lf.lfHeight = -MulDiv(7, GetDeviceCaps(hDC, %LOGPIXELSY), 72) lf.lfFaceName = "Tahoma" & CHR$(0) lf.lfWeight = %FW_NORMAL '%FW_BOLD hListviewFont = CreateFontIndirect(lf) SendMessage(hListview, %WM_SETFONT, hListviewFont, MAKLNG(%TRUE, 0)) 'DeleteObject(hListviewFont) 'delete the custom font (actually, don't delete it because we need to reuse it in %NM_CUSTOMDRAW) ReleaseDC(hListview, hDC) 'release the DC '-- initialize the listview (column headers/sizes): CALL InitializeListView(hListview) REDIM gColumnSortDirection(%ListviewColumnCount - 1) AS GLOBAL LONG CALL SetTimer(CBHNDL, %TIMERID, 2000, %NULL) '-- post a message to immediately refresh the listview. this avoids the startup delay (waiting for the timer): CALL PostMessage(CBHNDL, %WM_REFRESHLISTVIEW, 0, 0) '------------------------------------------------------------------------------------------------------ CASE %WM_NCACTIVATE STATIC hWndSaveFocus AS DWORD IF ISFALSE CBWPARAM THEN hWndSaveFocus = GetFocus() ELSEIF hWndSaveFocus THEN SetFocus(hWndSaveFocus) hWndSaveFocus = 0 END IF '------------------------------------------------------------------------------------------------------ CASE %WM_SIZE 'called after the dialog changes size SetWindowPos(hListview, 0, 0, 0, LO(WORD, CBLPARAM), HI(WORD, CBLPARAM), %SWP_NOMOVE OR %SWP_NOOWNERZORDER) FUNCTION = 0 : EXIT FUNCTION '------------------------------------------------------------------------------------------------------ CASE %WM_COMMAND SELECT CASE AS LONG CBCTL CASE %IDC_Listview CASE %IDCANCEL 'ESC key DIALOG END CBHNDL, 0 END SELECT '------------------------------------------------------------------------------------------------------ CASE %WM_CLOSE 'dialog is about to close KillTimer(CBHNDL, %TIMERID) END SELECT END FUNCTION '============================================================================================================ '== ShowTaskManDialog ======================================================================================= '============================================================================================================ FUNCTION ShowTaskManDialog(BYVAL hParent AS DWORD) AS LONG LOCAL lRslt AS LONG #PBFORMS BEGIN DIALOG %IDD_TaskManDialog->-> LOCAL hDlg AS DWORD DIALOG NEW hParent, "Task Manager Emulator", 259, 220, 460, 267, %WS_POPUP OR %WS_BORDER OR %WS_DLGFRAME OR %WS_THICKFRAME OR %WS_CAPTION OR %WS_SYSMENU OR %WS_MINIMIZEBOX OR %WS_MAXIMIZEBOX OR %WS_CLIPSIBLINGS OR %WS_VISIBLE OR _ %DS_MODALFRAME OR %DS_CENTER OR %DS_3DLOOK OR %DS_NOFAILCREATE OR %DS_SETFONT, %WS_EX_CONTROLPARENT OR %WS_EX_TOPMOST OR %WS_EX_LEFT OR %WS_EX_LTRREADING OR %WS_EX_RIGHTSCROLLBAR, TO hDlg CONTROL ADD "SysListView32", hDlg, %IDC_Listview, "SysListView32_1", 0, 0, 460, 267, %WS_CHILD OR %WS_VISIBLE OR %WS_TABSTOP OR %LVS_REPORT OR %LVS_SINGLESEL, %WS_EX_CLIENTEDGE OR %WS_EX_LEFT OR %WS_EX_RIGHTSCROLLBAR #PBFORMS END DIALOG DIALOG SHOW MODAL hDlg, CALL ShowTaskManDialogProc TO lRslt #PBFORMS BEGIN CLEANUP %IDD_TaskManDialog #PBFORMS END CLEANUP FUNCTION = lRslt END FUNCTION '============================================================================================================ '== INITIALIZELISTVIEW ====================================================================================== '============================================================================================================ SUB InitializeListView (BYVAL hListview AS DWORD) LOCAL col AS LONG LOCAL tLVC AS LVCOLUMN LOCAL szColumnTxt AS ASCIIZ * 100 'the text in the column header LOCAL szSampleTxt AS ASCIIZ * 1000 'the longest string of text that a column's subitems will contain (this is used to set column width) '-- modify the listview style: ListView_SetExtendedListViewStyle(hListview, ListView_GetExtendedListViewStyle(hListview) OR _ 'get the existing extended style %LVS_EX_FULLROWSELECT OR _ 'select an entire row (instead of a single cell) %LVS_EX_DOUBLEBUFFER OR _ 'this is supposed to reduce flicker (only with WinXP or greater) %LVS_EX_HEADERDRAGDROP) 'allow the user to reorder the columns via drag and drop (the listview automatically takes care of everything for us.) '%LVS_EX_GRIDLINES OR _ 'show lines inbetween all the cells (may causes noticable flickering when updating the cells) '-- initialize the columns: tLVC.mask = %LVCF_FMT OR %LVCF_SUBITEM OR %LVCF_TEXT OR %LVCF_WIDTH tLVC.pszText = VARPTR(szColumnTxt) FOR col = 0 TO %ListviewColumnCount - 1 IF col = %ColumnNumber_Name THEN szColumnTxt = "Process Name" szSampleTxt = "System Idle Process" tLVC.fmt = %LVCFMT_LEFT ELSEIF col = %ColumnNumber_ID THEN szColumnTxt = "PID" szSampleTxt = "00000" tLVC.fmt = %LVCFMT_RIGHT ELSEIF col = %ColumnNumber_ParentProcessID THEN szColumnTxt = "Parent PID" szSampleTxt = "00000" tLVC.fmt = %LVCFMT_RIGHT ELSEIF col = %ColumnNumber_CreateTime THEN szColumnTxt = "Created Date/Time" szSampleTxt = "10/00/2000 @ 00:00:00" tLVC.fmt = %LVCFMT_RIGHT ELSEIF col = %ColumnNumber_RunningTime THEN szColumnTxt = "Running Time" szSampleTxt = "00d00h00m00s000ms" tLVC.fmt = %LVCFMT_RIGHT ELSEIF col = %ColumnNumber_UserTime THEN szColumnTxt = "User CPU Time" szSampleTxt = "00h00m00s000ms" tLVC.fmt = %LVCFMT_RIGHT ELSEIF col = %ColumnNumber_KernelTime THEN szColumnTxt = "Kernel CPU Time" szSampleTxt = "00d00h00m00s000ms" tLVC.fmt = %LVCFMT_RIGHT ELSEIF col = %ColumnNumber_TotalCPUTime THEN szColumnTxt = "Total CPU Time" szSampleTxt = "00d00h00m00s000ms" tLVC.fmt = %LVCFMT_RIGHT ELSEIF col = %ColumnNumber_CPUPercent THEN szColumnTxt = "CPU %" szSampleTxt = "100.00%" tLVC.fmt = %LVCFMT_RIGHT ELSEIF col = %ColumnNumber_BasePriority THEN szColumnTxt = "Priority" 'Base Priority szSampleTxt = "N/A" tLVC.fmt = %LVCFMT_RIGHT ELSEIF col = %ColumnNumber_HandleCount THEN szColumnTxt = "Handles" szSampleTxt = "1,000" tLVC.fmt = %LVCFMT_RIGHT ELSEIF col = %ColumnNumber_NumberOfThreads THEN szColumnTxt = "Threads" szSampleTxt = "000" tLVC.fmt = %LVCFMT_RIGHT ELSEIF col = %ColumnNumber_PeakVirtualSize THEN szColumnTxt = "Peak Virtual Size" szSampleTxt = "000,000 K" tLVC.fmt = %LVCFMT_RIGHT ELSEIF col = %ColumnNumber_VirtualSize THEN szColumnTxt = "Virtual Size" szSampleTxt = "000,000 K" tLVC.fmt = %LVCFMT_RIGHT ELSEIF col = %ColumnNumber_PageFaultCount THEN szColumnTxt = "Page Faults" szSampleTxt = "00,000,000" tLVC.fmt = %LVCFMT_RIGHT ELSEIF col = %ColumnNumber_PeakWorkingSetSize THEN szColumnTxt = "Peak Mem Usage" 'Peak Working Set szSampleTxt = "000,000 K" tLVC.fmt = %LVCFMT_RIGHT ELSEIF col = %ColumnNumber_WorkingSetSize THEN szColumnTxt = "Mem Usage" 'Working Set szSampleTxt = "000,000 K" tLVC.fmt = %LVCFMT_RIGHT ELSEIF col = %ColumnNumber_PrivatePageCount THEN szColumnTxt = "Private Page Count" szSampleTxt = "00,000,000" tLVC.fmt = %LVCFMT_RIGHT ELSEIF col = %ColumnNumber_ReadOperationCount THEN szColumnTxt = "Read Ops Count" szSampleTxt = "00,000,000" tLVC.fmt = %LVCFMT_RIGHT ELSEIF col = %ColumnNumber_WriteOperationCount THEN szColumnTxt = "Write Ops Count" szSampleTxt = "00,000,000" tLVC.fmt = %LVCFMT_RIGHT ELSEIF col = %ColumnNumber_OtherOperationCount THEN szColumnTxt = "Other Ops Count" szSampleTxt = "00,000,000" tLVC.fmt = %LVCFMT_RIGHT ELSEIF col = %ColumnNumber_ReadTransferCount THEN szColumnTxt = "Read Trans Count" szSampleTxt = "00,000,000,000" tLVC.fmt = %LVCFMT_RIGHT ELSEIF col = %ColumnNumber_WriteTransferCount THEN szColumnTxt = "Write Trans Count" szSampleTxt = "00,000,000,000" tLVC.fmt = %LVCFMT_RIGHT ELSEIF col = %ColumnNumber_OtherTransferCount THEN szColumnTxt = "Other Trans Count" szSampleTxt = "00,000,000,000" tLVC.fmt = %LVCFMT_RIGHT END IF '-- force column 0 to be left-aligned: IF col = 0 THEN tLVC.fmt = %LVCFMT_LEFT 'the leftmost column in a list view control MUST be left aligned!! '-- get the required width of the column so the entire sample text can be displayed without being clipped: tLVC.cx = SendMessage(hListview, %LVM_GETSTRINGWIDTH, 0, VARPTR(szSampleTxt)) tLVC.cx = tLVC.cx + 12 'MS says that LVM_GETSTRINGWIDTH returns a number too small and requires padding to avoid text clipping (+12 seems to work ok) '-- insert the column: SendMessage(hListview, %LVM_INSERTCOLUMN, col, VARPTR(tLVC)) NEXT col END SUB
Comment