VS2010 學習C++動態鏈接庫的創建與使用(帶實例代碼)

本文只是記載下學習C++動態鏈接庫的過程以及記錄學習過程中的一些程序,好開始。

先用VS2010創建一個dll的工程,好,只看圖不說話,點擊finish就完成創建了。。。

創建頭文件developer_dll.h和源代碼developer_dll.cpp兩個文件,裏面可以寫你喜歡的函數以及類。

以下是我的.h和.cpp文件代碼:

developer_dll.h:

#pragma once
#ifdef dev_dll_api
#else
#define dev_dll_api _declspec(dllimport)
#endif

namespace developer_dll_namespace
{
	dev_dll_api void add(int a, int b);
	dev_dll_api void subtract(int a, int b);

	class dev_dll_api developer_dll_class
	{
	public:
		void class_add(int a, int b);
		void class_subtract(int a, int b);
	protected:
	private:
		int x;
		int y;
	};
}

developer_dll.cpp:

#include <iostream>

#define dev_dll_api _declspec(dllexport)
#include "developer_dll.h"

namespace developer_dll_namespace
{
	void add(int a, int b)
	{
		std::cout<<"兩者之和(調用普通函數):"<<a+b<<std::endl;
	}

	void subtract(int a, int b)
	{
		std::cout<<"兩者之差(調用普通函數):"<<((a>b)?(a-b):(b-a))<<std::endl;
	}

	void developer_dll_class::class_add(int a, int b)
	{
		std::cout<<"兩者之和(調用類的成員函數):"<<a+b<<std::endl;
	}

	void developer_dll_class::class_subtract(int a, int b)
	{
		x = a;
		y = b;
		std::cout<<"兩者之差(調用類的成員函數):"<<((x>y)?(x-y):(x-y))<<std::endl;
	}
}

好,搞定這兩個就可以build生成.lib以及.dll文件了,看圖:


然後就可以創建使用者的.h文件和.cpp文件來加載dll,在程序中加載DLL有兩種方法,分別爲隱式鏈接動態加載dll隱式鏈接需要(.lib、.dll、.h)文件,動態加載dll只需(.dll)文件就可以了。

1、然後下面先以隱式鏈接測試:

創建源代碼user_dll.cpp來測試dll。


.lib、.dll、.h)文件複製進來並且創建相應的文件夾。


在這裏將lib的路徑加進去,不然編譯鏈接會出錯。

下面是我的user_dll.cpp:

#include <iostream>
#include <Windows.h>

#include "developer_dllh/developer_dll.h"
#pragma comment(lib, "developer_dll.lib") 

using namespace std;
using namespace developer_dll_namespace;

int main()
{
	add(2,3);
	subtract(2,3);

	developer_dll_class users;
	users.class_add(2,3);
	users.class_subtract(2,3);

	system("pause");
	return 0;
}

運行測試一把:我的結果是這樣的:


那如果我們只想導出類的部分函數要怎麼導出:


這裏我們可以利用depends這個軟件來查看dll導出了哪些函數:


這裏爲什麼函數名會是這樣子呢,主要是C++爲了實現重載實現了函數傾軋,函數名就按照一定的規則發生的變化,只要都是C++是可以正常調用的。如果要用C語言調用dll,就要添加extern “C”,但是它不能用於導出類的成員函數。如果是給其他語言調用則使用extern “C”也沒有卵用。要避免這個問題就得使用模塊定義文件.def,待會就測試這個怎麼使用。

	
	add(2,3);
	subtract(2,3);

	developer_dll_class users;
	users.class_add(2,3);
	//users.class_subtract(2,3); 這個成員函數沒有導出,所以不能用了。

	system("pause");
	return 0;

好我們測試一把:



2、好下面來實現動態鏈接dll:

這裏需要用到幾個函數:

LoadLibrary:

[格式]:

  1. function LoadLibrary(LibFileName : PChar): Thandle;
[說明]:參數 LibFileName 指定了要裝載的 DLL 文件名,如果 LibFileName 沒有包含一個路徑,系統將按照:當前目錄、Windows 目錄、Windows 系統目錄、包含當前任務可執行文件的目錄、列在 PATH 環境變量中的目錄等順序查找文件。

[功能]:加載由參數 LibFileName 指定的 DLL 文件。

如果函數操作成功,將返回裝載 DLL 庫模塊的實例句柄,否則,將返回一個錯誤代碼。假如在應用程序中用 LoadLibrary 函數裝入某一個 DLL 前, 其他應用程序已把該 DLL 裝入內存中了,則系統將不再裝入該 DLL 的另一個實例,而是使該 DLL 的“引用計數”加 1

GetProAccess

