c DLL程式設計之一

2021-05-23 01:37:24 字數 3928 閱讀 6259

dll的優點

簡單的說,dll有以下幾個優點:

1)      節省記憶體。同乙個軟體模組,若是以源**的形式重用,則會被編譯到不同的可執行程式中,同時執行這些exe時這些模組的二進位製碼會被重複載入到記憶體中。如果使用dll,則只在記憶體中載入一次,所有使用該dll的程序會共享此塊記憶體(當然,像dll中的全域性變數這種東西是會被每個程序複製乙份的)。

2)      不需編譯的軟體系統公升級,若乙個軟體系統使用了dll,則該dll被改變(函式名不變)時,系統公升級只需要更換此dll即可,不需要重新編譯整個系統。事實上,很多軟體都是以這種方式公升級的。例如我們經常玩的星際、魔獸等遊戲也是這樣進行版本公升級的。

3)      dll庫可以供多種程式語言使用,例如用c編寫的dll可以在vb中呼叫。這一點上dll還做得很不夠,因此在dll的基礎上發明了com技術,更好的解決了一系列問題。

最簡單的dll

開始寫dll之前,你需要乙個c/c++編譯器和鏈結器,並關閉你的ide。是的,把你的vc和c++ builder之類的東東都關掉,並開啟你以往只用來記**的記事本程式。不這樣做的話,你可能一輩子也不明白dll的真諦。我使用了vc自帶的cl編譯器和link鏈結器,它們一般都在vc的bin目錄下(若你沒有在安裝vc的時候選擇註冊環境變數,那麼就立刻將它們的路徑加入path吧)。【如果用vc的話步驟:新建專案-windows控制台程式-dll(注意下面有個「輸出符號」要根據情況選擇或不選)】

最簡單的dll並不比c的helloworld難,只要乙個dllmain函式即可,包含objbase.h標頭檔案(支援com技術的乙個標頭檔案)。若你覺得這個標頭檔案名字難記,那麼用windows.h也可以。源**如下:dll_nolib.cpp

#include

#include

bool apientry dllmain(handle hmodule, dword dwreason, void* lpreserved)

