天天看點

DllMain簡介和DLL編寫說明

 

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