Search notes:

Determining loaded modules from VBA

C program

The following C code returns a two dimensional array from a DLL to Visual Basic for Applications with the names and handles of all currently loaded modules (DLLs):
#include <windows.h>
#include <psapi.h>

__declspec(dllexport) SAFEARRAY* __stdcall loadedModules() {

  HMODULE process = GetCurrentProcess();

#define maxNofModules 256
  HMODULE modules[maxNofModules];
  DWORD   bytesNeeded;

  if (! EnumProcessModules(
      process,
      modules, // HMODULE *lphModule,
      sizeof(modules),
     &bytesNeeded
  )) {
      MessageBox(0, "EnumProcessModules", 0, 0);
  }

  int nofModules = bytesNeeded/sizeof(HANDLE);

  SAFEARRAYBOUND dimensions[2];

  dimensions[0].lLbound = 0; dimensions[0].cElements = 2;
  dimensions[1].lLbound = 0; dimensions[1].cElements = nofModules;

  SAFEARRAY *ret;
  ret = SafeArrayCreate(
       VT_VARIANT,  // The returned array is a variant array and
       2         ,  // has two dimensions which are
       dimensions   // described in the variable dimensions
  );

  VARIANT vBaseName;
  vBaseName.vt = VT_BSTR;

  VARIANT vHandle;
  vHandle.vt = VT_I4;

  LONG putIndices[2];
  wchar_t baseName[MAX_PATH];

  for (LONG i=0; i<nofModules; i++) {

   //
   // Get the module name for a specific module handle.
   // Note: we're calling the wide character version of the
   // function (ending in W). This is necessary for the
   // following call to SysAllocString.
   //
      GetModuleBaseNameW(process, modules[i], baseName, MAX_PATH);

      vBaseName.bstrVal = SysAllocString(baseName);
      vHandle.lVal      =(long) modules[i];

   //
   // Set the array value with indices (0, i) to the
   // value of the module name.
   //
      putIndices[0] = 0;
      putIndices[1] = i;
      SafeArrayPutElement(ret, putIndices, &vBaseName);

   //
   // Set the array value with indices (1, i) to the
   // value of the module handle.
   //
      putIndices[0] = 1;
   // putIndices[1] = 1;

      SafeArrayPutElement(ret, putIndices, &vHandle);

  }

  return ret;

}
Github repository VBA-calls-DLL, path: /loadedModules/loadedModules.c

VBA code

The function in the DLL is used from VBA like so:
option explicit

declare function loadedModules lib "C:\github\VBA-calls-DLL\loadedModules\loadedModules.dll" () as variant()

sub main() ' {

    dim modules()  as variant
    dim i          as integer
    dim nofModules as integer

    modules = loadedModules

  '
  ' with the optional rank parameter of uBound, it's possible
  ' to determine how many modules were returned. We set the
  ' rank parameter to two to indicate the second dimension
  '
    nofModules = uBound(modules, 2) 

    for i = 0 to nofModules
        debug.print modules(1, i) & ": " & modules(0, i)
    next i

end sub ' }
Github repository VBA-calls-DLL, path: /loadedModules/loadedModules.bas

Makefile

This is a (gnu) makefile that allows to create the dll (at least with MinGW):
loadedModules.dll: loadedModules.c
	gcc -std=c99 -shared $< -o $@ -Wl,--add-stdcall-alias -lpsapi -lOleAut32
Github repository VBA-calls-DLL, path: /loadedModules/Makefile

See also

Showing exported functions of a DLL in VBA
Calling DLLs from VBA

Index