web-dev-qa-db-ja.com

Get DLL実行時のパス

コード内からdll'sディレクトリ(またはファイル)パスを取得したい。 (プログラムの.exeファイルパスではありません)

私が見つけたいくつかの方法を試しました:
GetCurrentDir-現在のディレクトリパスを取得します。
GetModuleFileName-実行可能ファイルのパスを取得します。

だから、どのdllにコードが入っているのかを知るにはどうすればよいですか?
C#のAssembly.GetExecutingAssembly

54
Yochai Timmer

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

....

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

GetModuleFileName()は、DLLのコード内から正常に機能します。最初のパラメーターをNULLに設定しないでください。呼び出しプロセスのファイル名を取得します。代わりに、DLLの実際のモジュールインスタンスを指定する必要があります。 DLLのDllEntryPoint()関数の入力パラメーターとして取得し、必要なときに後で使用できるように変数に保存するだけです。

17
Remy Lebeau

GetModuleFileName 関数を試してください。

1
Mythli

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

以下は、トップ投票の回答のユニコード、改訂版です。

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

次の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をロードするプロセスによって渡されます。

0
Jean-Marc Volle

同様の機能を1つの.dllにしたかった点を除いて、同様の機能を実現したかったのですが、__ ImageBaseを使用することはできません。私もアプローチを使用してオーバーライドしようとしました

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

レミーレバウの答えは最高ですが、DLLのディレクトリをレンダリングする他のすべての答えとは異なります。元の質問を引用します。「コード内からdllのディレクトリ(またはファイル)パスを取得したい。 (プログラムの.exeファイルパスではありません。)DLL内で2つの関数を開発します。最初の関数は完全修飾名を返し、2番目の関数は完全修飾パスを返します。 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);
}
_
0