1. 程式人生 > >dll動態連結庫檔案編寫

dll動態連結庫檔案編寫

1.動態連結庫(dll)概述

沒接觸dll之前覺得它很神祕,就像是一個黑盒子,既不能直接執行,也不能接收訊息。它們是一些獨立的檔案,其中包含能被可執行程式或其他dll呼叫來完成某項工作的函式,只有在其他模組呼叫dll中的函式時,dll才發揮作用。 
在實際程式設計中,我們可以把完成某項功能的函式放在一個動態連結庫裡,然後提供給其他程式呼叫。像Windows API中所有的函式都包含在dll中,如Kernel32.dll, User32.dll, GDI32.dll等。那麼dll究竟有什麼好處呢?

1.1 靜態庫和動態庫

  • 靜態庫:函式和資料被編譯進一個二進位制檔案(副檔名通常為.lib),在使用靜態庫的情況下,在編譯連結可執行檔案時,連結器從靜態庫中複製這些函式和資料,並把它們和應用程式的其他模組組合起來建立最終的可執行檔案(.exe)。當釋出產品時,只需要釋出這個可執行檔案,並不需要釋出被使用的靜態庫。
  • 動態庫:在使用動態庫時,往往提供兩個檔案:一個引入庫(.lib,非必須)和一個.dll檔案。這裡的引入庫和靜態庫檔案雖然副檔名都是.lib,但是有著本質上的區別,對於一個動態連結庫來說,其引入庫檔案包含該動態庫匯出的函式和變數的符號名,而.dll檔案包含該動態庫實際的函式和資料。

1.2 使用動態連結庫的好處

  1. 可以使用多種程式語言編寫:比如我們可以用VC++編寫dll,然後在VB編寫的程式中呼叫它。
  2. 增強產品功能:可以通過開發新的dll取代產品原有的dll,達到增強產品效能的目的。比如我們看到很多產品踢動了介面外掛功能,允許使用者動態地更換程式的介面,這就可以通過更換介面dll來實現。
  3. 提供二次開發的平臺
    :使用者可以單獨利用dll呼叫其中實現的功能,來完成其他應用,實現二次開發。
  4. 節省記憶體:如果多個應用程式使用同一個dll,該dll的頁面只需要存入記憶體一次,所有的應用程式都可以共享它的頁面,從而節省記憶體。

2. dll的建立

dll的建立主要有兩種方法:一是使用 __declspec(dllexport) 建立dll,二是使用模組定義(.def)檔案建立dll。

2.1 使用 __declspec(dllexport) 建立dll

首先在VS中的Visual C++中建立一個Win32 Project,取名為Dll1。在Application Type中選擇DLL,在Additional options中選擇Empty project,即建立一個空的動態連結庫工程。 
這裡寫圖片描述

 
然後為工程新增一個C++原始檔:Dll1.cpp,假設我要實現的是加法和減法運算,則程式碼如下:

__declspec(dllexport) int add(int a, int b){
    return a + b;
}

