DLL的創建與使用


一、動態鏈接庫(DLL)

動態鏈接庫提供了一種方法,使進程可以調用不屬於其執行代碼的函數。函數的可執行代碼位於一個.dll文件中,該文件包含一個或多個已被編譯、鏈接並使用它們的進程分開存儲的函數。

優點:

1.擴展了 應用程序的特性

2.可以使用多種編程語言來編寫

3.簡化項目的管理

二、依賴項

當某個程序或DLL使用其他DLL或DLL函數時,就會創建依賴項,因此程序就不會再獨立了,依賴項如果被破壞,該程序可能出現問題。

三、入口點

在創建DLL時,可以有選擇地指定入口點的函數。當進程或線程將它們自身附加到DLL或者將它們自身以DLL分離時,都會調用入口函數。入口函數應該只進行簡單的初始化工作,而不應該調用其他DLL函數或者終止函數。

關於創建DLL,我的環境是VS2017,步驟為:文件->新建項目->選擇動態鏈接庫(DLL)->完成。在創建的項目中的源文件可以看到有一個已經創建好的dllmain.cpp,這個就DLL入口點的代碼了。

 1 // dllmain.cpp : 定義 DLL 應用程序的入口點。
 2 #include "stdafx.h"
 3 BOOL APIENTRY DllMain( HMODULE hModule,     //模塊句柄
 4                        DWORD  ul_reason_for_call,//調用原因
 5                        LPVOID lpReserved   //參數保留,好像沒什么用
 6                      )
 7 {
 8     switch (ul_reason_for_call)
 9     {
10     case DLL_PROCESS_ATTACH:  //DLL被某個程序加載
11     case DLL_THREAD_ATTACH:   //DLL被某個線程加載
12     case DLL_THREAD_DETACH:   //DLL被某個線程卸載
13     case DLL_PROCESS_DETACH:  //DLL被某個程序加載
14         break;
15     }
16     return TRUE;
17 }

四、如何導出(在DLL中實現的)

第一種:

步驟1:向所有需要導出的DLL函數中添加關鍵字__declspec(dllexport)。

步驟2:要在應用程序中使用導出的DLL函數,必須使用__declspec(dllexport)來聲明要導入的各個函數。

本章使用的就是這種方法

通常最好使用一個包含define語句和ifdef語句的頭文件,以便分隔導出語句和導入語句,代碼如下。

說明:該DLL庫實現的是簡單的加減乘除運算,注意實現減法的這個函數,我這里特別使用了一個抽象類來實現,我發現有些企業代碼都會有這種類似的抽象類,然后用純虛函數做接口。

 1 //__declspec(dllexport)修飾符指示編譯器和鏈接器從 
 2 //DLL 導出函數或變量,以便其他應用程序可以使用它,在ExprotDll.h中
 3 #pragma once
 4 #ifdef EXPORTDLL_EXPORTS
 5 #define EXPORTDLL_API __declspec(dllexport)
 6 #else
 7 #define EXPORTDLL_API __declspec(dllimport)
 8 #endif
 9 
10 //導出的類
11 class EXPORTDLL_API CExportDll {
12 public:
13     // TODO:  在此添加方法
14     //用純虛函數做為接口
15     virtual int SUBTARCT(int a, int b) = 0;
16     virtual ~CExportDll() {};
17 
18 };
19 
20 //導出函數,如果是在C++環境下,則執行"{" "}"中的內容
21 #ifdef __cplusplus
22 extern "C" {
23 #endif
24     EXPORTDLL_API CExportDll* Create();
25     EXPORTDLL_API void Destroy(CExportDll* ex);
26 #ifdef __cplusplus
27 }
28 #endif
29 
30 //也可用下列方法定義導出函數
31 extern "C" EXPORTDLL_API int nExportDll;
32 extern "C" EXPORTDLL_API int ADD(int a, int b);
33 extern "C" EXPORTDLL_API int MULTIPLY(int a, int b);
34 extern "C" EXPORTDLL_API float DIVIDE(float a, float b);

第二種:

創建模塊定義文件.def以列出導出的DLL函數:

五、特別調用

需要注意以下一些情況:

1.如果使用了Win32 API,則應該使用關鍵字__stdcall修飾函數

2.如果將C++生成的DLL供標准C語言使用,輸出文件用extern "C"來修飾,設置導出函數時采用.def文件形式,而不是__declspec(dllexport)

六、編寫DLL

