Подтвердить что ты не робот

Получить DLL-путь во время выполнения

Я хочу получить каталог dll (или файл) из своего кода. (не путь к файлу программы .exe)

Я пробовал несколько методов, которые я нашел:
GetCurrentDir - получает текущий путь к каталогу.
GetModuleFileName - получает исполняемый путь.

Итак, как я могу узнать, в какой DLL код находится?
Я ищу что-то похожее на С# Assembly.GetExecutingAssembly

4b9b3361

Ответ 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(), просто сохраните его в переменной где-нибудь для последующего использования, когда это необходимо.

Ответ 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);