79

I want to get a dll's directory (or file) path from within its code. (not the program's .exe file path)

I've tried a few methods I've found:
GetCurrentDir - gets the current directory path.
GetModuleFileName - gets the executable's path.

So how can i find out in which dll the code is in ?
I'm looking for something similar to C#'s Assembly.GetExecutingAssembly

Yochai Timmer
  • 48,127
  • 24
  • 147
  • 185

11 Answers11

131

You can use the GetModuleHandleEx function and get the handle to a static function in your DLL. You'll find more information here.

After that you can use GetModuleFileName to get the path from the handle you just obtained. More information on that call is here.

A complete example:

char path[MAX_PATH];
HMODULE hm = NULL;

if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | 
        GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
        (LPCSTR) &functionInThisDll, &hm) == 0)
{
    int ret = GetLastError();
    fprintf(stderr, "GetModuleHandle failed, error = %d\n", ret);
    // Return or however you want to handle an error.
}
if (GetModuleFileName(hm, path, sizeof(path)) == 0)
{
    int ret = GetLastError();
    fprintf(stderr, "GetModuleFileName failed, error = %d\n", ret);
    // Return or however you want to handle an error.
}

// The path variable should now contain the full filepath for this DLL.
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
mkaes
  • 13,781
  • 10
  • 52
  • 72
  • This technique worked perfectly for me. The accepted answer using __ImageBase caused my dll to crash during initialization with VC11. Just linking in the code referencing __ImageBase caused some CRT or ATL initialization code to crash with 0xC0000005. – Dave Mooney Oct 23 '12 at 14:51
  • 2
    It is interesting to note that the `lpModuleName` in the presence of the `GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS` flag might not only be the local function. It might be also the address of **a (local) static variable**. – Janusz Lenar Jan 23 '13 at 22:22
  • +1; great answer (just made use of it); superior to the accepted one. – Bathsheba Aug 20 '13 at 14:20
  • 3
    [PathRemoveFileSpec](http://msdn.microsoft.com/en-us/library/bb773748(VS.85).aspx) is also your friend. – zar Jan 30 '14 at 14:46
  • I agree--using GetModuleHandleEx and GetModuleFileName seems to be the best way to answer this question. I just made a gist, https://gist.github.com/pwm1234/05280cf2e462853e183d, that packages the code from mkaes into a standalone function, get_module_path and shows how to call it from anywhere. – Phil May 31 '15 at 03:09
  • I had to add `#define _WIN32_WINNT 0x0501` to my `stdafx.h` as my copy of `winbase.h` only defines `GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS` if the OS ver > `0x0500`. This is noted in the MS docs for `GetModuleHandleEx( )` – osullivj Jun 30 '15 at 14:52
  • 1
    Worked for me on a win32 dll on Windows 10 64 bit but I also had use wchar_t instead of char and wchar_t full_file_path[MAX_PATH]; and cast to (LPCWSTR)&functionInThisDll rather than (LPCSTR) &functionInThisDll – Noctiluque May 06 '20 at 10:27
  • There is an issue with this solution that if the DLL cannot be loaded (due to dependency issues) the function GetModuleHandleEx will fail - making the dll appear not 'installed' while it is – Noam Rathaus May 19 '20 at 12:55
  • @NoamRathaus: If the DLL cannot load, then its code does not run, and "get the DLL's file path from within its code" is moot. – Ben Voigt Jun 14 '23 at 17:12
45
EXTERN_C IMAGE_DOS_HEADER __ImageBase;

....

TCHAR   DllPath[MAX_PATH] = {0};
GetModuleFileName((HINSTANCE)&__ImageBase, DllPath, _countof(DllPath));
cprogrammer
  • 5,503
  • 3
  • 36
  • 56
  • 4
    Any comments about future compatibility of `__ImageBase` variable? – Ajay Aug 03 '11 at 09:31
  • 1
    __ ImageBase is variable of type IMAGE_DOS_HEADER, and this is coming first in the PE (portable executable format). It's a windows structure and is available only under Windows. In my opinion is safe to use and will not be changed in the future. An alternative will be GetModuleHandle but needs dll name. – cprogrammer Aug 03 '11 at 09:40
  • I just read about this global variable. I am aware about PE headers, but didn't know about this. Thanks! – Ajay Aug 03 '11 at 09:46
  • 1
    It is used in MS sample code, so should be safe. For example, see http://msdn.microsoft.com/en-us/library/windows/desktop/dd742738(v=vs.85).aspx – Harry Johnston Dec 03 '14 at 02:54
  • Why not use GetModuleHandle instead of __ImageBase? – Agnel Kurian Apr 21 '16 at 09:23
  • @AgnelKurian That requires another function call and it should return the pointer at `__ImageBase`. – Motomotes Sep 12 '16 at 23:53
  • 2
    Why donť you just use what you get in DllMain instead of __ImageBase? – Pyjong Oct 28 '16 at 15:14
  • Why use `GetModuleFileNameW` instead of `GetModuleFileNameA` ? – Haseeb Mir Jul 24 '20 at 03:27
  • @HaseeBMir Because the OS is unicode and you can have app in folder that contains unicode chars. GetModuleFileNameW is for unicode, in example I have used WCHAR. However if you really "need" the path as char array you can use GetModuleFileNameA. – cprogrammer Jul 24 '20 at 09:32
  • Ok but why dont system decide it they have Macros for it like GetModuleFileName which checks for `ifdef _UNICODE` and use `GetModuleFileNameW` version otherwise will use ASCII version – Haseeb Mir Jul 24 '20 at 11:17
  • Yes, you are right. It should be generic. However I strongly advise you to use Unicode. You cannot control the filesystem, user can and will do anything. – cprogrammer Jul 24 '20 at 12:50
24

GetModuleFileName() works fine from inside the DLL's codes. Just be sure NOT to set the first parameter to NULL, as that will get the filename of the calling process. You need to specify the DLL's actual module instance instead. You get that as an input parameter in the DLL's DllEntryPoint() function, just save it to a variable somewhere for later use when needed.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
5

Here's a Unicode, revised version of the top voted answer:

CStringW thisDllDirPath()
{
    CStringW thisPath = L"";
    WCHAR path[MAX_PATH];
    HMODULE hm;
    if( GetModuleHandleExW( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | 
                            GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
                            (LPWSTR) &thisDllDirPath, &hm ) )
    {
        GetModuleFileNameW( hm, path, MAX_PATH );
        PathRemoveFileSpecW( path );
        thisPath = CStringW( path );
        if( !thisPath.IsEmpty() && 
            thisPath.GetAt( thisPath.GetLength()-1 ) != '\\' ) 
            thisPath += L"\\";
    }
    else if( _DEBUG ) std::wcout << L"GetModuleHandle Error: " << GetLastError() << std::endl;
    
    if( _DEBUG ) std::wcout << L"thisDllDirPath: [" << CStringW::PCXSTR( thisPath ) << L"]" << std::endl;       
    return thisPath;
}
rorlork
  • 523
  • 5
  • 15
BuvinJ
  • 10,221
  • 5
  • 83
  • 96
3

Provided you implemented the following dll entry point: (usually dllmain.cpp)

BOOL APIENTRY DllMain( HMODULE hModule,
                   DWORD  ul_reason_for_call,
                   LPVOID lpReserved
                 )

You can simply do:

switch (ul_reason_for_call)
{ 
case DLL_PROCESS_ATTACH:
{
    TCHAR dllFilePath[512 + 1] = { 0 };
    GetModuleFileNameA(hModule, dllFilePath, 512)
}
    break;
case DLL_THREAD_ATTACH: break;
...

dllFilePath will then contain the path to where the current dll code was loaded. In this case hModule is passed by the process loading the dll.

Jean-Marc Volle
  • 3,113
  • 1
  • 16
  • 20
2

Imho, Remy Lebau’s answer is the best, but lacks like all other answers to render the directory of the DLL. I quote the original question: “I want to get a dll's directory (or file) path from within its code. (not the program's .exe file path).”

As Remy and Jean-Marc Volle pointed out, the DLL entry function DllMain usually contained in dllmain.cpp provides the handle to the DLL. This handle is often necessary, so it will be saved in a global variable hMod. I also add variables of type std::wstring to store the fully qualified name and the parent path of the DLL.

HMODULE hMod;
std::wstring PathAndName;
std::wstring OnlyPath;
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
  switch (ul_reason_for_call)
  {
    case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH:
       case DLL_PROCESS_DETACH:
     break;
  }
  hMod = hModule;
  const int BUFSIZE = 4096;
  wchar_t buffer[BUFSIZE];
  if (::GetModuleFileNameW(hMod, buffer, BUFSIZE - 1) <= 0)
  {
    return TRUE;
  }

  PathAndName = buffer;

  size_t found = PathAndName.find_last_of(L"/\\");
  OnlyPath = PathAndName.substr(0, found);

  return TRUE;
}

These global variables can be used inside the DLL.

1

For Delphi users:

SysUtils.GetModuleName(hInstance);              //Works; hInstance is a special global variable
SysUtils.GetModuleName(0);                      //Fails; returns the name of the host exe process
SysUtils.GetModuleName(GetModuleFilename(nil)); //Fails; returns the name of the host exe process

In case your Delphi doesn't have SysUtils.GetModuleName, it is declared as:

function GetModuleName(Module: HMODULE): string;
var
   modName: array[0..32767] of Char; //MAX_PATH is for a single filename; paths can be up to 32767 in NTFS - or longer.
begin
   {
      Retrieves the fully qualified path for the file that contains the specified module. 
      The module must have been loaded by the current process.
   }
   SetString(Result, modName, GetModuleFileName(Module, modName, Length(modName)));
end;
Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219
1

Try GetModuleFileName function.

arrowd
  • 33,231
  • 8
  • 79
  • 110
Mythli
  • 5,995
  • 2
  • 24
  • 31
0

I wanted to achieve something similar, except wanted to make similar function into one .dll - but then you cannot use __ImageBase, since it's specific to that .dll where function is located. I've even tried to override using approach

GetDllPath( HMODULE hDll = (HMODULE) __ImageBase)

But that did not work our either. (For some reason returns application path after that.)

