How to Enumerate All Process Modules
by
, 15-Apr-2011 at 09:56 AM (6407 Views)
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:
and we will use this as:Code:External_Function WinAPI_GetCurrentProcessId "GetCurrentProcessId" Kernel32.Dll Returns Integer
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:Function ProcessModules Returns String[] DWord dwProcessId Move (WinAPI_GetCurrentProcessId ()) to dwProcessId If (dwProcessId <> 0) Begin End End_Function
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:External_Function WinAPI_OpenProcess "OpenProcess" Kernel32.Dll DWord dwDesiredAccess Boolean bInheritHandle DWord dwProcessId Returns Handle
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: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)
The changes are marked in a red color.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 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:
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:External_Function WinAPI_EnumProcessModules "EnumProcessModules" Psapi.Dll Handle hProcess Address lphModule Integer cb Pointer lpcbNeeded Returns Integer
Also in this code block the changes are marked in a red color.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
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:
Now extend our ProcessModules function again with the following code:Code:Move (ResizeArray (sModuleNames, iModules, Repeat (Character (0), MAX_PATH))) to sModuleNames
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: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
The complete function is:Code:External_Function WinAPI_CloseHandle "CloseHandle" Kernel32.Dll Handle hObject Returns Integer
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.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