C++ 在运行时获取 DLL 路径

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6924195/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 20:55:54  来源:igfitidea点击:

Get DLL path at runtime

c++dll

提问by Yochai Timmer

I want to get a dll'sdirectory (or file) path from within its code. (not the program's .exe file path)

我想从其代码中获取dll 的目录(或文件)路径。(不是程序的 .exe 文件路径)

I've tried a few methods I've found:
GetCurrentDir- gets the current directory path.
GetModuleFileName- gets the executable's path.

我尝试了一些我发现的方法:
GetCurrentDir- 获取当前目录路径。
GetModuleFileName- 获取可执行文件的路径。

So how can i find out in which dll the code is in ?
I'm looking for something similar to C#'s Assembly.GetExecutingAssembly

那么我怎样才能找出代码在哪个 dll 中呢?
我正在寻找类似于 C# 的东西Assembly.GetExecutingAssembly

回答by mkaes

You can use the GetModuleHandleExfunction and get the handle to a static function in your DLL. You'll find more information here.

您可以使用该GetModuleHandleEx函数并获取 DLL 中静态函数的句柄。您将在此处找到更多信息。

After that you can use GetModuleFileNameto get the path from the handle you just obtained. More information on that call is here.

之后,您可以使用GetModuleFileName从刚刚获得的句柄中获取路径。有关该电话的更多信息,请点击此处

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.

回答by cprogrammer

EXTERN_C IMAGE_DOS_HEADER __ImageBase;

....

....

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

回答by Remy Lebeau

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.

GetModuleFileName()从 DLL 的代码内部工作正常。请确保不要将第一个参数设置为NULL,因为这将获得调用进程的文件名。您需要改为指定 DLL 的实际模块实例。您将其作为 DLLDllEntryPoint()函数中的输入参数获取,只需将其保存到某个地方的变量中,以备日后需要时使用。

回答by BuvinJ

Here's a Unicode, revised version of the top voted answer:

这是最高投票答案的 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;
}

回答by Mythli

Try GetModuleFileNamefunction.

尝试GetModuleFileName函数。

回答by Ian Boyd

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:

如果您的 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;

回答by Jean-Marc Volle

Provided you implemented the following dll entry point: (usually dllmain.cpp)

如果您实现了以下 dll 入口点:(通常是 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.

dllFilePath 然后将包含加载当前 dll 代码的路径。在这种情况下,hModule 由加载 dll 的进程传递。

回答by Dietrich Baumgarten

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).” I will develop two functions inside the DLL, the first will return its fully qualified name, the second its fully qualified path. Assume the fully qualified name of the DLL is “C:\Bert\Ernie.dll”, then the functions return “C:\Bert\Ernie.dll” and “C:\Bert” respectively. As Remy and Jean-Marc Volle pointed out, the DLL entry function DllMainusually 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:

恕我直言,Remy Lebau 的答案是最好的,但与呈现 DLL 目录的所有其他答案一样缺乏。我引用了最初的问题:“我想从其代码中获取 dll 的目录(或文件)路径。(不是程序的 .exe 文件路径)。” 我将在 DLL 中开发两个函数,第一个将返回其完全限定名称,第二个将返回其完全限定路径。假设DLL的全限定名是“C:\Bert\Ernie.dll”,那么函数分别返回“C:\Bert\Ernie.dll”和“C:\Bert”。正如 Remy 和 Jean-Marc Volle 指出的那样,DllMain通常包含在 dllmain.cpp 中的 DLL 入口函数提供了 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;
}

Now in the file TestDll.cpp I define the function GetFullyQualifiedNameOfDLL(wchar_t* PathFile)that renders the fully qualified name, in our example “C:\Bert\Ernie.dll” and the function GetFullyQualifiedPathToDLL(wchar_t * Path)returning only the path, here “C:\Bert”

现在在文件 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;
}

These functions can be used inside the DLL. A user of this DLL can call these functions as follows:

这些函数可以在 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);
}

回答by TarmoPikaro

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

我想实现类似的东西,除了想将类似的功能放入一个 .dll 中 - 但是您不能使用 __ImageBase,因为它特定于函数所在的 .dll。我什至尝试使用方法覆盖

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 ?

然后我想通了 - 为什么我不使用 VirtualQuery,而是使用函数指针并从那里获取 HMODULE。但是再次 - 如何获取调用者的函数指针?

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

回答by foxy

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