在运行时获取 DLL 路径

我想从它的代码中获得一个 DLL 的目录(或文件)路径(而不是程序的.exe 文件路径)

我尝试了一些我发现的方法:
得到工作目录路径。
获取可执行文件的路径。

我怎么才能知道代码在哪个 dll 里?
我在找类似 C # 的 Assembly.GetExecutingAssembly的东西

86115 次浏览
EXTERN_C IMAGE_DOS_HEADER __ImageBase;

....

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

试试 GetModuleFileName函数。

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

您可以使用 GetModuleHandleEx函数并获得 DLL 中静态函数的句柄。你会发现更多的信息 给你

之后,可以使用 GetModuleFileName从刚才获得的句柄中获取路径。关于这个电话的更多信息是 给你

一个完整的例子:

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.

GetModuleFileName()在 DLL 的代码内工作得很好。只要确保不要将第一个参数设置为 NULL,因为这将获得调用进程的文件名。您需要指定 DLL 的实际模块实例。您可以在 DLL 的 DllEntryPoint()函数中将其作为输入参数,只需将其保存到某个变量中,以便以后需要时使用。

以下是统一码(Unicode) ,经过修订的最受欢迎答案:

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

我想达到类似的东西,除了想使类似的功能成为一个。Dll-但是不能使用 _ _ ImageBase,因为它是特定于 _ _ ImageBase 的。函数所在的 dll。我甚至试过用方法覆盖

GetDllPath( HMODULE hDll = (HMODULE) __ImageBase)

但是这也不起作用。(出于某种原因,在那之后返回应用程序路径。)

然后我明白了——为什么我不使用 VirtualQuery,而是使用函数指针并从中获取 HMODULE。但是再问一次——如何获得来电函数指针?

现在它回到调用堆栈确定-我不会用所有肮脏的细节打扰你,只是按照链接的引用。

下面是整个代码快照:

//
//  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

对于 Delphi 用户:

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

如果您的 Delphi 没有 SysUtils.GetModuleName,那么它被声明为:

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;

前提是您实现了以下 dll 入口点: (通常是 dllmain.cpp)

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

你可以简单地做:

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 将包含当前 dll 代码加载位置的路径。在这种情况下,hModule 通过加载 dll 的进程传递。

Imho,Remy Lebau 的答案是最好的,但是和其他所有答案一样,缺少呈现 DLL 目录的答案。我引用了最初的问题: “我想从 dll 的代码中获取一个 dll 的目录(或文件)路径。不是程序的。Exe 文件路径)。”

Remy 和 Jean-Marc Volle 指出,dllmain.cpp 中通常包含的 DLL 输入函数 DllMain提供了 DLL 的句柄。这个句柄通常是必需的,因此它将被保存在一个全局变量 hMod中。我还添加了 std::wstring类型的变量来存储 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;
}

这些全局变量可以在 DLL 中使用。

我(在我的情况下)将对那些揭示了简单 DllMain 方法的人进行补偿。这项工作是找出在 dll 中运行的当前线程的计算机位数。为了确保万无一失,我还想要运行 dll 和 exe 的名称。调用者的名称由 NULL 参数查找:

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

使用 DllMain 的 hModule 查找 dll 名称和计算机位感觉是最完整和可信的方法。 代码被发现,以便获得“机器”只需要模块。下面是为了调试目的而提出名称的部分。

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