標籤:style blog http color io os ar 使用 strong
Windows下DLL尋找順序
Tocy 時間:2014-10-18
一、寫作初衷
在Windows下單個DLL可能存在多個不同的版本,若不特別指定DLL的絕對路徑或使用其他手段指定,在應用程式載入DLL時可能會尋找到錯誤的版本,進而引出各種莫名其妙的問題。本文主要考慮以下兩個方面:
a. 參考MSDN,給出Windows下DLL尋找順序
b. 簡單使用ProcessMonitor來驗證DLL尋找順序
二、DLL尋找順序
(本部分多數內容是參考MSDN上的Dynamic-Link Library Search Order一文,連結如下http://msdn.microsoft.com/en-us/library/ms682586(v=vs.85).aspx。多數為翻譯,有部分內容修改。本文僅關注傳統型應用程式的尋找順序,對於Windows Store apps請參考MSDN原文。)
1. DLL尋找路徑基礎
應用程式可以通過以下方式控制一個DLL的載入路徑:使用全路徑載入、使用DLL重新導向、使用manifest檔案。如果上述三種方式均未指定,系統尋找DLL的順序將按照本部分描述的順序進行。
對於以下兩種情況的DLL,系統將不會尋找,而是直接載入:
a. 對於已經載入到記憶體中的同名DLL,系統使用已經載入的DLL,並且忽略待載入DLL的路徑。(注意對某個進程而言,系統已經載入的DLL一定是唯一的存在於某個目錄下。)
b. 如果該DLL存在於某個Windows版本的已知DLL列表(unkown DLL)中,系統使用已知DLL的拷貝(包括已知DLL的依賴項)。已知DLL列表可以從如下登錄機碼看到:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs。
這裡有個比較坑的地方,對於有依賴項的DLL(即使使用全路徑指定DLL位置),系統尋找其所依賴DLL的方法是按照實際的模組名稱來的,因此如果載入的DLL不在系統尋找順序目錄下,那麼動態載入該DLL(LoadLibrary)會返回一個"找不到模組"的錯誤。
2. 系統標準DLL尋找順序
系統使用的標準DLL尋找順序依賴於是否設定了"安全DLL尋找模式"(safe DLL search mode)。"安全DLL尋找模式"會將使用者目前的目錄置於尋找順序的後邊。
"安全DLL尋找模式"預設是啟用的,禁用的話,可以將登錄機碼HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode設為0。調用SetDllDirectory函數可以禁用"安全DLL尋找模式",並修改DLL尋找順序。
Windows XP下,"安全DLL尋找模式"預設是禁用的,需要啟用該項的話,在註冊表中建立一個SafeDllSearchMode子項,並賦值為1即可。"安全DLL尋找模式"從Windows XP SP2開始,預設是啟用的。
啟用"安全DLL尋找模式"時,尋找順序如下:
a. 應用程式所在目錄;
b. 系統目錄。GetSystemDirectory返回的目錄,通常是系統硬碟\Windows\System32;
c. 16位系統目錄。該項只是為了向前相容的處理,可以不考慮;
d. Windows目錄。GetWindowsDirectory返回的目錄,通常是系統硬碟\Windows;
e. 目前的目錄。GetCurrentDirectory返回的目錄;
f. 環境變數PATH中所有目錄。
如果"安全DLL尋找模式"被禁用,尋找順序如下:
a. 應用程式所在目錄;
b. 目前的目錄。GetCurrentDirectory返回的目錄;
c. 系統目錄。GetSystemDirectory返回的目錄,通常是系統硬碟\Windows\System32;
d. 16位系統目錄。該項只是為了向前相容的處理,可以不考慮;
e. Windows目錄。GetWindowsDirectory返回的目錄,通常是系統硬碟\Windows;
f. 環境變數PATH中所有目錄。
3. 修改系統DLL尋找順序
系統使用的標準DLL尋找順序可以通過以下兩種方式調整:
3.1 使用LOAD_WITH_ALTERED_SEARCH_PATH標誌調用LoadLibraryEx函數;
這種方式調用LoadLibraryEx函數,需要設定lpFileName參數(絕對路徑)。與標準尋找策略不同的是,使用LOAD_WITH_ALTERED_SEARCH_PATH標誌調用LoadLibraryEx函數的DLL尋找順序將"尋找應用程式所在目錄"修改為lpFileName指定的目錄。
3.2 調用SetDllDirectory函數。
注意:SetDllDirectory函數在Windows XP SP1開始支援的。
函數SetDllDirectory在調用參數lpPathName是一個路徑時,可支援修改DLL搜尋路徑。修改之後的搜尋順序如下:
a. 應用程式所在目錄;
b. 函數SetDllDirectory參數lpPathName給定的目錄;
c. 系統目錄。GetSystemDirectory返回的目錄,通常是系統硬碟\Windows\System32;
d. 16位系統目錄。該項只是為了向前相容的處理,可以不考慮;
e. Windows目錄。GetWindowsDirectory返回的目錄,通常是系統硬碟\Windows;
f. 環境變數PATH中所有目錄。
如果lpPathName參數為空白字串,這樣就會把目前的目錄從DLL搜尋路徑中去掉。
如果用NULL參數調用SetDllDirectory函數,可以恢複按照系統註冊表的"安全DLL尋找模式"來尋找DLL。
當然win8或者windows server 2012提供更多的可定製方法,這個可以參考MSDN上介紹。比如:SetDefaultDllDirectories、 AddDllDirectory、RemoveDllDirectory。
三、ProcessMonitor使用
ProcessMonitor可以從http://technet.microsoft.com/en-us/sysinternals/bb896645下載。
官網給出的介紹資料如下:
Process Monitor一款系統進程監視軟體,總體來說,Process Monitor相當於Filemon+Regmon,其中的Filemon專門用來監視系統 中的任何檔案操作過程,而Regmon用來監視註冊表的讀寫操作過程。 有了Process Monitor,使用者就可以對系統中的任何檔案和 註冊表操作同時進行監視和記錄,通過註冊表和檔案讀寫的變化, 對於協助診斷系統故障或是發現惡意軟體、病毒或木馬來說,非常 有用。
軟體下載之後,解壓就可以直接運行。Process Monitor預設會啟用針對真當前系統的"File System"、"Registry"、"Process"的所有操作的記錄,類似wireshark網卡抓包軟體,只是抓取的資訊不同。如果僅關心某個進程的事件,可以在工具列或者菜單中選擇Filter-Filiter,彈出所示對話方塊:
舉個例子,我們只關心進程名為"qwe.exe"的相關操作,可以做如下處理:從第一個下拉式清單方塊中選擇ProcessName,將進程名字填入輸入框,然後點擊"Add"按鈕,點擊 "OK"(如果已經開始監測,可以直接點Apply按鈕)。
其他關於Process Monitor的使用可以參考說明文檔,介紹整體比較詳細,這裡不做贅述。
四、驗證Windows下DLL載入順序是否正確
那麼我們可以考慮在win7下驗證下DLL載入順序,想法很簡單,隨便寫一個系統中不存在的DLL,用LoadLibray動態載入下看看,用Process Monitor記錄當前進程的操作記錄。
代碼如下:
1 #include <windows.h> 2 #include <iostream> 3 4 int main(int argc, char ** argv) 5 { 6 using std::cout; 7 using std::endl; 8 9 // 隨便設定一個不存在的dll名10 HMODULE hMod = LoadLibrary("123.dll");11 12 if (NULL != hMod)13 FreeLibrary(hMod);14 15 cout << "LoadLibrary Test" << endl;16 17 return 0;18 }
使用MinGW編譯之後,在命令列下運行該程式。Process Monitor輸出如下資訊(這裡僅截取關於123.dll載入的資訊):
可以看到這裡搜尋的路徑跟系統標準DLL搜尋路徑時一致的。我的環境變數Path從D:\software\Subversion\Apache2\bin開始到D:\software\tortoiseGit\bin結束。
五、總結
本文主要介紹了Windows下DLL尋找順序,理清這個尋找順序基本可以找到LoadLibrary返回NULL,提示"找不到指定模組"的原因。通常可以考慮一下幾點:
a. 待載入DLL檔案是正確的、完整的嗎? 是否有損壞? 載入全路徑是否正確?
b. 待載入DLL的依賴項是否存在?(Dependency Walker可以查看依賴性)這些依賴項在是否都在系統可以尋找到的目錄下?
另外,本文簡單介紹了使用Process Monitor剖析器運行對檔案系統、註冊表、進程/線程的操作,工具不錯,有待開發。
還有一個問題,需要特別注意的是目前的目錄,因為很多情況下目前的目錄會因為SetCurrentDirectory或者OpenFile的操作而改變,某些情況下會對載入DLL造成意外的麻煩。
註:著作權,請勿用於商業用途,轉載請註明原文地址。本人著作權所有,並保留一切權利。
Windows下DLL尋找順序