View RSS Feed

Development Team Blog

How to Enumerate All Process Modules

Rate this Entry
This blog teaches you how to write code to get a list of all the modules in use by the current process. This information is useful when you want to know if a certain module (DLL, OCX) has been loaded or not. It is a kind of debugging information. I would not advise you to add this in each and every program.

To make a list of modules we need a couple of Windows API functions. First we need to get the ID of the current process (that it your executable). You can retrieve this by calling the GetCurrentProcessId Windows API function. In Visual DataFlex code this is:
Code:
External_Function WinAPI_GetCurrentProcessId "GetCurrentProcessId" Kernel32.Dll Returns Integer
and we will use this as:
Code:
Function ProcessModules Returns String[]
    DWord dwProcessId

    Move (WinAPI_GetCurrentProcessId ()) to dwProcessId
    If (dwProcessId <> 0) Begin
    End
End_Function
To enumerate information from the process we need to open the process and this can be done with the OpenProcess Windows API function. The Visual DataFlex function declaration for this would be:
Code:
External_Function WinAPI_OpenProcess "OpenProcess" Kernel32.Dll DWord dwDesiredAccess Boolean bInheritHandle DWord dwProcessId Returns Handle
The first parameter of this function is an access level and we want to make use of PROCESS_VM_READ and PROCESS_QUERY_INFORMATION. These constants are not defined in the Visual DataFlex environment and you need to add these two or add the the whole set.
Code:
Define PROCESS_TERMINATE         for |CI$0001
Define PROCESS_CREATE_THREAD     for |CI$0002
Define PROCESS_SET_SESSIONID     for |CI$0004
Define PROCESS_VM_OPERATION      for |CI$0008
Define PROCESS_VM_READ           for |CI$0010
Define PROCESS_VM_WRITE          for |CI$0020
Define PROCESS_DUP_HANDLE        for |CI$0040
Define PROCESS_CREATE_PROCESS    for |CI$0080
Define PROCESS_SET_QUOTA         for |CI$0100
Define PROCESS_SET_INFORMATION   for |CI$0200
Define PROCESS_QUERY_INFORMATION for |CI$0400
Define PROCESS_ALL_ACCESS        for (STANDARD_RIGHTS_REQUIRED ior SYNCHRONIZE ior |CI$FFFF)
We will pass a FALSE as second parameter as we do not want to inherit handles. The third parameter is the current process ID. With the above we can extend our ProcessModules function to:

Code:
Function ProcessModules Returns String[]
    DWord dwProcessId
    Handle hProcess

    Move (WinAPI_GetCurrentProcessId ()) to dwProcessId
    If (dwProcessId <> 0) Begin
        Move (Winapi_OpenProcess (PROCESS_VM_READ + PROCESS_QUERY_INFORMATION, False, dwProcessId)) to hProcess
        If (hProcess <> 0) Begin
        End
    End
End_Function
The changes are marked in a red color.
The next step is more difficult. The loaded modules all have a handle and we can retrieve the handles via the Windows API function EnumProcessModules. The Visual DataFlex code to import the DLL function is:
Code:
External_Function WinAPI_EnumProcessModules "EnumProcessModules" Psapi.Dll Handle hProcess Address lphModule Integer cb Pointer lpcbNeeded Returns Integer
The function returns an array of handles and the array needs to be big enough to contain all the handles. So how big does the array need to be? 10, 20, 50, 100, 1000? That sounds difficult but the good thing is we can ask the function to return the needed size by specifying a too small buffer. This means we need to call the function twice, once to get the size and the second time with the right buffer. The returned size is in bytes, not in the number of elements for our array so we need to do a little math. The ProcessModules function can be extended to:
Code:
Function ProcessModules Returns String[]
    DWord dwProcessId
    Handle hProcess
    Handle[] hModules
    Integer iSize iVoid iModules
    Boolean bOk

    Move (WinAPI_GetCurrentProcessId ()) to dwProcessId
    If (dwProcessId <> 0) Begin
        Move (Winapi_OpenProcess (PROCESS_VM_READ + PROCESS_QUERY_INFORMATION, False, dwProcessId)) to hProcess
        If (hProcess <> 0) Begin
            Move 0 to iSize
            Move (WinAPI_EnumProcessModules (hProcess, AddressOf (hModules), 0, AddressOf (iSize))) to bOk
            If (not (bOk)) Begin
                Get ShowLastError to iVoid
            End
            Else Begin
                Move (iSize / SizeOfType (Handle)) to iModules
                Move (ResizeArray (hModules, iModules)) to hModules
                Move (WinAPI_EnumProcessModules (hProcess, AddressOf (hModules), iSize, AddressOf (iSize))) to bOk
                If (not (bOk)) Begin
                    Get ShowLastError to iVoid
                End
            End
        End
    End
