爲何要開發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文件即可)
#pragma comment(lib,"名稱.lib")
extern "C" _declspec(dllimport) int WINAPI a(int i);
int _tmain(int argc, _TCHAR* argv[])
{
int i=a(1);
char a[10];
_itoa(i, a, 10);
MessageBoxA(NULL, a, "靜態調用結果", 0);
return 0;
}
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
}
_a(at)4
_函數名@函數參數所需堆棧大小