[DllImport("psapi.dll", SetLastError = true)]
public static extern bool EnumProcessModules(IntPtr hProcess,
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U4)] [In][Out] uint[] lphModule, uint cb, [MarshalAs(UnmanagedType.U4)] out uint lpcbNeeded);
<DllImport("psapi.dll", SetLastError:=True)> _
Public Shared Function EnumProcessModules(ByVal hProcess As IntPtr, <MarshalAs(UnmanagedType.LPArray, ArraySubType := UnmanagedType.U4)> <[In]()> <Out()> ByVal lphModule As UInteger(), ByVal cb As UInteger, <MarshalAs(UnmanagedType.U4)> ByRef lpcbNeeded As UInteger) As Boolean
End Function
None.
System.Diagnostics.Process.Modules
I found that the C# signature above did not work for me. Instead I am using the following signature:
[DllImport("psapi.dll", CallingConvention=CallingConvention.StdCall, SetLastError = true)]
public static extern int EnumProcessModules(IntPtr hProcess, [Out] IntPtr lphModule, uint cb, out uint lpcbNeeded);
Please add some!
Used in conjuction with GetModuleFileNameEx:
Process[] pc = Process.GetProcessesByName("communicator");
foreach (Process p in pc)
{
// Setting up the variable for the second argument for EnumProcessModules
IntPtr[] hMods = new IntPtr[1024];
GCHandle gch = GCHandle.Alloc(hMods, GCHandleType.Pinned); // Don't forget to free this later
IntPtr pModules = gch.AddrOfPinnedObject();
// Setting up the rest of the parameters for EnumProcessModules
uint uiSize = (uint)(Marshal.SizeOf(typeof(IntPtr)) * (hMods.Length));
uint cbNeeded = 0;
if (EnumProcessModules(p.Handle, pModules, uiSize, out cbNeeded) == 1)
{
Int32 uiTotalNumberofModules = (Int32)(cbNeeded / (Marshal.SizeOf(typeof(IntPtr))));
for (int i = 0; i < (int)uiTotalNumberofModules; i++)
{
StringBuilder strbld = new StringBuilder(1024);
GetModuleFileNameEx(p.Handle, hMods[i], strbld, (uint)(strbld.Capacity));
Console.WriteLine("File Path: " + strbld.ToString());
Console.WriteLine();
}
Console.WriteLine("Number of Modules: " + uiTotalNumberofModules);
Console.WriteLine();
}
// Must free the GCHandle object
gch.Free();
}
public enum ListModules : uint
{
LIST_MODULES_DEFAULT = 0x0,
LIST_MODULES_32BIT = 0x01,
LIST_MODULES_64BIT = 0x02,
LIST_MODULES_ALL = 0x03
}
// see: https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-enumprocessmodulesex
[DllImport("psapi.dll", SetLastError = true)]
public static extern bool EnumProcessModulesEx(
IntPtr hProcess,
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U4)][In][Out] IntPtr[] lphModule,
uint cb,
[MarshalAs(UnmanagedType.U4)] out uint lpcbNeeded,
ListModules dwFilterFlag
);
// To avoid cleaning the list,
// the number of modules is returned with a tuple
public (IntPtr[] List, uint Length) GetProcessModules(IntPtr handle)
{
bool listAgain = true;
uint arraySize = 256;
IntPtr[] processMods = new IntPtr[arraySize];
uint arrayBytesSize = arraySize * (uint)IntPtr.Size;
uint bytesCopied = 0;
// Loop until all modules are listed
// See: https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-enumprocessmodules#:~:text=If%20lpcbNeeded%20is%20greater%20than%20cb%2C%20increase%20the%20size%20of%20the%20array%20and%20call%20EnumProcessModules%20again.
// accuracy vs performance, '''make your choice :)'''
while (listAgain)
{
// Stops if:
// - EnumProcessModulesEx return 0 (call failed)
// - All modules are listed
// - The next size of the list is greater than uint.MaxValue
if (EnumProcessModulesEx(handle, processMods, arrayBytesSize, out bytesCopied, ListModules.LIST_MODULES_ALL) &&
arrayBytesSize == bytesCopied && arraySize <= uint.MaxValue - 128)
{
arraySize += 128;
processMods = new IntPtr[arraySize];
arrayBytesSize = arraySize * (uint)IntPtr.Size;
}
else
listAgain = false;
}
return (List: processMods, Length: bytesCopied >> 2);
}
// test with notepad
public void test()
{
Process[] pc = Process.GetProcessesByName("notepad");
if (pc.Length > 0)
{
IntPtr handle = pc[0].Handle;
(IntPtr[] List, uint Length) modules = GetProcessModules(handle);
for (int i = 0; i < modules.Length; i++)
Console.WriteLine("0x{0:X}", modules.List[i].ToInt64());
Console.WriteLine("{0} modules", modules.Length);
}
}