關于DLL搜索路徑順序的一個問題 2016-09-28 00:00:00 廣州睿豐德信息科技有限公司 閱讀 睿豐德科技 專注RFID識別技術和條碼識別技術與管理軟件的集成項目。質量追溯系統、MES系統、金蝶與條碼系統對接、用友與條碼系統對接 DLL的動態鏈接有兩種方法。一種是加載時動態鏈接(Load_time dynamic linking)。Windows搜索要裝入的DLL時,按以下順序:應用程序所在目錄→當前目錄→Windows SYSTEM目錄→Windows目錄→PATH環境變量指定的路徑。 前天看到這幾句,突然設計出一道自認絕妙的筆試題:"如果采用加載時動態鏈接的方式,Windows搜索要裝入的DLL采用怎樣的順序?"這個是基礎題,估計你很容易答出(答案就是上面的)。呵呵,我還有后著呢:"你是如何證明Windows搜索要裝入的DLL遵循這樣的順序呢,說出你的證明步驟" 你可以思考一下。下面是我設想的一個方案,但是自己測試了一下。結果卻令人吃驚。 我的證明步驟是這樣的:1. 首先新建一個MFC 常規DLL工程OutputModulePath,在里面添加一個API函數,功能就是打印DLL所在路徑,然后將其導出,代碼如下: [cpp] view plaincopy void PaintModulePath() { extern COutputModulePathApp theApp; TCHAR szModulePath[MAX_PATH]; :: GetModuleFileName(theApp.m_hInstance,szModulePath, MAX_PATH); AfxMessageBox(szModulePath); } 2. 編譯這個DLL工程,然后將生成的DLL文件OutputModulePath.dll復制到當前目錄、Windows SYSTEM目錄→Windows目錄→PATH環境變量指定的路徑。這里要說明一下:如果調試運行的話,當前目錄應該是當前目錄的工程文件所在的路徑,如果單擊直接運行的話,當前目錄應該是exe程序所在的路徑,具體可以通過一下代碼獲取當前目錄: [cpp] view plaincopy TCHAR szBuf[MAX_PATH]; ::ZeroMemory(szBuf,MAX_PATH); if (::GetCurrentDirectory(MAX_PATH,szBuf) > 0) { //獲取進程目錄成功。 AfxMessageBox(szBuf); } PATH環境變量指定的路徑這里可能有多個路徑,這里只須將OutputModulePath.dll拷貝到其中之一就行,例如我拷貝到的是:D:/Program Files/Lua/5.1。 3. 建一個測試工程(對話框程序或單文檔程序都可以),在里面調用PaintModulePath函數。我的測試步驟及結果是這樣的,按F5調試運行測試程序,首先程序輸出的是exe程序所在的路徑,然后我將exe程序路徑下的dll文件刪除,但接著輸出的是C:/WINDOWS/system/OutputModulePath.dll,我將C:/WINDOWS/system/OutputModulePath.dll刪除,接著輸出的是C:/WINDOWS/ OutputModulePath.dll,將C:/WINDOWS/ OutputModulePath.dll刪除,輸出的才是當前目錄下的文件路徑,最后輸出的是PATH環境變量指定的路徑。 這樣的結果和書上的理論不符。難道我的測試方案有什么不對嗎? 到論壇一問,得到一個答案是微軟上的一篇文章: Dynamic-Link Library Search Order 現在把它翻譯一下: 動態鏈接庫的搜索順序 一個系統可以包含多個版本的同一個動態鏈接庫(dll)。應用程序能夠通過使用動態鏈接庫重定向或清單文件 指定要加載的DLL的全路徑。如果沒有使用這些方法,這篇文章將講述在裝入DLL時DLL的搜索順序。 標準的搜索順序 DLL的搜索順序取決于是否安全DLL搜索模式是啟用或禁用。安全DLL搜索模式在默認狀態下是啟用的。通過創HKLM/System/CurrentControlSet/Control/Session Manager/SafeDllSearchMode注冊表項并將它的值設為0可以關閉這個屬性。通過調用SetDllDirectory函數可以有效禁用安全DLL搜索模式而在這篇文章中這個函數指定的路徑將加入搜索范圍并改變搜索順序。 Windows XP and Windows 2000 with SP4: 安全DLL搜索模式在默認狀態下是禁用的。通過創HKLM/System/CurrentControlSet/Control/Session Manager/SafeDllSearchMode注冊表項并將它的值設為1可以啟用這個屬性。安全DLL搜索模式在默認狀態下得到啟用始于帶sp2的Windows XP。 Windows 2000: 并不支持安全DLL搜索模式。在這種情況下DLL的搜索順序和安全DLL搜索模式被禁用的情況下的順序是一樣的。安全DLL搜索模式得到支持始于帶sp4的Windows 2000。 假如安全DLL搜索模式啟用,搜索順序如下:1. 應用程序所在的路徑2. Windows SYSTEM目錄。通過調用GetSystemDirectory函數可以獲取這個目錄的路徑。3. 16位系統的目錄。并沒有函數可以獲取這個目錄的路徑,但是它會被查找。4. Windows目錄。通過調用GetWindowsDirectory函數可以獲取這個目錄的路徑。5. 當前目錄6. PATH環境變量指定的路徑。請注意,這并不包括每個應用程序的應用程序路徑注冊表項中指定。在應用程序路徑注冊表項的鍵值并不作為DLL的搜索路徑。 假如安全DLL搜索模式禁用,搜索順序如下:1. 應用程序所在的路徑2. 當前目錄3. Windows SYSTEM目錄。通過調用GetSystemDirectory函數可以獲取這個目錄的路徑。4. 16位系統的目錄。并沒有函數可以獲取這個目錄的路徑,但是它會被查找。5. Windows目錄。通過調用GetWindowsDirectory函數可以獲取這個目錄的路徑。6. PATH環境變量指定的路徑。請注意,這并不包括每個應用程序的應用程序路徑注冊表項中指定。在應用程序路徑注冊表項的鍵值并不作為DLL的搜索路徑。 預備的搜索順序 由系統指定的標準搜索順序可以通過調用LoadLibraryEx函數加上LOAD_WITH_ALTERED_SEARCH_PATH參數值得到改變。標準搜索順序也可以通過調用SetDllDirectory函數得到改變。 Windows XP: 通過調用SetDllDirectory函數來改變標準搜索順序并不支持直到Windows XP sp1出現。Windows 2000: 不支持通過調用SetDllDirectory函數來改變標準搜索順序。 如果您指定一個備用的搜索順序,程序將按備用的搜索順序進行搜索,直到所有相關的可執行模塊被找到。系統啟動后,DLL初始化例程處理,該系統將恢復為標準的搜索順序。 LoadLibraryEx函數通過指定LOAD_WITH_ALTERED_SEARCH_PATH屬性和lpFileName參數指定一個絕對路徑支持一個預備的搜索順序。 請注意:標準搜索順序和通過調用指定LOAD_WITH_ALTERED_SEARCH_PATH屬性的LoadLibraryEx函數來設置的預備搜索順序只是有一點不同:標準搜索順序開始于搜索1. 應用程序所在的路徑而預備搜索順序開始于LoadLibraryEx函數所要加載的可執行模塊的所在目錄。 假如安全DLL搜索模式啟用,搜索順序如下: 1. lpFileName參數值所指定的目錄2. Windows SYSTEM目錄。通過調用GetSystemDirectory函數可以獲取這個目錄的路徑。3. 16位系統的目錄。并沒有函數可以獲取這個目錄的路徑,但是它會被查找。4. Windows目錄。通過調用GetWindowsDirectory函數可以獲取這個目錄的路徑。5. 當前目錄6. PATH環境變量指定的路徑。請注意,這并不包括每個應用程序的應用程序路徑注冊表項中指定。在應用程序路徑注冊表項的鍵值并不作為DLL的搜索路徑。 假如安全DLL搜索模式禁用,搜索順序如下: 1. lpFileName參數值所指定的目錄2. 當前目錄3. Windows SYSTEM目錄。通過調用GetSystemDirectory函數可以獲取這個目錄的路徑。4. 16位系統的目錄。并沒有函數可以獲取這個目錄的路徑,但是它會被查找。5. Windows目錄。通過調用GetWindowsDirectory函數可以獲取這個目錄的路徑。6. PATH環境變量指定的路徑。請注意,這并不包括每個應用程序的應用程序路徑注冊表項中指定。在應用程序路徑注冊表項的鍵值并不作為DLL的搜索路徑。 假如lpPathName參數指定了一個路徑,SetDllDirectory函數支持一個預備的搜索順序。這個預備的搜索順序如下: 1. 應用程序所在的路徑2. lpPathName參數指定的目錄3. Windows SYSTEM目錄。通過調用GetSystemDirectory函數可以獲取這個目錄的路徑。4. 16位系統的目錄。并沒有函數可以獲取這個目錄的路徑,但是它會被查找。5. Windows目錄。通過調用GetWindowsDirectory函數可以獲取這個目錄的路徑。6. PATH環境變量指定的路徑。請注意,這并不包括每個應用程序的應用程序路徑注冊表項中指定。在應用程序路徑注冊表項的鍵值并不作為DLL的搜索路徑。 如果lpPathName參數為一個空字符串,當前目錄將會從搜索順序中刪除。SetDllDirectory 有效地禁用安全DLL搜索模式,而在搜索指定的目錄路徑。要恢復安全 DLL搜索模式的SafeDllSearchMode注冊表值的基礎和恢復當前目錄到搜索順序,調用 lpPathName的參數值為NULL的SetDllDirectory函數。 看完這篇文章,我大致知道了我的測試為何會出現那個結果,因為我的操作系統環境是Win XP + sp3。 參考文獻: 1. MFC深入淺出RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成