__declspec(dllexport) int subtract(int a, int b){
    return a - b;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

為了讓dll匯出函式,需要在每一個需要被匯出的函式前面加上識別符號:__declspec(dllexport)。 
利用Build命令生成Dll1動態連結庫,這時在Dll1/Debug目錄下就會生成.dll檔案和.lib檔案,這兩個檔案即為所需的動態連結庫的檔案。 
這裡寫圖片描述 
既然已經有了這個dll檔案,是不是就可以在其他程式中訪問該dll中的add和subtract函數了呢?必須注意的一點是:應用程式如果想要訪問某個dll中的函式,那麼這個函式必須是已經被匯出的函式。 
為了檢視一個dll中有哪些匯出函式,Visual Studio提供了一個命令列工具:Dumpbin。

2.2 使用Dumpbin命令確認dll的匯出函式

首先在命令列中進入到VS的安裝目錄下,執行一個名為VCVARS32.bat的批處理程式(對於VS2013來說,該bat檔案位於\VC\bin目錄下),該檔案的作用是用來建立VC++使用的環境資訊。(注意,當在命令列介面執行VCVARS32.bat檔案後,該檔案設定的環境資訊只在當前命令列視窗生效。) 
然後輸入dumpbin命令,即可列出該命令的使用方法: 
這裡寫圖片描述 
那麼想要檢視一個dll提供的匯出函式,在Dll1.dll檔案所在目錄下,在命令列中輸入下述命令:

dumpbin -exports Dll1.dll

這裡寫圖片描述

在上圖中可以看到我們匯出了兩個函式,但是匯出函式的名稱長得很奇怪,add匯出函式的名稱是“?[email protected]@[email protected]”,subtract匯出函式的名稱是“?[email protected]@[email protected]”。這是因為在編譯連結時,C++會按照自己的規則篡改函式的名稱,這一過程稱為“名字改編”。這會導致不同的編譯器、不同的語言下呼叫dll發生問題。因此我們希望動態連結庫檔案在編譯時,匯出函式的名稱不要發生變化。 
為了實現這一目的,可以再定義匯出函式時加上限定符:extern “C”,如

extern "C" __declspec(dllexport) int add(int a, int b){
//...
}
  • 1
  • 2
  • 3

但是這種方式只能解決C++和C語言之間相互呼叫時函式命名的問題。為了徹底解決這個問題,可以通過模組定義(.def)檔案實現。

2.3 使用模組定義(.def)檔案建立dll

使用def檔案建立dll的話就不再需要__declspec(dllexport),因此將程式碼寫成最原始的樣子:

int add(int a, int b){
    return a + b;
}

int subtract(int a, int b){
    return a - b;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

同時為工程建立一個字尾名為.def的檔案,並新增進工程,編輯其內容為:

LIBRARY Dll1

EXPORTS
add
subtract
  • 1
  • 2
  • 3
  • 4
  • 5

其中LIBRARY語句用於指定動態連結庫的名稱,該名稱與生成的動態連結庫名稱一定要匹配。EXPORTS語句用於表明dll將要匯出的函式,以及為這些匯出函式指定的符號名。 
將該模組定義檔案連結到工程中,方法為工程屬性頁面>連結器>輸入>模組定義檔案中寫入“Dll1.def”。 
這裡寫圖片描述 
然後重新Build Solution,並用dumpbin工具檢視現在dll匯出的函式,可以發現函式的名字改編問題得到了解決! 
這裡寫圖片描述

以上就是建立dll的兩種方法,個人比較提倡使用模組定義(.def)檔案建立dll,程式碼簡潔的同時還沒有名字改編的問題。 
接下來我們來看看如何使用建立好的dll。

3. dll的使用

dll的使用也有兩種方法,一是隱式連結的方式載入dll,二是顯示載入方式載入dll。

3.1 隱式連結方式載入dll

為了更好地展示dll的使用,我首先建立了一個基於MFC的對話方塊程式,然後為其新增兩個按鈕。 
這裡寫圖片描述 
將生成好的Dll1.dll和Dll1.lib複製到對話方塊程式所在的資料夾,然後在CXXXDlg.h中註冊動態連結庫的引入庫檔案。因為.lib檔案包含了Dll1.dll中匯出函式的符號名,相當於告訴對話方塊程式相關函式應該去dll中呼叫。

#pragma comment(lib,"Dll1.lib")
  • 1

然後在CXXXDlg.cpp中宣告外部函式:

_declspec(dllimport) int add(int a, int b);
_declspec(dllimport) int subtract(int a, int b);
  • 1
  • 2

這樣我們就可以使用這兩個函數了。為兩個按鈕新增事件響應程式,並新增如下程式碼:

void CXXXDlg::OnBtnAdd()
{
    // TODO: Add your control notification handler code here
    CString str;
    str.Format(_T("5 + 3 = %d"), add(5, 3));
    MessageBox(str);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

執行程式發現可以通過dll的匯出函式來實現加法功能了。說明dll可以使用。 
這裡寫圖片描述

3.2 顯示載入方式載入dll

另一種是通過LoadLiabrary函式顯示載入dll。程式碼如下。需要注意的是這時候我們不再需要註冊.lib檔案,也不需要宣告外部函式。只要在需要使用的地方呼叫dll檔案即可。

void CXXXDlg::OnBtnSubtract()
{
    // TODO: Add your control notification handler code here
    HINSTANCE hInst;
    hInst = LoadLibrary(L"Dll1.dll");
    typedef int(*SUBPROC)(int a, int b);
    SUBPROC Sub = (SUBPROC)GetProcAddress(hInst, "subtract");
    CString str;
    str.Format(_T("5-3=%d"), Sub(5, 3));
    FreeLibrary(hInst);       //LoadLibrary後要記得FreeLibrary
    MessageBox(str);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

這裡寫圖片描述

3.3 兩種載入方式對比

通過以上的例子,可以看到隱式連結和動態載入兩種載入dll的方式各有優點。

  • 隱式連結方式實現簡單,一開始就把dll載入進來,在需要呼叫的時候直接呼叫即可。但是如果程式要訪問十多個dll,如果都採用隱式連結方式載入他們的話,在該程式啟動時,這些dll都需要被載入到記憶體中,並對映到呼叫程序的地址空間,這樣將加大程式的啟動時間。而且一般來說,在程式執行過程中只是在某個條件滿足的情況下才需要訪問某個dll中的函式,如在上述例子中,我只有在點選按鈕時才需要訪問dll,其他情況下並不需要訪問。這樣如果所有dll都被載入到記憶體中,資源浪費是比較嚴重的。
  • 顯示載入的方法則可以解決上述問題,dll只有在需要用到的時候才會被載入到記憶體中。另外,其實採用隱式連結方式訪問dll時,在程式啟動時也是通過呼叫LoadLibrary函式載入該程序需要的動態連結庫的。

本文將建立一個簡單的動態連結庫,並編寫一個控制應用程式使用該動態連結庫,該動態連結庫為“JAVA呼叫動態連結庫DLL之JNative學習”中使用的DLL,只是專案及檔名稱不同。

建立動態連結庫專案:
1、開啟Microsoft Visual Studio 2010,選擇檔案->新建->專案。


2、在新建專案視窗中選擇其他語言->Visual C++->Win32。


3、選擇Win32 專案,設定名稱:simpleDLL,設定解決方案名:simpleDLL
4、單擊確定,在出現的Win32 應用程式嚮導的概述對話方塊中點選下一步。


5、在應用程式設定中,選擇應用程式型別下的DLL。


6、勾選附加選項下的空專案。
7、單擊完成建立專案。
向動態連結庫新增類:
1、新增新類標頭檔案。右鍵單擊simpleDLL專案,新增->新建項,選擇標頭檔案(.h),設定名稱為simpleDLL,單擊新增。



2、新增新類原始檔。右鍵單擊simpleDLL專案,新增->新建項,選擇C++ 檔案(.cpp),設定名稱為simpleDLL,單擊新增。



3、為新類新增內容。內容如下:

標頭檔案simpleDLL.h:

  1. //------------------ SimpleDLL.h ----------------
  2. #pragma once;
  3. //該巨集完成在dll專案內部使用__declspec(dllexport)匯出
  4. //在dll專案外部使用時,用__declspec(dllimport)匯入
  5. //巨集DLL_IMPLEMENT在SimpleDLL.cpp中定義
  6. #ifdef DLL_IMPLEMENT
  7. #define DLL_API __declspec(dllexport)
  8. #else
  9. #define DLL_API __declspec(dllimport)
  10. #endif
  11. DLL_API int add(int x, int y); //簡單方法
  12. DLL_API constwchar_t* getPlayUrl(constwchar_t* mgrIp, long mgrPort, long materialId);  
  13. DLL_API constchar* getUrl(constchar* mgrIp, long mgrPort, long materialId);  
原始檔simpleDLL.cpp:
  1. //------------------ SimpleDLL.cpp ----------------
  2. //注意此處的巨集定義需要寫在#include "SimpleDLL.h"之前
  3. //以完成在dll專案內部使用__declspec(dllexport)匯出
  4. //在dll專案外部使用時,用__declspec(dllimport)匯入
  5. #define DLL_IMPLEMENT
  6. #include "SimpleDLL.h"
  7. #include<Windows.h>
  8. #include <intrin.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11. int DLL_API add(int x, int y)  
  12. {  
  13.     return x+y;  
  14. }  
  15. DLL_API constwchar_t* getPlayUrl(constwchar_t* mgrIp, long mgrPort, long materialId)  
  16. {  
  17.     staticwchar_t url[260] = { 0 };  
  18.     wcscpy_s(url, L"http://中文");  
  19.     wcscat_s(url, mgrIp);  
  20.     wcscat_s(url, L":");  
  21.     wchar_t szPort[20] = { 0 };  
  22.     _ltow_s(mgrPort, szPort, 10);  
  23.     wcscat_s(url, szPort);  
  24.     return url;  
  25. }  
  26. DLL_API constchar* getUrl(constchar* mgrIp, long mgrPort, long materialId)  
  27. {  
  28.     staticchar url[260] = { 0 };  
  29.     strcpy_s(url, "http://中文");  
  30.     strcat_s(url, mgrIp);  
  31.     strcat_s(url, ":");  
  32.     char szPort[20] = { 0 };  
  33.     _ltoa_s(mgrPort, szPort, 10);  
  34.     strcat_s(url, szPort);  
  35.     return url;  
  36. }  

建立引用動態連結庫的應用程式:
1、在解決方案上單擊滑鼠右鍵->新增->新建專案。


2、在新增新專案中選擇其它語言->Visual C++->Win32。


3、選擇Win32 控制檯應用程式,設定名稱:simpleDLLTest。
4、單擊確定,在出現的Win32 應用程式嚮導的概述對話方塊中點選下一步。


5、在應用程式設定中,選擇應用程式型別下的控制檯應用程式。


6、單擊完成建立專案
在控制檯應用程式中使用類庫的功能:
1、為SimpleDLLTest.cpp新增內容。如下所示:

  1. // SimpleDLLTest.cpp : 定義控制檯應用程式的入口點。
  2. #include "stdafx.h"
  3. #include "../SimpleDLL/SimpleDLL.h" //新增標頭檔案引用
  4. #pragma comment(lib, "..\\..\\SimpleDLL\\Release\\SimpleDLL.lib") //新增lib檔案引用 
  5. #include <process.h>
  6. #include <locale.h>
  7. int _tmain(int argc, _TCHAR* argv[])  
  8. {  
  9.     setlocale(LC_ALL, "chs"); //配置地域化資訊為簡體中文,否則打印出來的中文是亂碼
  10.     wprintf(L"getPlayUrl: %s\r\n", getPlayUrl(L"127.0.0.1", 10087, 1));  
  11.     printf("getUrl: %s\r\n", getUrl("127.0.0.1", 10087, 1));  
  12.     system("pause");  
  13. 相關推薦

    dll動態連結檔案編寫

    1.動態連結庫(dll)概述 沒接觸dll之前覺得它很神祕,就像是一個黑盒子,既不能直接執行,也不能接收訊息。它們是一些獨立的檔案,其中包含能被可執行程式或其他dll呼叫來完成某項工作的函式,只有在其他模組呼叫dll中的函式時,dll才發揮作用。  在實際程式設計中,我

    怎樣用VB編寫.DLL動態連結檔案

    一、名詞解釋:DLL 的學名叫 動態連結庫二、它是幹什麼用的:搜尋一下有很完整詳細的解釋。這裡,我只引用過來當中的一句話“在Windows中,許多應用程式並不是一個完整的可執行檔案,它們被分割成一些相對獨立的動態連結庫,即DLL檔案,放置於系統中。當我們執行某一個程式時,相應

    .h標頭檔案 .lib檔案 .dll動態連結檔案關係

    .h標頭檔案是編譯時必須的,lib是連結時需要的,dll是執行時需要的。 附加依賴項的是.lib不是.dll,若生成了DLL,則肯定也生成 LIB檔案。如果要完成原始碼的編譯和連結,有標頭檔案和lib就夠了。如果也使動態連線的程式執行起來,有dll就夠了。在開發和除錯階段,當然最好都有。 .h .lib .

    VS程式設計,x86,x64,Any CPU在可執行檔案(EXE)或dll(動態連結)的區別:

    Any CPU和x86的可執行檔案(EXE)或dll(動態連結庫)在32位和64位下的區別: 1、x86平臺編譯出來的exe(可執行檔案)或dll(動態連結庫)都是32位的 2、x64平臺編譯出來的exe(可執行檔案)或dll(動態連結庫)對應的則是64位的。 3、Any

    C程式碼檔案如何生成Dll動態連結

    已經有c程式碼檔案,如何生成dll,並能然後c#呼叫裡面的函式: 1.在vs裡面新建一個c++的空專案,把c程式碼都載入進去,然後更改專案的屬性, 如下圖: 配置型別改為動態庫.dll。 2.新增.def檔案,並編輯檔案內容 LIBRARY EXPORTS test1 @

    使用JNI開啟底層裝置是報錯,需要看看.so動態連結檔案是否編譯到APK裡面

        這是因為動態連結庫沒有編譯到APK,所以要在build.gradle檔案裡配置  jniLibs.srcDirs = ['libs'] sourceSets { main { manifest.srcF

    關於原始檔,標頭檔案,靜態連結檔案動態連結檔案的的理解

    先從原始檔和標頭檔案的關係說起,由於是還是初學階段,只接觸了C++語言和windows平臺下的程式設計,所以只講這兩方面的東東, 標頭檔案的作用:對函式,變數,和類的宣告,其實在標頭檔案也可對一些特殊函式和變數定義,比如可以在標頭檔案中對行內函數和const型別變數定義,由於對類的宣告

    使用Java的JNative呼叫dll動態連結

    1,首先下載JNative的庫,其中包含JNative.jar, JNativeCpp.dll, libJNativeCpp.so這三個包。       JNative.jar是需要匯入到Java工程的lib下。 通過Build Path即可匯入

    使用DLL動態連結遮蔽任意按鍵(VB呼叫)

    ' Module1.bas Option Explicit Private Const WM_KEYDOWN = &H100 Private Const WM_KEYUP = &H101 Private Const WM_SYSKEYDOW

    Untiy 匯入C# DLL動態連結

    Untiy 匯入C# DLL外掛。 1. 建立類庫檔案,注意選擇.NET最好不要大於3.5版本,因為Unity使用的mono版本為2.0,支援最多到.NET 3.5版本。 2. 如下圖,建立一個MyRandomClass,包含一個公有方法GetRandom(),獲取C#自帶隨機數獲取函

    dll動態連結(1)

    1、庫型別 動態連結庫:dll型別, 靜態連結庫:lib型別; 2、動態庫簡介: 動態連結庫可以看成是一種倉庫,一種資源的集合:函式,變數,類,資源……都可以由動態連結庫來匯出。 3、動態庫與靜態庫的區別: a、靜態庫中的程式碼會直接塞到EXE中,而動態庫則可以被EXE動態的

    如何使用Python呼叫dll動態連結

    1. 需求 最近有這樣一個需求:網路上下載了大佬的深度學習影象識別模型,想要整合到自己的後端作為服務呼叫。 模型是.dll檔案,而我這邊使用的是Python。 2. 思路 .dll是使用C或者C++編譯的動態連結庫,一般留有函式入口可以進行呼叫。 首先我們通過閱讀原專案的原始

    原始檔,標頭檔案,靜態連結檔案動態連結檔案的的理解

    如果你把一個.exe檔案只接放到沒有操作系充的“裸機”上去執行,顯然是執行不了的,可是你把這個程式放在一個裝有windows系統的電腦上就能運行了,顯然,程式的執行還是得依靠windows作業系統,這裡就要說到.dll檔案,上面說到的連結這一步時的程式碼複製只講到對程式作者自已寫的檔案和.lib檔案中用到的程

    Windows下用Codeblocks建立一個最簡單的DLL動態連結

    建立一個最簡單的只有一個get_id() 函式的DLL庫  一、建立C語言動態連結庫 1.新建一個動態庫的工程 File - New - Project - DLL - Go 新建的工程原來的main.cpp和main.h刪除,新建兩個檔案simple.

    VS2013 c++ 生成和呼叫DLL動態連結

    在專案方案目錄裡,Debug資料夾中可以找到DLLGenerator.lib 和 DLLGenerator.dll, 把這兩個檔案和工程的標頭檔案(dllgenerator.h)移動到一個空資料夾(E:\dlltest\DLL)中,以後你的專案用到此dll就包含這個目錄,至此動態庫建立完畢。

    Codeblocks建立和呼叫DLL動態連結(C語言)

    建立一個最簡單的只有一個get_id() 函式的DLL庫  一、建立C語言動態連結庫 1.新建一個動態庫的工程 File - New - Project - DLL - Go 新建的工程原來的main.cpp和main.h刪除,新建兩個檔案simple.h, simple

    KERNEL32.dll動態連結報錯解決方法

     在Windows XP安裝遊戲或一些程式時,如百度雲管理等軟體面向Windows 7高版本的系統,對Windows XP相容性不那麼注重了,會出現錯誤提示:“定位程式輸入點GetlogicalprocessorInformation於動態連結庫KERNEL32.dll上”,這不是.net沒有安裝導致,而

    如何檢視linux動態連結檔案的版本等其他資訊

    ldd <可執行檔名> 檢視可執行檔案連結了哪些 系統動態連結庫nm <可執行檔名> 檢視可執行檔案裡面有哪些符號strip <可執行檔名> 去除符號表可以給可執行檔案瘦身如果我們想從可執行程式裡面提取

    java呼叫C/C++生成的dll動態連結----藉助JNI

    由於專案的需要,最近研究了java 呼叫DLL的方法,將如何呼叫的寫於此,便於日後查閱: 採用的方法是JNI:Java Native Interface,簡稱JNI,是Java平臺的一部分,可用於讓Java和其他語言編寫的程式碼進行互動。 下面是從網上摘取的JNI工作示意圖: 總體說明:先在JA

    使用Java呼叫dll動態連結

    一:什麼是dll? DLL(Dynamic Link Library)檔案為動態連結庫檔案,又稱“應用程式拓展”,是軟體檔案型別。在Windows中,許多應用程式並不是一個完整的可執行檔案,它們被分割成一些相對獨立的動態連結庫,即DLL檔案,放置於系統中。當我