Windows 網路編程:DLL編程

尋夢新聞LINE@每日推播熱門推薦文章,趣聞不漏接❤️

加入LINE好友

DLL(Dynamic Link Library,動態鏈接庫)是一個可以被其他應用程序調用的程序模塊,其中封裝了可以被調用的資源和函數。動態鏈接庫的擴展名一般是.DLL,不過有時也可能是其他的。DLL文件也屬於可執行文件,只不過它是依附於EXE文件來被執行的。一個DLL文件可以被多個EXE文件加載。

一、什麼是DLL

下有非常多的DLL文件,有的是操作系統的DLL文件,有的是應用程序的DLL文件。DLL文件有什麼好處呢?DLL是動態鏈接庫,相對應的有靜態鏈接庫。動態鏈接庫是在EXE文件運行時被加載執行的,而靜態鏈接庫是在OBJ文件進行連接時同時被保存到程序中。動態鏈接庫可以減少可執行文件的體積,在需要的時候進入內存等很多好處。

二、編寫一個簡單的DLL程序

我們編寫一個簡單的DLL程序,並在DLL程序中添加一個導出函數。所謂導出函數,就是DLL提供給外部EXE或其他類型的可執行文件調用的函數,當然DLL本身也可以自己進行調用。我們啟動VC6來編寫一個DLL程序。

啟動VC6程序,單擊菜單「文件」->「新建」命令,在「Projects」選項卡中的左邊選擇「Win32 Dynamic-Link Library」,在「Project name:」文本框中填寫「FirstDll」,如圖1所示。

Windows 網絡編程:DLL編程

圖1 新建DLL工程

單擊「OK」按鈕,出現「Win32 Dynamic-Link Library– Step 1 of 1」界面,選擇「A simple DLL Project」項,單擊「Finish」按鈕。在然後出現的對話框中直接單擊「OK」按鈕即可。在創建好該工程後,VC6自動生成如下代碼:

Windows 網絡編程:DLL編程

DLL程序的入口函數是DllMain(),該函數有以下3個參數。

(1)hModule:DLL模塊的句柄。

(2)ul_reason_for_call:DllMain函數被調用的原因。

該參數的取值有4種,分別是DLL_PROCESS_ATTACH(當DLL被某進程加載時DllMain被調用)、DLL_PROCESS_DETACH (當DLL被某進程卸載時DllMain被調用)、DLL_THREAD_ATTACH (進程中有線程被創建時DllMain被調用)和DLL_THREAD_DETACH(進程中有線程結束時DllMain被調用)。

(3)lpReserved:保留項,也就是的保留參數。所謂保留參數不是不使用的參數,是不想讓我們知道作用的參數。

Windows 網絡編程:DLL編程

WINAPI也是一個宏,該宏表示一種函數調用約定。

Windows 網絡編程:DLL編程

這樣寫就可以達到根據不同的調用原因執行不同的代碼。我們添加一個簡單的導出函數。

該函數的定義如下:

Windows 網絡編程:DLL編程

運行函數後彈出一個對話框,顯示一個字符串,並顯示其所在的進程的進程名。我們分別在DLL_PROCESS_ATTACH和DLL_PROCESS_DETACH下加一個對該函數的調用,代碼如下:

Windows 網絡編程:DLL編程

編譯該代碼,會生成兩個對我們有用的文件,一個是「FirstDll.dll」,另外一個是「FirstDll.lib」,前面的是DLL文件,後面的是庫文件,該庫文件中包含著導出函數的相關信息。

三、對DLL程序的調用方法一

DLL程序就寫到這里,接下來寫個調用該DLL的程序測試一下吧。我們需要對該DLL進行兩方面的測試,一個是看當加載和卸載該DLL時,是否會彈出對話框;另外一個是調用這個DLL的導出函數看是否能成功。

在工作區的「Workspace ‘FirstDll’:1project」上單擊右鍵,在彈出的菜單中選擇「Add New Project to Workspace …」,如圖2所示。

Windows 網絡編程:DLL編程

圖2 添加測試工程

彈出「New」對話框,在「Projects」選項卡的左面選擇「Win32 Console Application」,在「Project name:」文本框中填寫:「DllTest1」。單擊「OK」按鈕,在出現的對話框中選擇「A Simple Application」,該處和前面內容類似。在左面的工作區打開我們新建的工程,在DllTest1.cpp中添加代碼:

Windows 網絡編程:DLL編程

對該代碼進行編譯連接,這時並沒有生成我們想要的可執行文件,在編譯連接過程中出錯了。

