我想从其代码中获取 dll的目录(或文件)路径。 (不是程序的.exe文件路径)
我尝试了一些我发现的方法:
GetCurrentDir
- 获取当前目录路径
GetModuleFileName
- 获取可执行文件的路径。
那么如何才能找到代码所在的dll?
我正在寻找类似于C#的Assembly.GetExecutingAssembly
答案 0 :(得分:89)
您可以使用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.
答案 1 :(得分:32)
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
...
WCHAR DllPath[MAX_PATH] = {0};
GetModuleFileNameW((HINSTANCE)&__ImageBase, DllPath, _countof(DllPath));
答案 2 :(得分:17)
GetModuleFileName()
可以在DLL的代码中正常工作。请确保不要将第一个参数设置为NULL
,因为这将获得调用进程的文件名。您需要指定DLL的实际模块实例。你可以将它作为DLL的DllEntryPoint()
函数中的输入参数,只需将其保存到某个地方的变量中,以便以后在需要时使用。
答案 3 :(得分:1)
尝试GetModuleFileName功能。
答案 4 :(得分:1)
这是最高投票答案的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, 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;
}
答案 5 :(得分:0)
我想要实现类似的东西,除了想要将类似的函数组合成一个.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
答案 6 :(得分:0)
对于Delphi用户:
#include <QtCore>
int main() {
QPointer<QObject> p;
Q_ASSERT(p.isNull());
{
QObject obj;
p = &obj;
Q_ASSERT(!p.isNull());
}
Q_ASSERT(p.isNull());
}
如果您的Delphi没有SysUtils.GetModuleName,则声明为:
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
答案 7 :(得分:0)
假设您实现了以下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的进程传递。
答案 8 :(得分:0)
Imho,雷米·勒鲍(Remy Lebau)的答案是最好的,但与所有其他答案一样,它无法呈现DLL目录。我引用原始问题:“我想从其代码中获取dll的目录(或文件)路径。 (不是程序的.exe文件路径)。”我将在DLL中开发两个函数,第一个将返回其完全限定名称,第二个将返回其完全限定路径。假设DLL的完全限定名称为“ C:\ Bert \ Ernie.dll”,则函数分别返回“ C:\ Bert \ Ernie.dll”和“ C:\ Bert”。
正如Remy和Jean-Marc Volle所指出的那样,dllmain.cpp中通常包含的DLL入口函数DllMain
提供了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);
}
答案 9 :(得分:-4)
HMODULE hmod = GetCurrentModule();
TCHAR szPath[MAX_PATH + 1] = 0;
DWORD dwLen = GetModuleFileHName(hmod, szPath, MAX_PATH);