Get DLL path at runtime

C++Dll

C++ Problem Overview


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

C++ Solutions


Solution 1 - C++

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.

Solution 2 - C++

EXTERN_C IMAGE_DOS_HEADER __ImageBase;

....

TCHAR	DllPath[MAX_PATH] = {0};
GetModuleFileName((HINSTANCE)&__ImageBase, DllPath, _countof(DllPath));

Solution 3 - C++

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.

Solution 4 - C++

Try GetModuleFileName function.

Solution 5 - C++

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

Solution 6 - C++

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;

Solution 7 - C++

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.

Solution 8 - C++

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

Solution 9 - C++

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.

Solution 10 - C++

HMODULE hmod = GetCurrentModule();
TCHAR szPath[MAX_PATH + 1] = 0;
DWORD dwLen = GetModuleFileHName(hmod, szPath, MAX_PATH);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionYochai TimmerView Question on Stackoverflow
Solution 1 - C++mkaesView Answer on Stackoverflow
Solution 2 - C++cprogrammerView Answer on Stackoverflow
Solution 3 - C++Remy LebeauView Answer on Stackoverflow
Solution 4 - C++MythliView Answer on Stackoverflow
Solution 5 - C++BuvinJView Answer on Stackoverflow
Solution 6 - C++Ian BoydView Answer on Stackoverflow
Solution 7 - C++Jean-Marc VolleView Answer on Stackoverflow
Solution 8 - C++TarmoPikaroView Answer on Stackoverflow
Solution 9 - C++Dietrich BaumgartenView Answer on Stackoverflow
Solution 10 - C++foxyView Answer on Stackoverflow