{handle g_hmodule;

switch(dwreason)

{case dll_process_attach:

cout<<"dll is attached!"《其中dllmain是每個dll的入口函式,如同c的main函式一樣。dllmain帶有三個引數,hmodule表示本dll的例項控制代碼(聽不懂就不理它,寫過windows程式的自然懂),dwreason表示dll當前所處的狀態,例如dll_process_attach表示dll剛剛被載入到乙個程序中,dll_process_detach表示dll剛剛從乙個程序中解除安裝。當然還有表示載入到執行緒中和從執行緒中解除安裝的狀態,這裡省略。最後乙個引數是乙個保留引數(目前和dll的一些狀態相關,但是很少使用)。

從上面的程式可以看出,當dll被載入到乙個程序中時,dll列印"dll is attached!"語句;當dll從程序中解除安裝時,列印"dll is detached!"語句。

編譯dll需要以下兩條命令:

cl /c dll_nolib.cpp

這條命令會將cpp編譯為obj檔案,若不使用/c引數則cl還會試圖繼續將obj鏈結為exe,但是這裡是乙個dll,沒有main函式,

因此會報錯。不要緊,繼續使用鏈結命令。

link /dll dll_nolib.obj

載入dll(顯式呼叫)

使用dll大體上有兩種方式,顯式呼叫和隱式呼叫。這裡首先介紹顯式呼叫。編寫乙個客戶端程式:dll_nolib_client.cpp

#include

#include

int main(void)

{//載入我們的dll

hinstance hinst=::loadlibrary("dll_nolib.dll");

if (null != hinst)

{cout<<"dll loaded!"並執行dll_nolib_client.exe,得到如下結果:

dll is attached!

dll loaded!

dll is detached!

以上結果表明dll已經被客戶端載入過。但是這樣僅僅能夠將dll載入到記憶體,不能找到dll中的函式。

使用dumpbin命令檢視dll中的函式【也可以用vs2005的工具depends.exe檢視】

dumpbin命令可以檢視乙個dll中的輸出函式符號名,鍵入如下命令:

dumpbin –exports dll_nolib.dll

通過檢視,發現dll_nolib.dll並沒有輸出任何函式。

如何在dll中定義輸出函式

總體來說有兩種方法,一種是新增乙個def定義檔案,在此檔案中定義dll中要輸出的函式;第二種是在源**中待輸出的函

數前加上__declspec(dllexport)關鍵字。

含def檔案的dll

首先寫乙個帶有輸出函式的dll,源**如下:dll_def.cpp

#include

#include

void funcindll (void)

{cout<<"funcindll is called!"《這個dll的def檔案如下:dll_def.def

library         dll_def.dll

description     "c)2007-2009 wang xuebin"

exports

funcindll @1 private

cl /c dll_def.cpp

link /dll dll_def.obj /def:dll_def.def

再呼叫dumpbin檢視生成的dll_def.dll:

dumpbin –exports dll_def.dll

得到如下結果:

dump of file dll_def.dll

file type: dll

section contains the following exports for dll_def.dll

0 characteristics

46e4ee98 time date stamp mon sep 10 15:13:28 2007

0.00 version

1 ordinal base

1 number of functions

1 number of names

ordinal hint rva      name

1    0 00001000 funcindll

summary

2000 .data

1000 .rdata

1000 .reloc

6000 .text

觀察這一行

1    0 00001000 funcindll

會發現該dll輸出了函式funcindll。

顯式呼叫dll中的函式

寫乙個dll_def.dll的客戶端程式:dll_def_client.cpp

#include

#include

int main(void)

{//定義乙個函式指標

typedef void (* dllwithlib )(void);

//定義乙個函式指標變數

dllwithlib pffuncindll = null;

//載入我們的dll

hinstance hinst=::loadlibrary("dll_def.dll");

if (null != hinst)

{cout<<"dll loaded!"《有兩個地方值得注意,第一是函式指標的定義和使用,不懂的隨便找本c++書看看;第二是getprocaddress的使用,這個api是用來查詢dll中的函式位址的,第乙個引數是dll的控制代碼,即loadlibrary返回的控制代碼,第二個引數是dll中的函式名稱,即dumpbin中輸出的函式名(注意,這裡的函式名稱指的是編譯後的函式名,不一定等於dll源**中的函式名)。

dll loaded!

funcindll is called!

這表明客戶端成功呼叫了dll中的函式funcindll。

RAPI程式設計之一

今天剛開始學習rapi程式設計,先將今天學習的成果放出來,大家學習。如果 有問題,請提出,多謝 在pc上使用vc6,好久沒有用它了。一直在evc4下程式設計,呵呵.rapi功能的實現,需要rapi.dll和rapi.h。在以下的實現中,link的動態庫是c windows system32 rapi...

如何學習程式設計之一?

第乙個階段 什麼都不會。不會就先抄你老師給你們寫的例子,不准複製,要乙個字乙個字的敲。放心,其 中肯定會遇到問題,先自己想想問題怎麼解決,能解決就盡量自己解決,不能就 看你老師源 第二個階段 知道大概了。但是要自己寫還是寫不出來,這個時候你要先看你老師 知道大概的思路。然後你以自己的思路開始寫,開始...

小白談程式設計 之一

小白談程式設計 生產者消費者模式之串列埠資料的接收處理顯示 1 串列埠資料接收問題 2 資料快取類實現 3 多執行緒及其同步問題 4 子執行緒間資料傳遞問題 5 子執行緒資料如何實時更新到介面問題 6 窗體資料傳遞問題。問題這麼多?誰讓我是小白啊?小白的痛,小白懂!在開始這個題目之前,先介紹一下例項...