這個錯誤確切地說是連接錯誤,原因是找不到DLL的Lib文件,我們把「FristDll.lib」文件復制到DllTest1這個工程的目錄下,再次編譯連接,這次通過了。那麼我們就來運行這個DllTest1的程序。不過很可惜,運行出錯,錯誤提示如圖3所示。

Windows 網絡編程:DLL編程

圖3運行DllTest1時的錯誤信息

這個錯誤的原因是DllTest1找不到FirstDll.dll這個文件,把開始編譯好的這個文件也復制到DllTest1的工程目錄下,再次運行,這次一切正常,並且看到彈出3次對話框,說明DLL程序已經能夠正常運行了。

四、對DLL程序的調用方法二

第一種方法是屬於靜態調用,現在的第二種方法屬於動態調用。靜態調用就是在編譯測試程序DllTest1時,FirstDll.dll的信息就已經寫入了DllTest1的程序中了。對於動態加載的話,就不是編譯時完成了,而是在運行時完成,那麼FirstDll.dll的信息也不會寫入測試程序中了。現在來寫一個DllTest2程序,該程序的建立方法與DllTest1的建立方法相同。DllTest2的代碼如下:

Windows 網絡編程:DLL編程

我們對代碼進行編譯連接,正常編譯通過。那麼就運行該程序,提示「FirstDll.dll文件不存在」,這說明DllTest2程序沒有找到FirstDll.dll文件。把FirstDll.dll文件拷貝到DllTest2的工程目錄下,再次運行該程序,這次運行成功了,並且應該彈出的3個對話框也都正常彈出了。我們的測試也是成功的。

DLL的動態加載是非常有用的,在DllTest1中,如果無法找到DLL文件,系統會直接報錯而退出,而在DllTest2中,如果無法找到DLL文件,程序會給出一個錯誤提示,並且可以繼續運行,而不影響其他代碼的運行。除此而外,如果知道一些API函數,而這些API函數是未文檔化的函數,或者是沒有提供頭文件的API函數,要怎麼辦呢?比如在前面的內容中用到的函數OpenThread(),該函數在VC6默認的PSDK中是沒有提供定義的,在新的PSDK中才有,那如何使用呢?那就需要用到LoadLibrary()和GetProcAddress()這兩個API函數了。看一下LoadLibrary()和GetProcAddress()這兩個函數的定義:

Windows 網絡編程:DLL編程

該函數只有一個參數,就是要加載的DLL文件的文件名。

Windows 網絡編程:DLL編程

該函數有兩個參數,hModule是模塊的句柄,lpProcName指定要獲取函數地址的函數名稱。

在關於DLL話題的最後,介紹一下如何查看DLL程序的導出函數。在這里介紹兩個工具,一個工具是VC6自帶的工具「Depends」,另一個工具是用來查看PE結構的工具「PEID」。

首先用「Depends」來查看DLL的導出函數,如何找到這個工具呢?在VC6的安裝菜單下就可以找到該工具。方法如下:單擊菜單「開始」->「程序」->「Microsoft Visual Studio6.0」->「Microsoft Visual Studio 6.0 Tools」->「Depends」命令,打開該程序,再單擊菜單「File」->「Open…」命令,在「打開」對話框中找到我們寫的FirstDll.dll文件並打開,如圖4所示。

Windows 網絡編程:DLL編程

圖4 Depends界面

右下角這個區域範圍就是我們的導出函數部分,這里可以看到導出函數「MsgBox」。

除了這個工具以外,再介紹一個工具——PEID。該工具是用來進行查殼的工具,我們把FirstDll.dll文件拖曳到PEID界面上,PEID會自動解析出該DLL文件的PE結構,該界面如圖5所示。

Windows 網絡編程:DLL編程

圖5 PEID界面

可以看到,PEID最下方的編輯框處顯示出DLL是由VC6開發的,而且版本是Debug版本。單擊「子系統」右邊的「」按鈕,會顯示PE結構的詳細信息,在詳細信息的下半部分有一個「目錄信息」,在「目錄信息」中第一個就是我們想要查看的導出函數的內容,單擊「導出表」右面的「大於號按鈕」按鈕,出現如圖6所示的界面。

Windows 網絡編程:DLL編程

圖6 導出表信息

因為DLL中只有一個導出函數MsgBox(),那麼該導出表中就只有一個導出項。

對於DLL的編程就介紹到這里了,希望大家可以自己動手完成這個簡單的DLL文件。

About 尋夢園
尋夢園是台灣最大的聊天室及交友社群網站。 致力於發展能夠讓會員們彼此互動、盡情分享自我的平台。 擁有數百間不同的聊天室 ,讓您隨時隨地都能找到志同道合的好友!