Windows核心編程學習筆記——-19章

來源:互聯網
上載者:User
第19章 DLL基礎

Windows API提供的所有函數都包含在DLL中。三個最重要的DLL:Kernel32.dll(管理記憶體、進程和線程)、Use32.dll(執行與使用者介面相關的任務)、GDI32.dll(繪製映像和顯示文字)。

19.1、DLL和進程的地址空間

第一層:DLL定位概要

在應用程式(或其他DLL)能調用一個DLL中函數前,必須將該DLL的檔案映像映射到調用進程的地址空間中。兩種方式:隱式載入時連結和顯示運行時連結。

映射後,當調用DLL中一個函數時,該函數會線上程棧中取得傳給他的參數,並用線程棧來存放它需要的局部變數。此外,DLL中函數建立的任何對象都為調用線程或進程所擁有。

19.2、縱觀全域

第二層:理論流程概要

若一個EXE需要從另一個DLL模組中匯入函數或變數,則需:

先構建DLL:

1)先建立一個標頭檔,包含在DLL中匯出的函數原型、結構及符號。為構建該DLL,DLL的所有源檔案需包含這個標頭檔。構建EXE時需同一個標頭檔。

2)建立源檔案來實現DLL模組中匯出的函數和變數。

3)構建DLL模組時,編譯器會對每個源檔案處理並產生一個.obj模組。

4)當所有.obj模組都建立完後,連結器將所有.obj模組內容合并,產生一個單獨的DLL。

5)若連結器檢測到DLL源檔案輸出了至少一個函數或變數,則連結器還會產生一個.lib檔案。它只是列出所有被匯出的函數或變數的符號名。

再構建EXE(可執行模組):

1)所有引用了匯出的函數、變數、資料結構或符號的源檔案中,必須包含DLL對應的標頭檔。

2)構建EXE時,編譯器會對每個源檔案處理並產生一個.obj模組。

3)編譯完後,連結器會將所有.obj模組內容合并產生一個exe。該exe(可執行模組)包含一個匯入段,其中列出了所有它需要的DLL模組,以及它從每個DLL模組中引用的符號。執行exe,OS的載入程式會執行5)。

4)載入程式先為新的進程建立一個虛擬位址空間,並將可執行模組映射到新進程的地址空間中。載入程式接著解析可執行模組的匯入段。對匯入段中列出的每個DLL,載入程式會在使用者系統中對該DLL模組進行定位,並將該DLL映射到進程的地址空間中。由於DLL模組可從其他DLL模組中匯入函數和變數,因此DLL模組可能有自己的匯入段並需將它所有的DLL模組映射到進程的地址空間中。

載入程式將EXE和所有DLL映射到進程的地址空間後,進程的主線程可以開始執行。

第三層:實踐詳細流程

19.2.1、構建DLL模組

一個DLL可匯出變數、函數或C++類。應避免匯出變數。僅當匯出的C++類的模組使用的編譯器與匯入的C++類的模組使用的編譯器由同一廠商提供時,才可匯出C++類。

巧妙之處(代碼P515):DLL標頭檔中

#ifdef  MYLIBAPI

#else

#define MYLIBAPI  extern  “C”  _declspec(dllimport)

#endif

//匯出的函數或變數

MYLIBAPI  int Add(int nLeft, int nRight);

DLL源檔案中

#define  MYLIBAPI  extern  “C”  _declspec(dllexport)   //必須在dll標頭檔之前

#include “dll標頭檔”

EXE源檔案中

#include “dll標頭檔”       //不能定義MYLIBAPI宏

如此在DLL源檔案中MYLIBAPI被定義為匯出,EXE源檔案中MYLIBAPI被定義為匯入。

_declspec(dllexport):源檔案中不必在被匯出變數和函數前加此修飾。因編譯器在解析標頭檔時會記住應匯出哪些變數和函數。

_declspec(dllimport):DLL的標頭檔中加在匯出的函數、變數或C++類前。非必需,但能略微提高效率。EXE的源檔案中,編譯器看到此符號,會知道該從DLL模組中匯入該符號。

extern “C”:在編寫C++代碼時才使用(C不該使用)。因C++編譯器通常會對函數名和變數名進行改編,連結時會出錯。此修飾符是告訴編譯器不要對變數名或函數進行改編。

_stdcall:即使是C,當用此約定時,Microsoft的編譯器會對函數名進行改編。具體方法是:給函數名添加底線首碼和一個特殊的尾碼。該尾碼由一個@符號跟作為參數傳給函數的位元組數組成。如:_declspec(dllexport)
LONG _stdcall MyFunc(int a, int b);匯出為_MyFunc@8。所以要防止改編。兩種方式:建立一個.def檔案,並在.def檔案中包含一下類似下面段:

EXPORTS

         MyFunc

第二種方法(建議不用)是匯出未經改編的函數名。在DLL的源檔案中加入:

#pragma comment(linker, “/export:MyFunc=_MyFunc@8”)

.def檔案格式:

LIBRARY  XX(dll名稱這個並不是必須的,但必須確保跟產生的dll名稱一樣)

EXPORTS

[函數名] @ [函數序號] 

匯出類:注意匯出類和使用匯出類同匯出函數和使用匯出函數類似,但在匯出類中不可使用extern “C”符號。

匯出段:當Microsoft C/C++編譯器看到__declspec(dllexport)修飾的變數、函數或C++類時,會在產生的.obj檔案中嵌入一些額外的資訊。當連結器在連結DLL的所有.obj檔案時,會解析這些資訊。連結器會在產生的DLL檔案中嵌入一個匯出符號表。這個匯出段列出了匯出的變數、函數或類的符號名。還會儲存相對虛擬位址,表示每個符號可在DLL模組的何處找到。(DumpBin.exe工具加入-exports可查看一個DLL的匯出段)

匯入段:當連結器看到__declspec(dllimport)修飾的匯入符號時,會在產生的可執行模組中嵌入一個特殊的段,它的名字叫匯入段。匯入段列出了該模組所需的DLL模組,以及它從每個DLL模組中引用的符號。(DumpBin.exe工具加入-imports可查看一個DLL的匯出段)

/***********************************Moudle: MyLib.h***********************************/#ifdef MYLIBAPI//MYLIBAPI should be defined in all of the DLL's source code //moudles before this header file is included.//All functions/variables are being exported.#else//This header file is included by an EXE source code moudle///Indicate that all functions/variables are being imported.#define MYLIBAPI extern "C" _declspec(dllimport)#endif////////////////////////////////////////////////Define any data structures and symbols here.////////////////////////////////////////////////Define exported variables here.(Note:Avoid exporting variables.)MYLIBAPI int g_nResult;///////////////////////////////////////////////Define exported function prototypes here.MYLIBAPI int Add(int nLeft, int nRight);////////////////////////End of File////////////////////////****************************************Moudle:MyLibFile1.cpp****************************************/#include <windows.h>#define MYLIBAPI extern "C" _declspec(dllexport)#include "MyLib.h"/////////////////////////////////////////int g_nResult;int Add(int nLeft, int nRight){g_nResult = nLeft + nRight;return g_nResult;}///////////////////End of File/////////////

19.2.2、構建可執行模組

1)包含dll的匯出標頭檔:#include <>;注意不要定義MYLIBAPI宏。

2)包含lib檔案:#pragma comment(lib, “”);為了讓連結器確定代碼中的匯入符號來自哪個DLL。

3)直接使用匯出的變數、函數或C++類。

19.2.3、運行可執行模組

         OS的載入程式先為進程建立虛擬位址空間,然後將EXE映射到地址空間中,之後載入程式回檢查EXE的匯入段,對所需DLL進行定位並將它們映射到進程的地址空間中。

         因匯入段只包含DLL名稱,不包含DLL路徑,因此載入程式必須搜尋DLL,順序為:

1) 
包含可執行檔的目錄

2) 
Windows的系統目錄,可通過GetSystemDirectory得到

3) 
16位的系統目錄,即Windows目錄的System子目錄

4) 
Windows目錄,可通過GetWindowsDirectory得到

5) 
進程的目前的目錄

6) 
PATH環境變數中列出的目錄。

比如現在建立好了一個DLL匯出了CMyClass類,客戶也能正常使用這個DLL,假設CMyClass對象的大小為30位元組。如果我們需要修改DLL 中的CMyClass類,讓它有相同的函數和成員變數,但是給增加了一個私人的成員變數int類型,現在CMyClass對象的大小就是34位元組了。當直接把這 個新的DLL給客戶使用替換掉原來30位元組大小的DLL,客戶應用程式期望的是30位元組大小的對象,而現在卻變成了一個34位元組大小的對象,糟糕,客戶程式出錯 了。

類似的問題,如果不是匯出CMyClass類,而在匯出的函數中使用了CMyClass,改變對象的大小仍然會有問題的。這個時候修改這個問題的唯一辦法就是替 換客戶程式中的CMyClass的標頭檔,全部重新編譯整個應用程式,讓客戶程式使用大小為34位元組的對象。

這就是一個嚴重的問題,有的時候如果沒有客戶程式的原始碼,那麼我們就不能使用這個新的DLL了。

具體用到時再baidu

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.