[格式]:

  1. function GetProcAddress(Module:Thandle; ProcName:PChar): TfarProc;
[說明]:參數 Module 包含被調用函數的 DLL 句柄,這個值由 LoadLibrary 返回, ProcName是指向含有函數名的以 nil 結尾的字符串指針,或者可以是函數的次序值,但大多數情況下,用函數名是一種更穩妥的選擇。這裏如果要使用函數名字如果不採用.def的方式會比較麻煩。

int main()
{
	HMODULE load_ex = LoadLibrary(_T("developer_dll.dll"));//返回一個句柄
	if (load_ex)
	{
		addprt add = (addprt)GetProcAddress(load_ex,"?add@developer_dll_namespace@@YAXHH@Z");
		if (add)
		{
			add(2,3);
		}
		subtractprt subtract = (subtractprt)GetProcAddress(load_ex,"?subtract@developer_dll_namespace@@YAXHH@Z");
		if (subtract)
		{
			subtract(2,3);
		}
	}

如果該函數執行成功,則返回 DLL 中由參數 ProcName 指定的過程或函數的入口地址,否則返回 nil 。
FreeLibrary:

[功能]:返回參數 Module 指定的模塊中,由參數 ProcName 指定的過程或函數的入口地址

[格式]:

  1. procedure  FreeLibrary(Module: Thandle);

[說明]:將由參數 Module 指定的 DLL 文件從內存中卸載 1 次。
[說明]:Module 爲 DLL 庫的句柄。這個值由 LoadLibrary 返回。由於 DLL 在內存中只裝載一次,因此調用 FreeLibrary 首先使 DLL 的引用計數減 1,如果計數減爲 0 則卸載該 DLL。

上面函數說明是從這裏摘下來的,感興趣可以去閱讀https://www.cnblogs.com/westsoft/p/5936092.html

現在測試以導出文件(.def)的方式導出dll,在工程裏面添加.def文件。


下面是我的源文件:

//.cpp文件
#include <iostream>
#include "developer_dllmd.h"
namespace developer_dll_namespace
{
	void add(int a, int b)
	{
		std::cout<<"兩者之和(調用普通函數):"<<a+b<<std::endl;
	}
	void subtract(int a, int b)
	{
		std::cout<<"兩者之差(調用普通函數):"<<((a>b)?(a-b):(b-a))<<std::endl;
	}
#if 0
void developer_dll_class::class_add(int a, int b)
	{
		std::cout<<"兩者之和(調用類的成員函數):"<<a+b<<std::endl;
	}

	void developer_dll_class::class_subtract(int a, int b)
	{
		x = a;
		y = b;
		std::cout<<"兩者之差(調用類的成員函數):"<<((x>y)?(x-y):(y-x))<<std::endl;
	}
#endif
}
//.h文件
#pragma  once
namespace developer_dll_namespace
{
	void add(int a, int b);
	void subtract(int a, int b);
	#if 0
class developer_dll_class
	{
	public:
		void class_add(int a, int b);
		void class_subtract(int a, int b);
	protected:
	private:
		int x;
		int y;
	};
#endif
}
//.def文件
LIBRARY "developer_dllmd"
EXPORTS
add @1
export_subtract=subtract @2

下面編寫user函數實現動態調用:

#include <iostream>
#include <Windows.h>
#include <tchar.h>
#include <winuser.h>

using namespace std;
typedef void (*addprt)(int a, int b);
typedef void (*subtractprt)(int a, int b);

int main()
{
	HMODULE load_ex = LoadLibraryA("developer_dllmd.dll");//返回一個句柄
	if (load_ex)
	{
		addprt add = (addprt)GetProcAddress(load_ex,"add");
		if (add)
		{
			add(2,3);
		}

		addprt add1 = (addprt)GetProcAddress(load_ex,MAKEINTRESOURCEA(1));
		if (add1)
		{
			cout<<"通過序號調用:"<<"\t";
			add1(2,3);
		}
		else
		{
			cout<<"獲取地址失敗"<<endl;
		}

		subtractprt subtract = (subtractprt)GetProcAddress(load_ex,"export_subtract");
		if (subtract)
		{
			subtract(2,3);
		}
		subtractprt subtract1 = (subtractprt)GetProcAddress(load_ex,MAKEINTRESOURCEA(2));
		if (subtract1)
		{
			cout<<"通過序號調用:"<<"\t";
			subtract1(2,3);
		}
	}
        FreeLibrary(load_ex);
	system("pause");
	return 0;
}

好測試一把,我們看下結果如何:


對於類的動態導出其實文中方法不太好,還有更好的方法下會解說。

好,文章到此結束,謝謝觀看,歡迎提問,共同討論和提高。

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