[Win32] DLL的開發和使用

本博文由CSDN博主zuishikonghuan所作,版權歸zuishikonghuan所有,轉載請註明出處:http://blog.csdn.net/zuishikonghuan/article/details/47775535

爲何要開發DLL:

1。DLL可以在程序需要時加載或卸載,可以實現軟件的“模塊化開發”。

2。可以達到一些特殊目的,比如通過DLL注入來進入另一個進程的空間,Hook它的函數,等等。

所以不管怎麼說,DLL是Windows開發中相當重要的一部分。

1。DllMain

MSDN:https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583(v=vs.85).aspx

BOOL WINAPI DllMain(
  _In_ HINSTANCE hinstDLL,
  _In_ DWORD     fdwReason,
  _In_ LPVOID    lpvReserved
);

hinstDLL:DLL模塊的實例句柄

fdwReason:可以是下列值之一:

DLL_PROCESS_ATTACH:DLL 被加載到進程啓動或通過調用 LoadLibrary 到當前進程的虛擬地址空間。Dll 可以利用這個機會來初始化。

DLL_PROCESS_DETACH:正在從調用進程的虛擬地址空間卸載 DLL。

DLL_THREAD_ATTACH:當前進程創建一個新線程。請注意,DLL 入口點函數調用時使用此值只由線程創建後由進程加載的 DLL。當 DLL 加載時使用 LoadLibrary 時,現有的線程不調用新加載的 dll 入口點函數。

DLL_THREAD_DETACH:一個線程正在退出。

lpvReserved:

如果 fdwReason 是 DLL_PROCESS_ATTACH,NULL表示DLL是動態載入的,非NULL說明是靜態載入的。
如果 fdwReason 是 DLL_PROCESS_DETACH,NULL表示是調用FreeLibrary使DLL卸載的,非NULL說明是進程即將終止而調用的。

返回值:當系統調用 DllMain 函數的 DLL_PROCESS_ATTACH 值時,如果初始化成功,該函數返回TRUE,如果初始化失敗,返回FALSE(導致LoadLibrary失敗或進程加載失敗)。

如果不是非常有必要,不要在DLL中靜態調用非Kernel32.dll和非Ntdll.dll中的函數!

原因:調用時相應DLL可能沒有加載,因此會出現無法預料的問題。

2。導出函數

DLL中的函數之所以能被程序調用,是因爲DLL可以把自身的函數導出去給其他程序調用,這就是爲什麼用DLL可以進行“模塊化開發”的原因。

導出函數可以用兩種方法導出

1。在函數聲明或定義上顯示聲明爲:_declspec(dllexport)

2。使用DEF文件

比如些一DLL:

#include "stdafx.h"

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}

extern "C" _declspec(dllexport) int WINAPI a(int i){
	return (++i);
}

extern "C"作用:禁用函數重載以避免C++編譯器不按照符號修飾約定導出符號。

編譯後會生成DLL文件和Lib文件,如果是控制檯編譯,先寫一def文件,然後使用lib命令將def打包成lib,其中def文件這樣寫:

LIBRARY DLL名字(很重要,這個會寫入lib,在exe編譯時dll名稱會寫入PE文件導入表)
EXPORTS 
函數1
函數2
。。。。

lib.exe從def導出lib的命令爲:lib.exe /def:xxx.def(如果從obj導出直接lib加上obj文件即可)

3。調用DLL(靜態調用和動態調用)
靜態調用是指進程自身聲明函數原形,編譯時產生弱符號,和lib中的強符號連接,lib文件中來自LIBRARY的DLL名稱會寫入PE程序的導入表,exe載入內存後dll直接載入內存,並完成重定向,構造IAT。
動態調用是指exe在需要時把DLL加載進來,找到制定導出函數的地址並帶調用,在不需要的時候卸載DLL。

靜態調用:
1。將lib和dll置於工程目錄中
2。添加lib引用
#pragma comment(lib,"名稱.lib")
3。添加函數聲明
extern "C" _declspec(dllimport) int WINAPI a(int i);
用_declspec(dllimport)倒入函數
4。調用函數
int _tmain(int argc, _TCHAR* argv[])
{
	int i=a(1);
	char a[10];
	_itoa(i, a, 10);
	MessageBoxA(NULL, a, "靜態調用結果", 0);
	return 0;
}
彈出的對話框顯示2

動態調用:
void calldll(){
	typedef int (WINAPI *A)(int i);//定義函數指針
	HMODULE hdll = LoadLibrary(TEXT("DLL.dll"));//加載DLL
	if (hdll != NULL){
		A aa = (A)GetProcAddress(hdll, "_a@4");//獲取函數地址
		if (aa != NULL){
			int i = aa(1);//調用函數
			char a[10];
			_itoa(i, a, 10);
			MessageBoxA(NULL, a, "動態調用結果", 0);
		}
	}
	//....
	FreeLibrary(hdll);//不需要時可以卸載DLL
}
彈出的對話框依然顯示2
關於GetProcAddress函數名稱爲
_a(at)4
的問題:是因爲我用_declspec(dllexport)導出符號並禁用重載了,同時我用的stdcall,所以按照標準函數符號修飾約定,函數名稱爲
_函數名@函數參數所需堆棧大小
如果不想這樣就需要自己寫def文件導出符號了。



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