2015-09-15 87 views
3

鏈接從另一個DLL一個DLL我已動態以下文件結構的Visual C++:使用相對路徑

C:\Application\application.exe 
C:\Application\plugins\myplugin\myplugin.dll 
C:\Application\plugins\myplugin\libs\utils.dll 

這裏application.exe負載myplugin.dll經由LoadLibrary。請注意,我無法控制application.exe,因爲我只開發插件。

我想是讓myplugin.dll負載libs\utils.dll通過相對路徑(最好使用靜態鏈接)。也就是說,我不想依賴application.exe的位置。我目前在安裝myplugin時將C:\Application\plugins\myplugin\libs添加到PATH環境變量中,但環境變量不是理想的解決方案,我想避免這樣做。

我希望我可以使用程序集和配置文件來指定相對路徑libs\utils.dllmyplugin.dll。我試過這個,但無濟於事。然後我在StackOverflow上看到有人提到配置文件僅適用於應用程序(即可執行文件)。但正如我上面所說,我無法控制application.exe。有沒有解決這個看起來很簡單的問題,我相信在Unix系統上可以通過rpath解決?

+3

號必須使用GetModuleFileName()來發現您的安裝位置。附加子目錄+文件名。 –

回答

3

你不能靜態鏈接到一個DLL的路徑可言,相對或絕對的。 PE導入表只包含文件名。這就是爲什麼DLL search path存在以查找DLL。

如果你想控制在何處utils.dll從加載,你必須動態加載。 myplugin.dll可以使用GetModuleFileName()檢索自己的路徑,使用傳入其DllMain()入口點的模塊句柄。然後,它可以從路徑中刪除文件名,將相對路徑追加到路徑,然後在需要時加載DLL(不在DllMain()本身內,否則可能發生死鎖/崩潰)。

有兩種方法可以處理這個問題:

  1. 負載的一切動態自己:

    #include <windows.h> 
    #include <shlwapi.h> 
    
    #pragma comment(lib, "shlwapi.lib") 
    
    HINSTANCE hThisDLL = NULL; 
    HMODULE hUtils = NULL; 
    
    typedef ReturnType __CallingConv (*DllFuncType)(Params); 
    DllFuncType DllFunc = NULL; 
    
    BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) 
    { 
        if (fdwReason == DLL_PROCESS_ATTACH) 
        { 
         hThisDLL = hinstDLL; 
         ... 
        } 
    
        return TRUE; 
    } 
    
    ... 
    
    ReturnType CallDllFunc(Params) 
    { 
        if (!hUtils) 
        { 
         TCHAR szUtilsFileName[MAX_PATH] = {0}; 
         GetModuleFileName(hThisDLL, szUtilsFileName, MAX_PATH); 
    
         if (!PathRemoveFileSpec(szUtilsFileName)) 
         { 
          // do something... 
          return ...; 
         } 
    
         if (!PathAppend(szUtilsFileName, TEXT("libs\\utils.dll"))) 
         { 
          // do something... 
          return ...; 
         } 
    
         hUtils = LoadLibrary(szUtilsFileName); 
         if (!hUtils) 
         { 
          // do something... 
          return ...; 
         } 
        } 
    
        if (!DllFunc) 
        { 
         DllFunc = (DllFuncType) GetProcAddress(hUtils, "DllFuncName"); 
         if (!DllFunc) 
         { 
          // do something... 
          return ...; 
         } 
        } 
    
        return DllFunc(Params); 
    } 
    
  2. 靜態鏈接,一切都像往常一樣,但然後利用你的編譯器的delay load功能(如果支持),所以你可以在運行時動態指定DLL的文件名,但仍然靜態鏈接到DLL函數本身(延遲加載機制將爲你調用GetProcAddress())。

    #include <windows.h> 
    #include <shlwapi.h> 
    #include <delayimp.h> 
    
    #pragma comment(lib, "Delayimp.lib") 
    #pragma comment(lib, "shlwapi.lib") 
    
    HINSTANCE hThisDLL = NULL; 
    
    FARPROC WINAPI DelayLoadHook(unsigned dliNotify, PDelayLoadInfo pdli) 
    { 
        if ((dliNotify == dliNotePreLoadLibrary) && 
         (strcmpi(pdli->szDll, "utils.dll") == 0)) 
        { 
         TCHAR szUtilsFileName[MAX_PATH] = {0}; 
         GetModuleFileName(hThisDLL, szUtilsFileName, MAX_PATH); 
    
         if (!PathRemoveFileSpec(szUtilsFileName)) 
         { 
          // do something... 
          return NULL; 
         } 
    
         if (!PathAppend(szUtilsFileName, TEXT("libs\\utils.dll"))) 
         { 
          // do something... 
          return NULL; 
         } 
    
         HMODULE hUtils = LoadLibrary(szUtilsFileName); 
         return reinterpret_cast<FARPROC>(hUtils); 
        } 
    
        return NULL; 
    } 
    
    PfnDliHook __pfnDliNotifyHook2 = DelayLoadHook; 
    
    BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) 
    { 
        if (fdwReason == DLL_PROCESS_ATTACH) 
        { 
         hThisDLL = hinstDLL; 
         ... 
        } 
    
        return TRUE; 
    } 
    
    ... 
    
    ReturnType CallDllFunc(Params) 
    { 
        return DllFuncName(Params); 
    } 
    
+0

謝謝Remy,延遲加載解決了我的問題!在Unix上它比rpath更復雜,但沒關係。順便說一句,而不是在一個全局變量保持手柄當前的模塊,有可能獲得這樣的'DelayLoadHook'函數內部的句柄: 'HMODULE hThisDLL = NULL; GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,(LPCTSTR)DelayLoadHook,與hThisDLL);'這樣一個並不一定必須實現'DllMain'功能。 –

+0

@IliyanGeorgiev:也許,但我認爲這會慢一些,因爲它必須驗證內存地址並找到相應的模塊,而'DllMain()'已經直接爲您提供模塊句柄。 –

+0

我同意,它可能會更慢,雖然這段代碼只執行一次,對嗎? –