創建DLL工程並且定義了相應的導出函數、變量或類之后,接下來就是實現功能了。本章中因為CExportDll是一個抽象類,所以需要再創建一個它的子類(CExportDllChild.h中),實現相應的接口功能(EXportDll.cpp中),同時其他函數也在(EXportDll.cpp)中實現功能。

1 //ExportDllChild.h
2 #pragma once
3 #include "ExportDll.h"
4 
5 class CExportDllChild :public CExportDll
6 {
7     virtual int SUBTARCT(int a, int b);
8     virtual ~CExportDllChild() {};
9 };
 1 // ExportDll.cpp : 定義 DLL 應用程序的導出函數。
 2 
 3 #include "stdafx.h"
 4 #include "ExportDllChild.h"
 5 
 6 //這是一個導出變量的一個示例
 7 EXPORTDLL_API int nExportDll = 0;
 8 
 9 //這是導出函數的幾個示例
10 //當前使用的是C++編譯出來的
11 
12 //構造對象
13 EXPORTDLL_API CExportDll* Create()
14 {
15     return new CExportDllChild;
16 }
17 //析構對象
18 EXPORTDLL_API void Destroy(CExportDll* ex)
19 {
20     delete ex;
21 }
22 //
23 EXPORTDLL_API int ADD(int a, int b)
24 {
25     return a + b;
26 }
27 //
28 int CExportDllChild::SUBTARCT(int a, int b)
29 {
30     return a - b;
31 }
32 //
33 EXPORTDLL_API int MULTIPLY(int a, int b)
34 {
35     return a * b;
36 }
37 //
38 EXPORTDLL_API float DIVIDE(float a, float b)
39 {
40     if (b != 0) {
41         return a / b;
42     }
43     else
44         return -1;
45 }

創建編寫好后,進行生成,可以在項目Debugp目錄下看到生成的動態庫.dll和靜態庫.lib

七、調用DLL

調用DLL有兩種方法,一種是顯式鏈接方式,另一種是隱式鏈接方式。

在調用前,先創建一個Win32控制台應用程序或者MFC,這里創建的是一個簡單的Win32程序(LoadDll)

1.顯式鏈接:

在此項目中,需要將CExportDllChild.h放在LoadDll目錄下。然后在cpp中添加以下代碼

 1 // LoadDll.cpp : 此文件包含 "main" 函數。程序執行將在此處開始並結束。
 2 //
 3 
 4 #include "pch.h"
 5 #include <Windows.h>
 6 #include <iostream>
 7 #include "ExportDll.h"
 8 
 9 /*-------------動態鏈接-------------*/
10 int main()
11 {
12     HMODULE hModule;   //模塊句柄
13     //定義函數指針
14     typedef int(*FUNC1)(int a, int b);
15     typedef CExportDll* (*CreateFun2)();
16     typedef void(*DestroyFun2)(CExportDll* ex);
17     typedef int(*FUNC3)(int a, int b);
18     typedef float(*FUNC4)(float a, float b);
19     //調用DLL,先找當前文件夾,如果沒有,就會去system32下查找 
20     hModule = ::LoadLibrary(L"ExportDll.dll"); 
21     
22     if (hModule == NULL){
23         MessageBox(NULL, L"DLL加載失敗", L"Mark", MB_OK);
24     }
25     //獲取相應DLL函數的入口地址
26     FUNC1 add = (FUNC1)::GetProcAddress(hModule, "ADD");
27     CreateFun2 create = (CreateFun2)::GetProcAddress(hModule, "Create");
28     DestroyFun2 destroy = (DestroyFun2)::GetProcAddress(hModule, "Destroy");
29     FUNC3 multiply = (FUNC3)::GetProcAddress(hModule, "MULTIPLY");
30     FUNC4 divide = (FUNC4)::GetProcAddress(hModule, "DIVIDE");
31     //
32     if (add != NULL){
33         std::cout << "a+b="<< add(10, 5) << std::endl;
34     }
35     //
36     if (create&&destroy) 
37     {
38         CExportDll* p = create();
39         std::cout << "a-b=" << p->SUBTARCT(10, 5) << std::endl;
40         destroy(p);
41         p = NULL;
42     }
43     //
44     if (multiply != NULL){
45         std::cout << "a*b=" << multiply(10, 5) << std::endl;
46     }
47     //
48     if (divide != NULL)
49     {
50         std::cout << "a/b=" << divide(10, 5) << std::endl;
51     }
52     FreeLibrary(hModule);   //釋放句柄
53     return 0;
54 }

調用結果:

2.隱式調用

在程序開始執行時就將DLL文件加載到應用程序中。隱式調用沒用過,不做表述了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM