1、寫做初衷 ios
在Windows下單個DLL可能存在多個不一樣的版本,若不特別指定DLL的絕對路徑或使用其餘手段指定,在應用程序加載DLL時可能會查找到錯誤的版本,進而引出各類莫名其妙的問題。本文主要考慮如下兩個方面:windows
a. 參考MSDN,給出Windows下DLL查找順序安全
b. 簡單使用ProcessMonitor來驗證DLL查找順序app
2、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查找路徑基礎spa
應用程序能夠經過如下方式控制一個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。
3、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的使用能夠參考幫助文檔,介紹總體比較詳細,這裏不作贅述。
4、驗證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結束。
5、總結
本文主要介紹了Windows下DLL查找順序,理清這個查找順序基本能夠找到LoadLibrary返回NULL,提示"找不到指定模塊"的緣由。一般能夠考慮一下幾點:
a. 待加載DLL文件是正確的、完整的嗎? 是否有損壞? 加載全路徑是否正確?
b. 待加載DLL的依賴項是否存在?(Dependency Walker能夠查看依賴性)這些依賴項在是否都在系統能夠查找到的目錄下?
另外,本文簡單介紹了使用Process Monitor分析程序運行對文件系統、註冊表、進程/線程的操做,工具不錯,有待開發。
還有一個問題,須要特別注意的是當前目錄,由於不少狀況下當前目錄會由於SetCurrentDirectory或者OpenFile的操做而改變,某些狀況下會對加載DLL形成意外的麻煩。
注:版權全部,請勿用於商業用途,轉載請註明原文地址。本人保留全部權利