【发布时间】:2023-03-08 07:36:01
【问题描述】:
我想从其代码中获取 dll 的 目录(或文件)路径。 (不是程序的.exe文件路径)
我尝试了一些我发现的方法:GetCurrentDir - 获取当前目录路径。GetModuleFileName - 获取可执行文件的路径。
那么我怎样才能找出代码在哪个 dll 中呢?
我正在寻找类似于 C# 的Assembly.GetExecutingAssembly
【问题讨论】:
我想从其代码中获取 dll 的 目录(或文件)路径。 (不是程序的.exe文件路径)
我尝试了一些我发现的方法:GetCurrentDir - 获取当前目录路径。GetModuleFileName - 获取可执行文件的路径。
那么我怎样才能找出代码在哪个 dll 中呢?
我正在寻找类似于 C# 的Assembly.GetExecutingAssembly
【问题讨论】:
您可以使用GetModuleHandleEx 函数并获取DLL 中静态函数的句柄。你会发现更多信息here。
之后你可以使用GetModuleFileName从你刚刚获得的句柄中获取路径。有关该电话的更多信息是here。
一个完整的例子:
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.
【讨论】:
lpModuleName 存在 GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 标志可能不仅是本地函数。它也可能是一个(本地)静态变量的地址。
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
....
TCHAR DllPath[MAX_PATH] = {0};
GetModuleFileName((HINSTANCE)&__ImageBase, DllPath, _countof(DllPath));
【讨论】:
__ImageBase 变量未来兼容性的任何cmets?
GetModuleFileName() 在 DLL 代码中可以正常工作。请确保不要将第一个参数设置为NULL,因为这将获取调用进程的文件名。您需要指定 DLL 的实际模块实例。您可以在 DLL 的 DllEntryPoint() 函数中将其作为输入参数,只需将其保存到某个变量中以供以后需要时使用。
【讨论】:
试试GetModuleFileName函数。
【讨论】:
对于 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 的进程传递。
【讨论】:
这是最高投票答案的 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,因为它特定于函数所在的那个 .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
【讨论】:
恕我直言,Remy Lebau 的答案是最好的,但缺乏像所有其他答案一样呈现 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 中使用。
【讨论】:
HMODULE hmod = GetCurrentModule();
TCHAR szPath[MAX_PATH + 1] = 0;
DWORD dwLen = GetModuleFileHName(hmod, szPath, MAX_PATH);
【讨论】: