EnumProcessModules (psapi)
Last changed: Oara-80.215.74.132

.
Summary
Retrieves a handle for each module in the specified process.

C# Signature:

[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);

VB.NET Signature:

<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

User-Defined Types:

None.

Alternative Managed API:

System.Diagnostics.Process.Modules

Notes:

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);

Tips & Tricks:

Please add some!

Sample Code:

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();
        }

Sample Code:

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);
     }
}

Documentation