End_Function
Also in this code block the changes are marked in a red color.
While that array of handles is useful it does not say much a you and me. We can convert the handles into filenames with the GetModuleFileName. Windows API function call. We do not need to import this function ourselves as it is already defined in WinKern.Pkg.
To convert the handles to filenames we create an array of strings and size it to the same size as the handles array and initialize the value of each element with MAX_PATH zero characters with the following instruction:
Code:
Move (ResizeArray (sModuleNames, iModules, Repeat (Character (0), MAX_PATH))) to sModuleNames
Now extend our ProcessModules function again with the following code:
Code:
Else Begin
    Move (ResizeArray (sModuleNames, iModules, Repeat (Character (0), MAX_PATH))) to sModuleNames
    Decrement iModules
    For iModule from 0 to iModules
        Move (GetModuleFileName (hModules[iModule], AddressOf (sModuleNames[iModule]), MAX_PATH)) to iLength
    Loop
End
Finally before we finish off the function we close the opened handle and return the array of filenames. Closing the handle is done with the CloseHandle Windows API function. The function declaration for this is:
Code:
External_Function WinAPI_CloseHandle "CloseHandle" Kernel32.Dll Handle hObject Returns Integer
The complete function is:
Code:
Function ProcessModules Returns String[]
    DWord dwProcessId
    Handle hProcess
    Handle[] hModules
    Integer iSize iVoid iModules iModule iLength
    Boolean bOk
    String[] sModuleNames

    Move (WinAPI_GetCurrentProcessId ()) to dwProcessId
    If (dwProcessId <> 0) Begin
        Move (Winapi_OpenProcess (PROCESS_VM_READ + PROCESS_QUERY_INFORMATION, False, dwProcessId)) to hProcess
        If (hProcess <> 0) Begin
            Move 0 to iSize
            Move (WinAPI_EnumProcessModules (hProcess, AddressOf (hModules), 0, AddressOf (iSize))) to bOk
            If (not (bOk)) Begin
                Get ShowLastError to iVoid
            End
            Else Begin
                Move (iSize / SizeOfType (Handle)) to iModules
                Move (ResizeArray (hModules, iModules)) to hModules
                Move (WinAPI_EnumProcessModules (hProcess, AddressOf (hModules), iSize, AddressOf (iSize))) to bOk
                If (not (bOk)) Begin
                    Get ShowLastError to iVoid
                End
                Else Begin
                    Move (ResizeArray (sModuleNames, iModules, Repeat (Character (0), MAX_PATH))) to sModuleNames
                    Decrement iModules
                    For iModule from 0 to iModules
                        Move (GetModuleFileName (hModules[iModule], AddressOf (sModuleNames[iModule]), MAX_PATH)) to iLength
                    Loop
                End
            End
            Move (WinAPI_CloseHandle (hProcess)) to bOk
        End
    End
    
    Function_Return sModuleNames
End_Function
If you studied the blog and the links to the Microsoft documentation a bit instead of jumping to the end where the full routine is you should have learned how to do a bit more advanced coding in Visual DataFlex.

Updated 15-Apr-2011 at 12:29 PM by Vincent Oorsprong (CloseHandle Function added)

Categories
Uncategorized

Comments