DllMain介紹
跟exe有個main或者WinMain入口函數一樣,DLL也有一個入口函數,就是DllMain。以“DllMain”為關鍵字,來看看MSDN幫助文檔怎麼介紹這個函數的。
The DllMain function is an optional method of entry into a dynamic-link library (DLL)。(簡要翻譯:對于動态連結庫,DllMain是一個可選的入口函數。)這句話很重要,很多初學者可能都認為一個動态連結庫肯定要有DllMain函數。其實不然,像很多僅僅包含資源資訊的DLL是沒有DllMain函數的。
雖然DLL不能自己運作,可是Windows在加載DLL的時候,需要一個入口函數,就如同EXE的main一樣,否則系統無法引用DLL。是以根據編寫規範,Windows必須查找并執行DLL裡的一個函數DllMain作為加載DLL的依據,這個函數不作為API導出,而是内部函數。DllMain函數使DLL得以保留在記憶體裡,有的DLL裡面沒有DllMain函數,可是依然能使用,這是因為Windows在找不到DllMain的時候,會從其它運作庫中找一個不做任何操作的預設DllMain函數啟動這個DLL使它能被載入,并不是說DLL可以放棄DllMain函數
何時調用DllMain
系統是在什麼時候調用DllMain 函數的呢? 靜态連結時,或 動态連結時調用 LoadLibrary和 FreeLibrary都會調用DllMain函數。DllMain的第二個參數fdwReason指明了系統調用Dll的原因,它可能是:: DLL_PROCESS_ATTACH、 DLL_PROCESS_DETACH、 DLL_THREAD_ATTACH、 DLL_THREAD_DETACH。 以下從這四種情況來分析系統何時調用了DllMain。
1 DLL_PROCESS_ATTACH
大家都知道,一個程式要調用Dll裡的 函數,首先要先把DLL檔案映射到程序的 位址空間。要把一個DLL檔案映射到程序的位址空間,有兩種方法: 靜态連結和 動态連結的LoadLibrary或者LoadLibraryEx。 當一個DLL檔案被映射到程序的位址空間時,系統調用該DLL的DllMain函數,傳遞的fdwReason參數為DLL_PROCESS_ATTACH,這種調用隻會發生在第一次映射時。如果同一個程序後來為已經映射進來的DLL再次調用LoadLibrary或者LoadLibraryEx,作業系統隻會增加DLL的使用次數,它不會再用DLL_PROCESS_ATTACH調用DLL的DllMain函數。不同程序用LoadLibrary同一個DLL時,每個程序的第一次映射都會用DLL_PROCESS_ATTACH調用DLL的DllMain函數。 可參考DllMainTest的DLL_PROCESS_ATTACH_Test函數。
2 DLL_PROCESS_DETACH
當DLL被從程序的 位址空間解除映射時,系統調用了它的DllMain,傳遞的fdwReason值是DLL_PROCESS_DETACH。當DLL處理該值時,它應該執行程序相關的清理工作。 那麼什麼時候DLL被從程序的位址空間解除映射呢?兩種情況: ◆FreeLibrary解除DLL映射(有幾個LoadLibrary,就要有幾個FreeLibrary) ◆程序結束而解除DLL映射,在程序結束前還沒有解除DLL的映射,程序結束後會解除DLL映射。(如果程序的終結是因為調用了TerminateProcess,系統就不會用DLL_PROCESS_DETACH來調用DLL的DllMain函數。這就意味着DLL在程序結束前沒有機會執行任何清理工作。) 注意:當用DLL_PROCESS_ATTACH調用DLL的DllMain 函數時,如果傳回 FALSE,說明沒有初始化成功,系統仍會用DLL_PROCESS_DETACH調用DLL的DllMain函數。是以,必須確定清理那些沒有成功初始化的東西。 可參考DllMainTest的DLL_PROCESS_DETACH_Test函數。
3 DLL_THREAD_ATTACH
當程序建立一 線程時,系統檢視目前映射到程序 位址空間中的所有DLL檔案映像,并用值DLL_THREAD_ATTACH調用DLL的DllMain函數。 新建立的線程負責執行這次的DLL的DllMain 函數,隻有當所有的DLL都處理完這一通知後,系統才允許程序開始執行它的線程函數。 注意跟DLL_PROCESS_ATTACH的差別,我們在前面說過,第n(n>=2)次以後地把DLL 映像檔案映射到程序的位址空間時,是不再用DLL_PROCESS_ATTACH調用DllMain的。而DLL_THREAD_ATTACH不同,程序中的每次建立線程,都會用值DLL_THREAD_ATTACH調用DllMain函數,哪怕是線程中建立線程也一樣。
4 DLL_THREAD_DETACH
如果 線程調用了 ExitThread來結束線程(線程 函數傳回時,系統也會自動調用ExitThread),系統檢視目前映射到程序空間中的所有DLL檔案映像,并用DLL_THREAD_DETACH來調用DllMain函數,通知所有的DLL去執行線程級的清理工作。 注意:如果線程的結束是因為系統中的一個線程調用了 TerminateThread,系統就不會用值DLL_THREAD_DETACH來調用所有DLL的DllMain函數。
為DllMain換名
在早期的SDK版本中,DllMain是叫做DllEntryPoint。其實有一件鮮為人知的事:一個Dll的入口函數名是可以自己定義的。下面我将以VC++6.0為例來示範如何更改。首先要說明一點,雖然DllMain可以換成其他函數名,但函數的參數和傳回值必須和DllMain一樣。而且這個函數要為__stdcall類型(DllMain本身也是__stdcall類型)。
打開VC++菜單Project\Settings\Link tab\ Output in the Category box,如下圖,在Entry-point symbol中輸入要替換DllMain的函數名(當然這個函數名是你程式中已經實作的函數)。Entry-point symbol是幹麼的呢?可以以關鍵字“Entry-point symbol”搜尋MSDN幫助文檔檢視,搜尋時,打鈎“僅搜尋标題”會更快定位。
DLL專題之庫的簡介和靜态庫
原文位址: http://blog.csdn.net/beyond_q/article/details/2117077
從現在開始,我們進入DLL專題.今天就從靜态庫開始.
首先,我們建立一個靜态庫:(VC裡Win32 Static Library工程) ,選擇空工程.增加頭檔案和實作檔案(分别是StaticLib.h和StaticLib.cpp),代碼如下:
StaticLib.h :
#ifndef _STATIC_LIBRARY_H_
#define _STATIC_LIBRARY_H_
#include "windows.h"
extern "C" void ShowMessage( LPCTSTR pstrMsg, LPCTSTR pstrTitle );
#endif//_STATIC_LIBRARY_H_
StaticLib.cpp:
#include "StaticLib.h"
void ShowMessage( LPCTSTR pstrMsg, LPCTSTR pstrTitle )
{
MessageBox( NULL, pstrMsg, pstrTitle, MB_OK | MB_ICONINFORMATION );
}
這樣就好了,我們編譯連接配接就生成了靜态庫了.
然後,我們怎麼用呢!在用之前,我們先說說庫吧!
庫的了解
靜态連結庫與動态連結庫都是共享代碼的方式,隻不過是他們是在二進制級别上重用代碼而已。
對于靜态連結庫:
1、lib中的指令被直接編譯連接配接到最終的EXE檔案中了
2、靜态連結庫中不能再包含其他的動态連結庫或者靜态庫
對于動态連接配接庫:
1、DLL不必被被編譯連接配接到最終EXE檔案中,EXE檔案執行時"動态"地裝載和解除安裝DLL檔案
2、動态連結庫中還可以再包含其他的動态或靜态連結庫
3、DLL 的編寫與具體的程式設計語言及編譯器是無關的,COM也是這樣。隻要遵循DLL編寫規
範,DLL可以被任何語言調用
當然了,庫的東西,不隻是靜态連結庫與動态連結庫。還有COM,COM和DLL一樣也是一種跨語
言的二進制級别的代碼共享,不過COM有更多的優點。以後,我們會專門讨論COM/COM+/DCOM/
ATL/ActiveX等
使用靜态庫的方法如下:
//靜态庫的使用
#include ".//StcLib//StaticLib.h"
靜态加載StcLib.lib
#pragma comment( lib, ".//StcLib//Debug//StcLib.lib" )表
示所在檔案生成的.obj檔案應與StcLib.lib一起連接配接,當然你也可以使
用VC開發環境設定。
#pragma comment( lib, ".//StcLib//Debug//StcLib.lib" )
void CDllDlg::OnBtnStaticlib()
{
// TODO: Add your control notification handler code here
ShowMessage( _T("Hello World!"), _T("Information") );
}
好了,靜态庫搞完了啊!
下回我們就進入動态連接配接庫了.記的看啊!呵呵.
DLL專題之VC開發DLL和DLL編寫說明
原文位址:http://blog.csdn.net/beyond_q/article/details/2119773
今天,我們就真正進入了DLL程式設計,當然了,今天是寫第一個DLL,就寫個簡單的啦!呵呵
首先建立一個DLL工程(Win32 DynamicLink(Dll))工程,選擇一個空的工程,然後添加一個頭檔案和一個實作檔案,分别是:Simple.h和Simple.cpp.具體代碼和說明如下:
Simple.h檔案内容:
#ifndef _SIMPLE_DLL_H_
#define _SIMPLE_DLL_H_
#include "windows.h"
_declspec(dllexport) : 表示這個函數是DLL導出函數,可以被外部引
用。
extern "C" 表面是使用C調用約定,并且不會發生函數名字改變
如果想讓VC++編寫的DLL被其他語言編寫的程式調用,那麼應該将函數的
調用約定聲明為__stdcall。
C/C++調用方式為__cdecl,若采用C調用方式,将函數聲明為extern "C"。
__stdcall方式與__cdecl對函數名最終生成符号是不同的,而且參數的
入棧順序也不同。
由于不同的調用方式下,可能最終導出的函數名稱會有不同的變化(即發
生了名字改編),為了防止名字改編,我們還有另一種方式來導出函數方
式(推薦使用) :
用名字定義檔案的方式,即增加一個.def檔案。
.def檔案格式如下:
; Simple.def : 導出DLL函數
LIBRARY Simple
EXPORTS
ShowMessage @ 1
1、注釋由“;” 指定,注釋不能與語句在同一行。
2、“LIBRARY”語句說明.def檔案相應的DLL;
3、“EXPORTS”說明要導出函數的名稱。“@ 1”,表示要導出函數的序
号為1(在函數調用時,可以用到這個序号)
是以,Simple.def檔案的解釋是:生成Simple.DLL的動态連結庫,其導出
ShowMessage函數,該函數的序号為1。
extern "C" _declspec(dllexport) void ShowMessage( LPCTSTR pstrMsg, LPCTSTR pstrTitle );
#endif//_SIMPLE_DLL_H_
Simple.cpp檔案内容:
#include "Simple.h"
void ShowMessage( LPCTSTR pstrMsg, LPCTSTR pstrTitle )
{
MessageBox( NULL, pstrMsg, pstrTitle, MB_OK |MB_ICONINFORMATION );
}
然後,我們就要試着來調用我們的DLL啊,那接着看下面的代碼吧:
使用VC編寫DLL
非MFC動态庫, 即非MFC類庫結構,其導出函數為标準的C接口,能被非MFC或MFC編寫的應用程式所
調用。
MFC規則動态庫, 有一個繼承自CWinApp的類,不過沒有消息循環。
MFC擴充動态庫, 使用MFC的動态連結版本建立,隻能被用MFC類庫所編寫的應用程式調用。
VC環境下有兩種方式檢視你的DLL:
一個是Depends工具,一個是控制台的dumpbin工具。
VC環境下調試: 如果你想調進DLL,你必須在調用DLL函數的時候設個斷點,當程式調試到斷點時,你按
F11鍵就可以進入DLL了
//動态庫的使用
void CDllDlg::OnBtnSimple()
{
// TODO: Add your control notification handler code here
動态連接配接庫一般使用方法:LoadLibrary - GetProcAddress - FreeLibrary
typedef void (*lpShowMessage)
( LPCTSTR pstrMsg, LPCTSTR pstrTitle ); //DLL裡的函數原型
HINSTANCE hInst = NULL; //DLL的執行個體句柄
lpShowMessage ShowMessage; //函數定義
hInst = LoadLibrary( ".//Simple//Debug//Simple.dll" ); //導入DLL
if ( !hInst ) return ;
ShowMessage = (lpShowMessage)GetProcAddress( hInst, "ShowMessage" ); //得到DLL裡的函數位址
if ( ShowMessage )
{
ShowMessage( _T("Hello World!"), _T("Information") ); //調用DLL裡的函數
}
FreeLibrary( hInst ); //釋放DLL
}
好了,就到這了.COPY的是不是很爽啊.要頂一頂了嘛!不說了,BYE