LoadLibrary無法加載.dll解決思路

動態載入DLL所需要的三個函數詳解(LOADLIBRARY,GETPROCADDRESS,FREELIBRARY

  • dll, lib, h的區別
  1. dll是動態庫, 將其鏈接進工程, 一般用LoadLibrary, GetProcAddress調用裏面導出的函數(api文檔);
  2. lib是靜態庫, 將其鏈接進工程, 調用時需要包含一個頭文件, 具體實現放在lib裏了;
  3. h頭文件, 實現放在.c或.cpp裏
  • 靜態鏈接庫LIB和動態鏈接庫DLL的區別
  1. 什麼是靜態連接庫,什麼是動態鏈接庫 靜態鏈接庫與動態鏈接庫都是共享代碼的方式,如果採用靜態鏈接庫,則無論你願不願意,lib 中的指令都全部被直接包含在最終生成的 EXE 文件中了。但是若使用 DLL,該 DLL 不必被包含在最終 EXE 文件中,EXE 文件執行時可以“動態”地引用和卸載這個與 EXE 獨立的 DLL 文件。靜態鏈接庫和動態鏈接庫的另外一個區別在於靜態鏈接庫中不能再包含其他的動態鏈接庫或者靜態庫,而在動態鏈接庫中還可以再包含其他的動態或靜態鏈接 庫。靜態鏈接庫與靜態鏈接庫調用規則總體比較如下。

    對於靜態鏈接庫(比較簡單): 首先,靜態鏈接庫的使用需要庫的開發者提供生成庫的.h頭文件和.lib文件。

    生成庫的.h頭文件中的聲明格式如下: extern “C” 函數返回類型 函數名(參數表); 在調用程序的.cpp源代碼文件中如下: #include “…\lib.h” #pragma comment(lib,"…\debug\libTest.lib") //指定與靜態庫一起鏈接

  2. 因爲靜態鏈接庫是將全部指令都包含入調用程序生成的EXE文件中。因此如果用的是靜態鏈接庫,那麼也就不存在“導出某個函數提供給用戶使用”的情況,要想用就得全要!要不就都別要!?

    對於動態鏈接庫: 動態鏈接庫的使用需要庫的開發者提供生成的.lib文件和.dll文件。或者只提供dll文件。 首先我們必須先注意到DLL內的函數分爲兩種: (1)DLL 導出函數,可供應用程序調用; (2)DLL 內部函數,只能在 DLL 程序使用,應用程序無法調用它們。 因此調用程序若想調用DLL中的某個函數就要以某種形式或方式指明它到底想調用哪一個函數。

  • 動態載入.dll
    動態載入方式是指在編譯之前並不知道將會調用哪些 DLL 函數, 完全是在運行過程中根據需要決定應調用哪些函數。

    方法是:用 LoadLibrary 函數加載動態鏈接庫到內存,用 GetProcAddress函數動態獲得 DLL 函數的入口地址。當一個 DLL 文件用 LoadLibrary 顯式加載後,在任何時刻均可以通過調用 FreeLibrary 函數顯式地從內存中把它給卸載。

    動態調用使用的 Windows API 函數主要有 3 個, 分別是 LoadLibrary、 GetProcAddress 和FreeLibrary。

LoadLibrary 函數

注:Delphi 中還提供了 SafeLoadLibrary 函數,它封裝了 Loadlibrary 函數,可以裝載由 Filename 參數指定的 WindowsDLL或 Linux 共享對象。它簡化了DLL的裝載並且使裝載更加安全。

function LoadLibrary(LibFileName : PChar): Thandle;

[功能]:加載由參數 LibFileName 指定的 DLL 文件。

[說明]:參數 LibFileName 指定了要裝載的 DLL 文件名,如果 LibFileName 沒有包含一個路徑,系統將按照:當前目錄、Windows 目錄、Windows 系統目錄、包含當前任務可執行文件的目錄、列在 PATH 環境變量中的目錄等順序查找文件。

如果函數操作成功,將返回裝載 DLL 庫模塊的實例句柄,否則,將返回一個錯誤代碼,錯誤代碼的定義如引文[1]所示。

GetProcAddress 函數

[格式]:
function GetProcAddress(Module:Thandle; ProcName:PChar): TfarProc;

[功能]:返回參數 Module 指定的模塊中,由參數 ProcName 指定的過程或函數的入口地址。

[說明]:參數 Module 包含被調用函數的 DLL 句柄,這個值由 LoadLibrary 返回, ProcName
是指向含有函數名的以 nil 結尾的字符串指針,或者可以是函數的次序值,但大多數情況下,用函數名是一種更穩妥的選擇。如果該函數執行成功,則返回 DLL 中由參數 ProcName 指定的過程或函數的入口地址,否則返回 nil 。

FreeLibrary 函數

[格式]:
procedure FreeLibrary(Module: Thandle);
[說明]:將由參數 Module 指定的 DLL 文件從內存中卸載 1 次。

[說明]:Module 爲 DLL 庫的句柄。這個值由 LoadLibrary 返回。由於 DLL 在內存中只裝載一次,因此調用 FreeLibrary 首先使 DLL 的引用計數減 1,如果計數減爲 0 則卸載該 DLL。

[注意]:每調用一次 LoadLibrary 函數就應調用一次 FreeLibrary 函數,以保證不會有多餘的庫模塊在應用程序結束後仍留在內存中,否則導致內存泄漏。

加載dll不成功解決思路

用LoadLibrary和GetProcAddress加載DLL時,可能會碰到LoadLibrary無法正確加載DLL,返回值總是NULL的情況。一般而言,可按下面的思路解決:

(1)檢查dll的路徑是否正確。

     可先用絕對路徑試一下,若可以,那說明相對路徑設置錯了。簡單的測試當面目錄的方法就是隨便寫一個文件,比如std::ofstream outfile("test.txt"),然後看輸出的test.txt文件在哪,就知道當前目錄是哪了,然後依據它設置相對目錄。

(2)檢查LoadLibrary輸入參數字符格式是否正確

     LoadLibrary的輸入參數,即DLL的路徑,要求是LPCTSTR類型。一般而言,如果你用

HINSTANCE hFaceDll =LoadLibrary("…/bin/dllTest.dll");
這樣的字符格式加載,那麼會出現“無法將const char [] 轉換爲LPCTSTR”的錯誤。當然,也有可能沒報錯,但是卻不能正確加載,這時候就應在路徑字符串前加"_T",轉換一下格式:

HINSTANCE hFaceDll = LoadLibrary(_T("…/bin/dllTest.dll")) ;
(3)檢查具體的錯誤

如果經過上面兩步,還是不能加載,那肯定就是其他錯誤了。你可以自制一個很小的庫(比如裏面就放個很小的整數求和函數啥的),加載一下,看下能不能成功,如果你自制的可以正確加載,但目的dll卻不能,那就是目的dll本身的問題。這時候就應該進一步找更具體的問題了。

   在LoadLibrary加載語句後面緊跟std::cout<<GetLastError()<<endl;一句,調用GetLastError()函數看具體的錯誤代碼。

一般而言,最有可能的錯誤代碼就是“126”,查找GetLastError的錯誤代碼列表,知道錯誤“126”是指錯誤“ 找不到指定的模塊 ”,這說明你要加載的dll本身還依賴於其他一些文件,找到這些依賴的文件(一般爲一些dll),放到編譯器可以找到的目錄(或在附加庫目錄裏設置下這些dll的路徑),編譯,一般就可以通過了。當然,若是其他錯誤代碼,那就要具體問題具體分析了。
GetLastError()返回值"193", 則需考慮DLL的一致性問題。也就是exe版本必須要和dll版本在debug/release上是一致的,x64或者win32可能也需要保持一致。

附註: 查找dll依賴文件的方法

方法一:用vs自帶的命令

dumpbin -imports MyDll.dll
其中"MyDLL.dll"爲你要分析的dll文件名。具體操作方式爲:打開VS自帶的"Visula Studio命令提示"命令行工具,切換到要分析的dll文件所在目錄下(也可不切換而用全路徑),執行上述命令即可。爲了方便瀏覽,最好在命令後面接重定向符“>”定位到一個文本文件中。如:

dumpbin -imports MyDll.dll > result.txt
方法二:下載工具Dependency Walker (http://www.dependencywalker.com/) 解壓後運行depends.exe,打開要分析的dll,軟件會自動分析,目標dll依賴的文件不存在的話,會在左側列表中用問號標出,並且下面也有錯誤提示。可依據錯誤提示去找這些依賴文件。另外,有時候,雖然你係統有依賴文件(比如opencv的一些庫),但卻檢測到沒有,很可能是因爲你沒設置相應環境變量或者沒有註冊對應dll到註冊表。

[1] https://www.cnblogs.com/westsoft/p/5936092.html
[2] https://www.cnblogs.com/15157737693zsp/p/4179438.html
[3] https://blog.csdn.net/gcola007/article/details/80079092

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章