Else
' Windows NT uses PSAPI functions
Dim i As Long, iCur As Long, cRequest As Long, cGot As Long
Dim aProcesses() As Long, hProcess As Long, hModule As Long
cRequest = 96 ' Request in bytes for 24 processes
Do
ReDim aProcesses(0 To (cRequest / 4) - 1) As Long
f = EnumProcesses(aProcesses(0), cRequest, cGot)
If f = 0 Then Exit Function
If cGot < cRequest Then Exit Do
cRequest = cRequest * 2
Loop
cGot = cGot / 4 ' From bytes to processes
ReDim Preserve aProcesses(0 To cGot - 1) As Long
For i = 0 To cGot - 1
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION Or _
PROCESS_VM_READ, 0, _
aProcesses(i))
' Ignore processes that fail (probably no
' security rights)
If hProcess = 0 Then GoTo NextFor
' Get first module only
f = EnumProcessModules(hProcess, hModule, 4, c)
If f = 0 Then GoTo NextFor
sName = String$(cMaxPath, 0)
c = GetModuleFileNameEx(hProcess, hModule, sName, cMaxPath)
' Put this process in vector and count it
Set process = New CProcess
process.Create aProcesses(i), Left$(sName, c)
iCur = iCur + 1
Set vec(iCur) = process
NextFor:
Next
End If
Set CreateProcessList = vec
End Function
With the process and module in hand, it’s time to get the module name with GetModuleFileNameEx. You might remember GetModuleFileName from 16-bit Windows. It still exists in 32-bit windows, but unlike the 16-bit version, it requires a real module handle rather than an instance handle, and even then it fails for all processes other than the current one. GetModuleFileNameEx works for any process, but it requires both the process and the module handle.
FLAME The loop in CreateProcessList is a perfect illustration of why Basic programmers need the Continue statement enjoyed by C, C++, and Java programmers. Structured programming fanatics will object to my use of GoTo to skip to the next iteration when a process is discovered to be inaccessible, but two additional levels of nesting would be even more unstructured in my view.
Private Sub RefreshProcessList()
Dim processes As CVector, process As CProcess, i As Long
Set processes = CreateProcessList
SetRedraw lstProcess, False
lstProcess.Clear
For i = 1 To processes.Last
lstProcess.AddItem processes(i).EXEName
lstProcess.ItemData(lstProcess.NewIndex) = processes(i).id
Next
SetRedraw lstProcess, True
End Sub