DLL的動态連結有兩種方法。一種是加載時動态連結(Load_time dynamic linking)。Windows搜尋要裝入的DLL時,按以下順序:應用程式所在目錄→目前目錄→Windows SYSTEM目錄→Windows目錄→PATH環境變量指定的路徑。
前天看到這幾句,突然設計出一道自認絕妙的筆試題:
"如果采用加載時動态連結的方式,Windows搜尋要裝入的DLL采用怎樣的順序?"
這個是基礎題,估計你很容易答出(答案就是上面的)。呵呵,我還有後着呢:
"你是如何證明Windows搜尋要裝入的DLL遵循這樣的順序呢,說出你的證明步驟"
你可以思考一下。下面是我設想的一個方案,但是自己測試了一下。結果卻令人吃驚。
我的證明步驟是這樣的:
1. 首先建立一個MFC 正常DLL工程OutputModulePath,在裡面添加一個API函數,功能就是列印DLL所在路徑,然後将其導出,代碼如下:
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程式所在的路徑,具體可以通過一下代碼擷取目前目錄:
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環境變量指定的路徑。
這樣的結果和書上的理論不符。難道我的測試方案有什麼不對嗎?
到論壇一問,得到一個答案是微軟上的一篇文章:
<a href="http://msdn.microsoft.com/en-us/library/ms682586%28VS.85%29.aspx">Dynamic-Link Library Search Order</a>
現在把它翻譯一下:
動态連結庫的搜尋順序
标準的搜尋順序
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搜尋模式禁用,搜尋順序如下:
2. 目前目錄
3. Windows SYSTEM目錄。通過調用GetSystemDirectory函數可以擷取這個目錄的路徑。
4. 16位系統的目錄。并沒有函數可以擷取這個目錄的路徑,但是它會被查找。
5. Windows目錄。通過調用GetWindowsDirectory函數可以擷取這個目錄的路徑。
預備的搜尋順序
由系統指定的标準搜尋順序可以通過調用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函數所要加載的可執行子產品的所在目錄。
1. lpFileName參數值所指定的目錄
假如lpPathName參數指定了一個路徑,SetDllDirectory函數支援一個預備的搜尋順序。這個預備的搜尋順序如下:
2. lpPathName參數指定的目錄
如果lpPathName參數為一個空字元串,目前目錄将會從搜尋順序中删除。
SetDllDirectory 有效地禁用安全DLL搜尋模式,而在搜尋指定的目錄路徑。要恢複安全 DLL搜尋模式的SafeDllSearchMode系統資料庫值的基礎和恢複目前目錄到搜尋順序,調用 lpPathName的參數值為NULL的SetDllDirectory函數。
看完這篇文章,我大緻知道了我的測試為何會出現那個結果,因為我的作業系統環境是Win XP + sp3。
參考文獻: