DLL系列5.延遲載入DLL

基本概念

  爲了讓DLL更易於使用, Microsoft Visual C++提供了一個很棒的特性,即延遲載入DLL。一個延遲載入的DLL是隱式鏈接的,系統一開始不會將該DLL載入,只有當我們的代碼試圖去引用DLL中包含的一個符號時,系統纔會實際載入該DLL。延遲載入DLL在下列情況下非常有用。

  • 如果應用程序使用了多個DLL,那麼它的初始化可能會比較慢,因爲加載程序要將所有必需的DLL映射到進程的地址空間中。緩解這個問題的一種方法就是講DLL的載入過程延伸到進程的執行過程中。延遲載入DLL可以讓我們很容易地實現這一點。
  • 如果我們在代碼中調用一個新的函數,然後又試圖在一個不提供該函數的老版本的操作系統中運行該應用程序,那麼加載程序會報告一個錯誤並且不允許應用程序運行。我們需要一種方法讓應用程序執行,如果(在運行的時候)發現應用程序正在老的操作系統下運行,那麼就不調用這個不存在的函數。延遲載入DLL同樣可以讓我們很容易地解決這個問題。

但是它仍然存在一些侷限性,具體如下;

  • 一個導出了字段(全局變量)的DLL無法延遲載入的。
  • Kernel32.dll模塊是無法延遲載入的,這是因爲必須載入該模塊才能調用LoadLibrary和GetProcAddres。
  • 不應該在DllMain入口函數中調用一個延遲載入的函數,因爲這樣可能導致程序崩潰。

示例演示

  首先,創建一個DLL,再創建一個可執行文件,這些和以往沒有任何不同。但在鏈接可執行文件的時候,我們必須修改一些鏈接器開關。下面是需要增加的兩個鏈接器開關:

/Lib:DelayImp.lib
/DelayLoad:MyDll.dll

  這兩個鏈接器開關是不能在源代碼中通過#pragma comment(linker, " ")來設置的。我們需要在項目屬性中設置這兩個鏈接器開關。“Delay Loaded DLLs“選項是通過Configuration Properties/Linker/Input屬性頁來設置的,如下圖所示。
在這裏插入圖片描述
  “Delay Loaded DLL“選項是通過Configuration Properties/Linker/Advanced屬性頁來設置的,如下圖所示。
在這裏插入圖片描述

/Lib開關告訴鏈接器要將指定的函數__delayLoadHelper2嵌入到我們的可執行文件中。第二個開關告訴鏈接器下列事項。

  • 將DLL模塊從可執行模塊的導入段中去除,這樣當進程初始化的時候,操作系統的加載程序就不會隱式地載入該DLL。
  • 在可執行模塊中嵌入一個新的延遲載入段(即Delay Import section,成爲.didata)來表示要從該DLL中導入哪些函數。
  • 通過讓對延遲載入函數的調用跳轉到__delayLoadHelper2函數,來完成對延遲載入函數的解析。

應用程序運行的時候,對延遲載入函數的調用實際上會調用__delayLoadHelper2函數。這個函數會引用那個特殊的延遲載入段,並會先後調用LoadLibrary和GetProcAddress。一旦得到了對應的延遲載入函數的地址,__delayLoadHelper2會修復對該函數的調用,這樣今後的調用將直接調用延遲載入函數。注意,同一個DLL中的其他函數仍然必須在第一次被調用的時候修復。另外請注意,我們可以多次指定/DelayLoad鏈接器開關——每一個開關對應一個我們想要延遲載入的DLL。

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