Получить DLL-путь во время выполнения
Я хочу получить каталог dll (или файл) из своего кода. (не путь к файлу программы .exe)
Я пробовал несколько методов, которые я нашел:
GetCurrentDir
- получает текущий путь к каталогу.
GetModuleFileName
- получает исполняемый путь.
Итак, как я могу узнать, в какой DLL код находится?
Я ищу что-то похожее на С# Assembly.GetExecutingAssembly
Ответы
Ответ 1
Вы можете использовать функцию GetModuleHandleEx
и получить указатель на статическую функцию в вашей DLL. Вы найдете больше информации здесь.
После этого вы можете использовать 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.
Ответ 2
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
....
WCHAR DllPath[MAX_PATH] = {0};
GetModuleFileNameW((HINSTANCE)&__ImageBase, DllPath, _countof(DllPath));
Ответ 3
GetModuleFileName()
отлично работает внутри DLL-кодов. Просто убедитесь, что НЕ установите первый параметр NULL
, так как это получит имя файла вызывающего процесса. Вместо этого вам нужно указать экземпляр фактического модуля DLL. Вы получаете это как входной параметр в функции DLL DllEntryPoint()
, просто сохраните его в переменной где-нибудь для последующего использования, когда это необходимо.
Ответ 4
Попробуйте функцию GetModuleFileName.
Ответ 5
Здесь Юникод, пересмотренная версия верхнего проголосовавшего ответа:
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, sizeof(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;
}
Ответ 6
Для пользователей 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;
Ответ 7
Я хотел добиться чего-то подобного, за исключением того, что хотел бы сделать подобную функцию в одной .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 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
Ответ 8
При условии, что вы внедрили следующую точку входа 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.
Ответ 9
Имхо, ответ реми Лебауса самый лучший, но, как и все остальные, ему не хватает для отображения каталога DLL. Я цитирую оригинальный вопрос: "Я хочу получить путь к каталогу (или файлу) библиотеки DLL из его кода. (не путь к файлу .exe программы). ' Я разработаю две функции внутри DLL, первая вернет полностью определенное имя, вторая - полностью уточненный путь. Предположим, что полное имя библиотеки DLL - "C:\Bert\Ernie.dll", тогда функции возвращают "C:\Bert\Ernie.dll" и "C:\Bert" соответственно.
Как отмечали Реми и Жан-Марк Волле, функция ввода DLL DllMain
, обычно содержащаяся в dllmain.cpp, обеспечивает дескриптор библиотеки DLL. Этот дескриптор часто необходим, поэтому он будет сохранен в глобальной переменной hMod
:
HMODULE hMod;
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;
return TRUE;
}
Теперь в файле TestDll.cpp я определяю функцию GetFullyQualifiedNameOfDLL(wchar_t* PathFile)
, которая отображает полное имя, в нашем примере "C:\Bert\Ernie.dll" и функцию GetFullyQualifiedPathToDLL(wchar_t * Path)
, возвращающую здесь только путь ". C:\Bert '
// TestDll.cpp : Defines the exported functions for the DLL application.
#include "stdafx.h"
extern HMODULE hMod;
extern "C" __declspec(dllexport) DWORD _stdcall GetFullyQualifiedNameOfDLL(wchar_t * PathFile)
{
return ::GetModuleFileNameW(hMod, PathFile, MAX_PATH);
}
extern "C" __declspec(dllexport) DWORD _stdcall GetFullyQualifiedPathToDLL(wchar_t * Path)
{
wchar_t PathFile[MAX_PATH];
if (GetFullyQualifiedNameOfDLL(PathFile) == 0)
{
return 0;
}
wchar_t *pszFileName;
DWORD FLen = ::GetFullPathNameW(PathFile, MAX_PATH, Path, &pszFileName);
if (FLen == 0 || FLen >= MAX_PATH)
{
return 0;
}
*pszFileName = 0;
return 1;
}
Эти функции можно использовать внутри DLL. Пользователь этой библиотеки DLL может вызывать эти функции следующим образом:
void _tmain(int argc, TCHAR *argv[])
{
wchar_t PathFile[MAX_PATH], Path[MAX_PATH];
GetFullyQualifiedNameOfDLL(PathFile);
wprintf(L"Fully qualified name: %s\n", PathFile);
//Split the fully qualified name to get the path and name
std::wstring StdPath = PathFile;
size_t found = StdPath.find_last_of(L"/\\");
wprintf(L"Path: %s, name: %s\n", StdPath.substr(0, found).c_str(), StdPath.substr(found + 1).c_str());
GetFullyQualifiedPathToDLL(Path);
wprintf(L"Path: %s\n", Path);
}
Ответ 10
HMODULE hmod = GetCurrentModule();
TCHAR szPath[MAX_PATH + 1] = 0;
DWORD dwLen = GetModuleFileHName(hmod, szPath, MAX_PATH);