Then I've figured out - why I don't use VirtualQuery, and use function pointer and get HMODULE from there. But again - how to get function pointer of caller ?

And now it gets back to call stack determination - I won't bother you with all dirty details, just follow links of referred links.

Here is whole code snapshot:

//
//  Originated from: https://sourceforge.net/projects/diagnostic/
//
//  Similar to windows API function, captures N frames of current call stack.
//  Unlike windows API function, works with managed and native functions.
//
int CaptureStackBackTrace2( 
    int FramesToSkip,                   //[in] frames to skip, 0 - capture everything.
    int nFrames,                        //[in] frames to capture.
    PVOID* BackTrace                    //[out] filled callstack with total size nFrames - FramesToSkip
)
{
#ifdef _WIN64
    CONTEXT ContextRecord;
    RtlCaptureContext(&ContextRecord);

    UINT iFrame;
    for (iFrame = 0; iFrame < (UINT)nFrames; iFrame++)
    {
        DWORD64 ImageBase;
        PRUNTIME_FUNCTION pFunctionEntry = RtlLookupFunctionEntry(ContextRecord.Rip, &ImageBase, NULL);

        if (pFunctionEntry == NULL)
        {
            if (iFrame != -1)
                iFrame--;           // Eat last as it's not valid.
            break;
        }

        PVOID HandlerData;
        DWORD64 EstablisherFrame;
        RtlVirtualUnwind(0 /*UNW_FLAG_NHANDLER*/,
            ImageBase,
            ContextRecord.Rip,
            pFunctionEntry,
            &ContextRecord,
            &HandlerData,
            &EstablisherFrame,
            NULL);

        if(FramesToSkip > (int)iFrame)
            continue;

        BackTrace[iFrame - FramesToSkip] = (PVOID)ContextRecord.Rip;
    }
#else
    //
    //  This approach was taken from StackInfoManager.cpp / FillStackInfo
    //  http://www.codeproject.com/Articles/11221/Easy-Detection-of-Memory-Leaks
    //  - slightly simplified the function itself.
    //
    int regEBP;
    __asm mov regEBP, ebp;

    long *pFrame = (long*)regEBP;               // pointer to current function frame
    void* pNextInstruction;
    int iFrame = 0;

    //
    // Using __try/_catch is faster than using ReadProcessMemory or VirtualProtect.
    // We return whatever frames we have collected so far after exception was encountered.
    //
    __try {
        for (; iFrame < nFrames; iFrame++)
        {
            pNextInstruction = (void*)(*(pFrame + 1));

            if (!pNextInstruction)     // Last frame
                break;

            if (FramesToSkip > iFrame)
                continue;

            BackTrace[iFrame - FramesToSkip] = pNextInstruction;
            pFrame = (long*)(*pFrame);
        }
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
    }

#endif //_WIN64
    iFrame -= FramesToSkip;
    if(iFrame < 0)
        iFrame = 0;

    return iFrame;
} //CaptureStackBackTrace2



//
//  Gets .dll full path or only directory.
//
CStringW GetDllPath( bool bPathOnly /* = false */ )
{
    void* pfunc = &GetDllPath;
    wchar_t path[MAX_PATH] = { 0 };
    MEMORY_BASIC_INFORMATION info;
    HMODULE hdll;

    CaptureStackBackTrace2(1, 2, &pfunc);

    // Get the base address of the module that holds the current function
    VirtualQuery(pfunc, &info, sizeof(MEMORY_BASIC_INFORMATION));

    // MEMORY_BASIC_INFORMATION::AllocationBase corresponds to HMODULE
    hdll = (HMODULE)info.AllocationBase;

    // Get the dll filename
    if ( !GetModuleFileName( hdll, path, MAX_PATH ) )
        return L"";

    if ( bPathOnly )
    {
        wchar_t* p = wcsrchr( path, '\\' );
        if ( p )
            *p = 0;
    }

    return path;
} //GetDllPath
TarmoPikaro
  • 4,723
  • 2
  • 50
  • 62
  • Next to the error-prone stacktrace function, you return 'path' in GetDllPath(), which is local array, so destroyed upon return. This will crash. Just using GetModuleFileName() with the DLL handle as passed in DllMain() (described above) works in 3 lines of code. – Ruud van Gaal May 11 '17 at 12:38
  • Use my code sample only if you want to place GetDllPath into shared dll and use it from other dlls. Simpler approach would be to write GetDllPath for each dll separately, but then you will have multiple copies of same function or same source code is included from multiple dlls - this is a solution as well of course. – TarmoPikaro May 11 '17 at 20:54
  • But 'path' is created on the stack, and as soon as GetDllPath() returns this variable will disappear. So using the string is bound to crash, right? – Ruud van Gaal Aug 24 '17 at 08:27
  • 1
    Function itself creates copy of string to caller. Should not crash in theory. – TarmoPikaro Aug 24 '17 at 14:29
  • I see 'wchar_t path', so it's on the stack. Then 'return path' which returns a pointer to a stack item. But indeed, I see an implicit CStringW as the return type, which probably makes the copy. Sorry for the confusion. – Ruud van Gaal Aug 30 '17 at 09:18
0

I'm (in my case) comping those who revealed the easy DllMain method. The job was to find what is the machine bitness of the current thread running inside of a dll. To be absolutely sure I wanted also the name of running dll and exe. Name of caller is found by NULL parameter:

char exePat[MAX_PATH] = { 0 }; GetModuleFileName(NULL, exePat, MAX_PATH);

Using the hModule of DllMain feels most complete and trustable way to find dll name and machine bitness. Code was found so that to obtain the "machine" only module is needed. Here is the part which brings up the name too for debugging purpose.

BOOL APIENTRY DllMain(HMODULE hModule, DWORD  reason,LPVOID lpReserved ) {

     char  path[MAX_PATH] = { 0 };
     GetModuleFileNameA(hModule, path, MAX_PATH);
     PIMAGE_DOS_HEADER startPoint = (PIMAGE_DOS_HEADER)hModule;
     PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((LPBYTE)startPoint + startPoint->e_lfanew);
     PIMAGE_FILE_HEADER fileHeader = &pNtHeaders->FileHeader;
     short machine = fileHeader->Machine;
     ////0x8664 is 64bit  0x14c is 32bit.
     int stop = 0;
     return TRUE;
 }
Tonecops
  • 127
  • 9
-4
HMODULE hmod = GetCurrentModule();
TCHAR szPath[MAX_PATH + 1] = 0;
DWORD dwLen = GetModuleFileHName(hmod, szPath, MAX_PATH);
foxy
  • 7,599
  • 2
  • 30
  • 34