web-dev-qa-db-fra.com

Récupère DLL chemin lors de l'exécution

Je veux obtenir un chemin de répertoire (ou de fichier) dll's à partir de son code. (pas le chemin du fichier .exe du programme)

J'ai essayé quelques méthodes que j'ai trouvées:
GetCurrentDir - obtient le chemin du répertoire courant.
GetModuleFileName - obtient le chemin de l'exécutable.

Alors, comment puis-je savoir dans quelle DLL se trouve le code?
Je recherche quelque chose de similaire à C # Assembly.GetExecutingAssembly

54
Yochai Timmer

Vous pouvez utiliser la fonction GetModuleHandleEx et obtenir le handle vers une fonction statique dans votre DLL. Vous trouverez plus d'informations ici .

Après cela, vous pouvez utiliser GetModuleFileName pour obtenir le chemin à partir du handle que vous venez d'obtenir. Plus d'informations sur cet appel sont ici .

Un exemple complet:

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.
94
mkaes
EXTERN_C IMAGE_DOS_HEADER __ImageBase;

....

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

GetModuleFileName() fonctionne très bien depuis l'intérieur des codes de la DLL. Assurez-vous simplement de ne PAS définir le premier paramètre sur NULL, car cela obtiendra le nom de fichier du processus appelant. Vous devez plutôt spécifier l'instance de module réelle de la DLL. Vous obtenez cela en tant que paramètre d'entrée dans la fonction DllEntryPoint() de la DLL, il suffit de l'enregistrer dans une variable quelque part pour une utilisation ultérieure en cas de besoin.

17
Remy Lebeau

Essayez la fonction GetModuleFileName .

1
Mythli

Pour les utilisateurs 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

Si votre Delphi n'a pas SysUtils.GetModuleName, il est déclaré comme:

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;
1
Ian Boyd

Voici une version révisée Unicode de la réponse la plus votée:

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;
}
1
BuvinJ

À condition d'avoir implémenté le point d'entrée de la DLL suivant: (généralement dllmain.cpp)

BOOL APIENTRY DllMain( HMODULE hModule,
                   DWORD  ul_reason_for_call,
                   LPVOID lpReserved
                 )

Vous pouvez simplement faire:

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 contiendra alors le chemin vers lequel le code dll actuel a été chargé. Dans ce cas, hModule est passé par le processus chargeant la DLL.

0
Jean-Marc Volle

Je voulais réaliser quelque chose de similaire, sauf que je voulais faire une fonction similaire en un seul .dll - mais vous ne pouvez pas utiliser __ImageBase, car il est spécifique à ce .dll où se trouve la fonction. J'ai même essayé de passer outre en utilisant l'approche

GetDllPath( HMODULE hDll = (HMODULE) __ImageBase)

Mais cela n'a pas fonctionné non plus. (Pour une raison quelconque, retourne le chemin de l'application après cela.)

Ensuite, j'ai compris pourquoi je n'utilise pas VirtualQuery, j'utilise le pointeur de fonction et j'obtiens HMODULE à partir de là. Mais encore une fois - comment obtenir le pointeur de fonction de l'appelant?

Et maintenant, il revient à la détermination de la pile d'appels - je ne vais pas vous déranger avec tous les détails sales, suivez simplement les liens des liens référés.

Voici un instantané de code entier:

//
//  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
0
TarmoPikaro

À mon humble avis, la réponse de Remy Lebau est la meilleure, mais elle manque, comme toutes les autres réponses, pour afficher le répertoire de la DLL. Je cite la question d'origine: "Je veux obtenir le chemin du répertoire (ou fichier) d'une DLL à partir de son code. (pas le chemin du fichier .exe du programme). "Je développerai deux fonctions à l'intérieur de la DLL, la première renverra son nom complet, la seconde son chemin complet. Supposons que le nom complet du DLL est "C:\Bert\Ernie.dll", puis les fonctions renvoient "C:\Bert\Ernie.dll" et "C:\Bert" Comme l'ont souligné Remy et Jean-Marc Volle, la fonction d'entrée DLL DllMain généralement contenue dans dllmain.cpp fournit le descripteur de la DLL. Ce descripteur est souvent nécessaire, il sera donc enregistré dans une variable globale 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;
}

Maintenant, dans le fichier TestDll.cpp, je définis la fonction GetFullyQualifiedNameOfDLL(wchar_t* PathFile) qui rend le nom complet, dans notre exemple "C:\Bert\Ernie.dll" et la fonction GetFullyQualifiedPathToDLL(wchar_t * Path) renvoyant uniquement le chemin, ici "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;
}

Ces fonctions peuvent être utilisées dans la DLL. Un utilisateur de ce DLL peut appeler ces fonctions comme suit:

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);
}
0
Dietrich Baumgarten