如何編寫dll文件:可以用幾種語言來實現(xiàn),如delphi編寫dll,pb編寫dll文件,java 編寫dll,vc 編寫dll,mfc dll 編寫等。
——動態(tài)鏈接庫(dll)是包含共享函數(shù)庫的二進(jìn)制文件,可以被多個應(yīng)用程序同時使用。建立應(yīng)用程序的可執(zhí)行文件時,不必將DLL連接到應(yīng)用程序中,而是 在運行時動態(tài)裝載DLL,裝載時DLL被映射到調(diào)用進(jìn)程的地址空間中。通常我們在調(diào)用DLL時所需的DLL文件必須位于以下三個目錄之一:
——(1)Windows的系統(tǒng)目錄:\windows\system;
——(2)DOS中path所指出的任何目錄;
——(3)程序所在的目錄;
一.動態(tài)鏈接庫(DLL)結(jié)構(gòu)
——DLL中定義有兩種函數(shù):導(dǎo)出函數(shù)(export function)和內(nèi)部函數(shù)
(internal function),導(dǎo)出函數(shù)可以被其他模塊調(diào)用,內(nèi)部函數(shù)只能在DLL內(nèi)部使用。我們在用C++定制DLL文件時,需要編寫的就是包含導(dǎo)出函數(shù)表的模塊 定義文件(.DEF)和實現(xiàn)導(dǎo)出函數(shù)功能的C++文件。下面以Sample.dll為例介紹DEF文件和實現(xiàn)文件的結(jié)構(gòu):
——1.模塊定義文件(.DEF)是由一個或者多個用于描述DLL屬性的模塊語
句組成的文本文件,每個.DEF文件至少必須包含以下模塊定義語句:
第一個語句必須是LIBRARY語句,指出DLL的名字。
EXPORTS語句列出被導(dǎo)出函數(shù)的名字。
可以使用DESCRIPTION語句描述DLL的用途(此句可選)。
";"對一行進(jìn)行注釋(可選)
——2.實現(xiàn)文件(.cpp文件為例)
——實現(xiàn)入口表函數(shù)的.cpp文件中,包含DLL入口點處理的API函數(shù)和導(dǎo)出
函數(shù)的代碼。
二.創(chuàng)建Sample.dll
——1.首先創(chuàng)建Sample.dll的工程,啟動VC++5.0按以下步驟生成DLL工程:
在菜單中選擇File\New\Project
在工程列表中選擇Win32Dynamic-LinkLibrary
在ProjectName中輸入工程名:Sample
單擊Location右邊按鈕,選擇c:\sample目錄
單擊OK完成,至此已創(chuàng)建了Sample.dll的工程文件
----2.創(chuàng)建Sample.def文件
在菜單中選擇File\New\TextFile
輸入以下完代碼后保存文件名"Sample.def"
Sample.def
指出DLL的名字Sample,鏈接器將這個名
字放到DLL導(dǎo)入庫中
LIBRARY Sample
定義導(dǎo)出函數(shù)ShowMe()為例
EXPORTS
ShowMe
def文件結(jié)束
---- 3. 創(chuàng) 建Sample.cpp
.在菜單中選擇File\New\C++ Source File項
.輸入以下代碼后保存文件名"Sample.cpp"
//Sample.cpp
#include < windows.h >
int ShowMe(void);
//DllEntryPoint為DLL入口點函數(shù),
負(fù)責(zé)初試化并終止DLL
BOOL WINAPI DllEntryPoint(HINSTANCE
hDLL,DWORD dwReason,LPVOID Reserved)
{
switch(dwReason)
{
case DLL_PROCESS_ATTACH:
{
break;
}
case DLL_PROCESS_DETACH:
{
break;
}
}
return TRUE;
}
int ShowMe(void)
{
//蜂鳴器響一下
MessageBeep((WORD)-1);
MessageBox("你好!");
return 1;
}
——4.編譯DLL文件——從 Build 菜單中選擇 BuildSample.DLL, 產(chǎn)生
Sample.DLL文件,以后就可以隨時調(diào)用了。
三.在應(yīng)用程序中調(diào)用DLL文件
——在應(yīng)用程序中要首先裝入DLL后才能調(diào)用導(dǎo)出表中的函數(shù),例如用MFC
創(chuàng)建基于對話框的工程Test,并在對話框上放置"Load"按鈕,你就必須添加裝載代碼。----1.首先在TestDlg.cpp的首部添加變量設(shè)置代碼:
//設(shè)置全局變量gLibSample用于存儲DLL句柄
HINSTANCE gLibSample=NULL;
//第二個變量ShowMe是指向DLL
庫中ShowMe()函數(shù)的指針
typedef int(* SHOWME)(void);
SHOWME ShowMe;
2.利用ClassWizard為"Load"按鈕添加裝載DLL的代碼
Void CTestDlg::OnLoadButton()
{
//要添加的代碼如下
if(gLibMyDLL!=NULL)
{
MessageBox("The Sample.DLL has already been load.");
return;
}
//裝載Sample.dll,未加路徑,將在三個默認(rèn)路徑中尋找
gLibSample=LoadLibrary("SAMPLE.DLL");
//返回DLL中ShowMe()函數(shù)的地址
ShowMe=(SHOWME)
GetProcAddress(gLibSample,"ShowMe");
//代碼添加完畢
}
——3.只要DLL裝載成功,在應(yīng)用程序中就可以直接調(diào)用ShowMe()函數(shù),此時已完成了定制和調(diào)用DLL的全部過程。----本程序在Windows95,VC++5.0中運行通過。
生成一個Dll工程,然后將你的類和函數(shù)都添加進(jìn)去,然后將要輸出的函數(shù)在該工程下的Translate.def文件中將名字列出(注意,只要名字,不要括號、參數(shù)等);實際上和你的Exe工程沒有很大的區(qū)別。
如果你要用VB用,在Dll中定義你的函數(shù)的時候前面要加上“__stdcall”關(guān)鍵字,否則VB沒有辦法使用。
建議Dll中少用資源,只用函數(shù)比較好,如果要用資源,在資源載入時要做一個處理,不然會由于hModule的問題發(fā)生錯誤。
第十三章 動態(tài)鏈接庫
在Windows應(yīng)用程序中使用動態(tài)鏈接庫有很多的好處。最主要的一點說是它可以使得多個應(yīng)用程序共享一段代碼,從而可以大幅度的降低應(yīng)用程序的資源開 銷,同時很縮小了應(yīng)用程序的最終執(zhí)行代碼的大小。此外,通過使用動態(tài)鏈接庫,我們可以把一些常規(guī)的例程獨立出來,有效的避免了不必要的重復(fù)開發(fā),并且,由 于應(yīng)用程序使用了動態(tài)鏈接的方式,還可以在不需重新改寫甚至編譯應(yīng)用程序的基礎(chǔ)上更新應(yīng)用程序的某些組件。
本章介紹Visual C++ 5.0中的動態(tài)鏈接庫的創(chuàng)建和使用。這些內(nèi)容包括
為什么要使用DLL
不同的DLL類型之間的選擇
在程序中創(chuàng)建和使用DLL
使用DLL擴(kuò)展MFC
本章的重點在講述Win32 DLL和基于MFC的常規(guī)DLL,對于MFC擴(kuò)展DLL僅給了一個很簡單的例子,更深入的資料請參閱Visual C++的聯(lián)機(jī)文檔。
第一節(jié) 概述
動態(tài)鏈接庫(DLL,Dynamic-Link Library)也是一種可執(zhí)行文件,只不過它不能象普通的EXE文件那樣可以直接運行,而是用來為其它可執(zhí)行文件(包括EXE文件和其它DLL)提供共 享函數(shù)庫。使用DLL的應(yīng)用程序可以調(diào)用DLL中的導(dǎo)出函數(shù)(imported function),不過在應(yīng)用程序本身的執(zhí)行代碼中并不包含這些函數(shù)的執(zhí)行代碼,它們經(jīng)過編譯和鏈接之后,獨立的保存在DLL中。這是一種和過去常用的 靜態(tài)鏈接不同的方式,使用靜態(tài)鏈接庫(static link library)的應(yīng)用程序從函數(shù)庫得到所引用的函數(shù)的執(zhí)行代碼,然后把執(zhí)行代碼放進(jìn)自身的執(zhí)行文件中,這樣,應(yīng)用程序在運行時就可以不再需要靜態(tài)函數(shù)庫 的支持。而使用DLL的應(yīng)用程序只包括了用于從DLL中定位所引用的函數(shù)的信息,而沒有函數(shù)具體實現(xiàn),要等到程序運行時才從DLL中獲得函數(shù)的實現(xiàn)代碼。 顯然,使用了DLL的應(yīng)用程序在運行時必須要有相應(yīng)的DLL的支持。
DLL在Windows編程中得到了廣泛的應(yīng)用。Windows應(yīng)用 程序的基礎(chǔ):Windows API函數(shù)中的相當(dāng)部分就是由一組DLL所提供的,這些DLL從安裝Windows起就存在于系統(tǒng)中了。盡管在前面的幾章中我們沒有明確的提到,但事實上 我們早就在使用DLL進(jìn)行編程了,只不過,所使用的DLL都是現(xiàn)成的,并且所有調(diào)用DLL的操作都由Visual C++的編譯和鏈接程序替我們完成了。
本章將詳細(xì)的介紹如何創(chuàng)建自己的DLL和如何使用自己創(chuàng)建的DLL進(jìn)行編程。
為什么要使用DLL呢?這當(dāng)然是因為與傳統(tǒng)的靜態(tài)鏈庫相比,使用DLL有著更多的優(yōu)勢:
使用DLL提供了一種共享數(shù)據(jù)和代碼的方便途徑。并且,由于多個應(yīng)用程序可以共享同一個DLL中的函數(shù),因此,使用DLL可以顯著的節(jié)省磁盤空間。尤其 對于Windows應(yīng)用程序,有很多的操作都是“標(biāo)準(zhǔn)化”了的,如果使用傳統(tǒng)的靜態(tài)鏈接,每一個需要完成這些操作的應(yīng)用程序都必須在自己的執(zhí)行文件中包括 相同的執(zhí)行代碼,這不但使單個的應(yīng)用程序變得更長,也浪費了磁盤空間。
由于相同的原因,多個應(yīng)用程序還可以同時共享DLL在內(nèi)存中的同一份拷貝,這樣就有效的節(jié)省了應(yīng)用程序所占用的內(nèi)存資源,減少了頻繁的內(nèi)存交換,從而提高了應(yīng)用程序的執(zhí)行效率。
由于DLL是獨立于可執(zhí)行文件的,因此,如果需要向DLL中增加新的函數(shù)或是增強現(xiàn)有函數(shù)的功能,只要原有函數(shù)的參數(shù)和返回值等屬性不變,那么,所有使 用該DLL的原有應(yīng)用程序都可以在升級后的DLL的支持下運行,而不需要重新編譯。這就極大的方便了應(yīng)用程序的升級和售后支持。
DLL除了包括函數(shù)的執(zhí)行代碼以外,還可以只包括如圖標(biāo)、位圖、字符串和對話框之類的資源,因此可以把應(yīng)用程序所使用的資源獨立出來做成DLL。對于一些常用的資源,把它們做到DLL中后,就可為多個應(yīng)用程序所共享。
便于建立多語言的應(yīng)用程序。我們可以把多語言應(yīng)用程序中所使用的與語言相關(guān)的函數(shù)做到DLL中,只要不同語言的應(yīng)用程序所調(diào)用的函數(shù)都具有相同的接口,這樣就可以通過簡單地更換DLL來實現(xiàn)多語言支持。
然而,使用DLL也有其不足之處。最典型的就是應(yīng)用程序在運行時必須要有相應(yīng)的DLL的支持。另外,使用DLL也增大了程序運行的開銷,好在這種額外的開銷對于大多數(shù)應(yīng)用程序的影響并不明顯,我們也只是在某些對運行速度要求苛刻的特殊場合,才不得不考慮這一點。
Visual C++ 5.0支持多種DLL,包括:
非MFC DLL
靜態(tài)鏈接到MFC的常規(guī)DLL
動態(tài)鏈接到MFC的常規(guī)DLL
MFC擴(kuò)展DLL
其中非MFC DLL(non-MFC DLL)內(nèi)部不使用MFC,調(diào)用非MFC DLL提供的導(dǎo)出函數(shù)的可執(zhí)行程序可以使用MFC,也可以不使用MFC。一般來說,非MFC DLL的導(dǎo)出函數(shù)都使用標(biāo)準(zhǔn)的C接口(standard C interface)。
其余三種DLL的內(nèi)部都使用了MFC。顧名思義,靜態(tài)鏈接到MFC的常規(guī)DLL(regular DLL statically linking to MFC)和動態(tài)鏈接到MFC的常規(guī)DLL(regular DLL dynamically linking to MFC)的區(qū)別在于一個使用的是MFC的靜態(tài)鏈接庫,而另一個使用的是MFC的DLL。這和一般的MFC應(yīng)用程序的情況是很類似的。
MFC擴(kuò)展DLL一般用來提供派生于MFC的可重用的類,以擴(kuò)展已有的MFC類庫的功能。MFC擴(kuò)展DLL使用MFC的動態(tài)鏈接版本。只有使用MFC動態(tài) 鏈接的可執(zhí)行程序(無論是EXE還是DLL)才能訪問MFC擴(kuò)展DLL。MFC擴(kuò)展DLL的另一個有用的功能是它可以在應(yīng)用程序和它所加載的MFC擴(kuò)展 DLL之間傳遞MFC和MFC派生對象的指針。在其它情況下,這樣做是可能導(dǎo)致問題的。
注意:
只有Visual C++ 5.0的專業(yè)版和企業(yè)版才支持到MFC的靜態(tài)鏈接。
靜態(tài)鏈接到MFC的常規(guī)DLL過去的USRDLL有著相同的特性,同樣的,MFC擴(kuò)展DLL和過去的AFXDLL有著相同的特性。在Visual C++ 5.0中已不再使用USRDLL和AFXDLL這兩個術(shù)語。
如何選擇所應(yīng)該使用的DLL的類型呢?我們可以從以下幾個方面來考慮:
相比使用了MFC的DLL而言,非MFC DLL顯得更為短小精悍。因此,如果DLL不需要使用MFC,那么使用非MFC DLL是一個很好的選擇,它將顯著地節(jié)省磁盤和內(nèi)存空間。同時,無論應(yīng)用程序是否使用了MFC,都可以調(diào)用非MFC DLL中所導(dǎo)出的函數(shù)。
如果需要創(chuàng)建使用了MFC的DLL,并希望MFC和非MFC應(yīng)用程序都能使用所創(chuàng)建的DLL,那么可以選擇的范圍包括靜態(tài)鏈接到MFC的常規(guī)DLL和動 態(tài)鏈接到MFC的常規(guī)DLL。動態(tài)鏈接到MFC的常規(guī)DLL比較短小,因此可以節(jié)省磁盤和內(nèi)存,但是,在分發(fā)動態(tài)鏈接到MFC的常規(guī)DLL時,必須同時分 發(fā)MFC的支持DLL,如MFCx0.DLL和MSVCRT.DLL等。而使用靜態(tài)鏈接到MFC的常規(guī)DLL則不存在這種問題。
如果希望在DLL中實現(xiàn)從MFC派生的可重用的類,或者是希望在應(yīng)用程序和DLL之間傳遞MFC的派生對象時,必須選擇MFC擴(kuò)展DLL。
第二節(jié) 創(chuàng)建和使用動態(tài)鏈接庫
本節(jié)以非MFC DLL為例來講解DLL的結(jié)構(gòu)和導(dǎo)出方法,并介紹創(chuàng)建和使用DLL的方法和步驟。
13.2.1 DLL的結(jié)構(gòu)和導(dǎo)出方式
DLL文件和EXE文件都屬于可執(zhí)行文件,不同的是DLL文件包括了一個導(dǎo)出表,導(dǎo)出表中給出了可以從DLL中導(dǎo)出的所有函數(shù)的名字。外部可執(zhí)行程序只能訪問包括在DLL的導(dǎo)出表中的函數(shù),DLL中的其它函數(shù)是私有的,不能為外部可執(zhí)行程序所訪問。
可以使用Visual C++提供的DUMPBIN實用程序(可以在DevStudio\VC\bin目錄下找到這個工具)來查看一個DLL文件的結(jié)構(gòu)。舉一個例子,如果需要查 看DLL文件msgbox.dll(我們將在本小節(jié)的后續(xù)內(nèi)容中創(chuàng)建該DLL)的導(dǎo)出表,可以在命令提示符下鍵入下面的命令:
>dumpbin /exports msgbox.dll
運行結(jié)果如下:
Microsoft (R) COFF Binary File Dumper Version 5.00.7022
Copyright (C) Microsoft Corp 1992-1997. All rights reserved.
Dump of file msgbox.dll
File Type: DLL
Section contains the following Exports for MSGBOX.dll
0 characteristics
351643C3 time date stamp Mon Mar 23 19:13:07 1998
0.00 version
1 ordinal base
1 number of functions
1 number of names
ordinal hint name
1 0 MsgBox (00001000)
Summary
7000 .data
1000 .idata
2000 .rdata
2000 .reloc
17000 .text
由上面的結(jié)果得知,msgbox.dll中僅包括了一個導(dǎo)出函數(shù)MsgBox()。
注意:
僅僅知道導(dǎo)出函數(shù)的名稱并不足以從DLL中導(dǎo)出該函數(shù)。若在應(yīng)用程序中使用顯式鏈接(link explicitly),至少還應(yīng)該知道導(dǎo)出函數(shù)的返回值的類型以及所傳遞給導(dǎo)出函數(shù)的參數(shù)的個數(shù)、順序和類型;若使用隱含鏈接(link implicitly),必須有包括導(dǎo)出函數(shù)(或類)的定義的頭文件(.H文件)和引入庫(import library,.LIB文件),這些文件是由DLL的創(chuàng)建者所提供的。關(guān)于顯式鏈接和隱含鏈接,將在本章的“13.2.2 鏈接應(yīng)用程序到DLL”小節(jié)中講述。
從DLL中導(dǎo)出函數(shù)有兩種方法:
在創(chuàng)建DLL時使用模塊定義(module DEFinition,.DEF)文件。
在定義函數(shù)時使用關(guān)鍵字__declspec(dllexport)。
下面我們通過一個簡單的例子來分別說明兩種方法的使用。在這個例子中,我們將創(chuàng)建一個只包括一個函數(shù)MsgBox()的DLL,函數(shù)MsgBox()用 來顯示一個消息框,它和Win32 API函數(shù)MessageBox()的功能是一樣,只不過在函數(shù)MsgBox()中,不需要指定消息的父窗口,而且可以缺省其它所有的參數(shù)。
(1) 使用模塊定義文件
模塊定義文件是一個文本文件,它包括了一系列的模塊語句,這些語句用來描述DLL的各種屬性,典型的,模塊語句定義了DLL中所導(dǎo)出的函數(shù)的名稱和順序值。
在講解模塊定義文件之前,我們先創(chuàng)建一個Win32 Dynamic-Link Library工程。
1. 在Microsoft Developer Studio中選擇File菜單下的New命令,在Projects選項卡中選擇Win32 Dynamic-Link Library,并為工程取一個名字,如msgbox。單擊OK后,Visual C++創(chuàng)建一個Win32 DLL的空白工程,必須手動的將所需要的文件添加到工程中。
2. 單擊Project菜單下的Add To Project子菜單下的New命令,在Files選項卡中選擇Text File,在File文本框中輸入DEF文件名,如msgbox.def。
3. 雙擊Workspace窗口的FileView選項卡中的msgbox.def節(jié)點,在msgbox.def文件中輸入下面的內(nèi)容:
LIBRARY MSGBOX
DESCRIPTION "一個DLL的簡單例子"
EXPORTS
MsgBox @1
在DEF文件中的第一條語句必須是LIBRARY語句,該語句表明該DEF文件屬于一個DLL,在LIBRARY之后是DLL的名稱,這個名稱在鏈接時將放到DLL的引入庫中。
EXPORTS語句下列出了DLL的所有導(dǎo)出函數(shù)以及它們的順序值。函數(shù)的順序值不是必須的,在指定導(dǎo)出函數(shù)的順序值時,我們在函數(shù)名后跟上一個@符號 和一個數(shù)字,該數(shù)字即導(dǎo)出函數(shù)的順序值。如果在DEF中指定了順序值,它必須不小于1,且不大于DLL中所有導(dǎo)出函數(shù)的數(shù)目。
DESCRIPTION語句是可選的,它簡單的說明了DLL的用途。
4. 下一步是向工程中添加一個頭文件,它定義了DLL中的函數(shù)的返回值的類型和參數(shù)的個數(shù)、順序和類型。
單擊菜單項Project|Add To Project|New...,在Files選項卡下選擇C/C++ Header File,在File文本框中指定頭文件名,如msgbox.h(可以省略后綴名.h)。
在頭文件中輸入如下的內(nèi)容:
#include
extern "C" int MsgBox(
// 消息框的文本
LPCTSTR lpText="雖然這個例子有一些幼稚,但它工作得非常的好!",
// 消息框的標(biāo)題
LPCTSTR lpCaption="一個簡單的例子",
// 消息框的樣式
UINT uType=MB_OK);
請注意函數(shù)定義前的關(guān)鍵字extern "C",這是由于我們使用了C++語言來開發(fā)DLL,為了使C語言模塊能夠訪問該導(dǎo)出函數(shù),我們應(yīng)該使用C鏈接來代替C++鏈接。否則,C++編譯器將使 用C++的類型安全命名和調(diào)用協(xié)議,這在使用C調(diào)用該函數(shù)時就會遇上問題。在本例中并不需要考慮到這個問題,因為我們在開發(fā)DLL和應(yīng)用程序時都是使用C ++,但我們?nèi)匀粡娏医ㄗh使用extern "C",以保證在使用C編寫的程序調(diào)用該DLL的導(dǎo)出函數(shù)不會遇上麻煩,在本章后面的內(nèi)容中我們還會討論到這個問題。
5. 下面要做的事是向工程中添加一個C++源文件,在該文件中實現(xiàn)函數(shù)MsgBox()。
仿照上面的過程,單擊菜單項Project|Add To Project|New...,在Files選項卡下選擇C++ Source File,在File文本框中指定源文件名,如msgbox.cpp。
在msgbox.cpp文件中添加如下的代碼:
#include "test1.h"
int MsgBox(LPCTSTR lpText,
LPCTSTR lpCaption,
UINT uType)
{
return MessageBox(NULL,lpText,lpCaption,uType);
}
編譯該工程,在Debug目錄下生成文件msgbox.lib和msgbox.dll。
在“13.2.2 鏈接應(yīng)用程序到DLL”小節(jié)中將講述如何使用在本節(jié)中所創(chuàng)建的DLL:msgbox.dll。
(2) 使用關(guān)鍵字__declspec(dllexport)
從DLL中導(dǎo)出文件的另一種方法是在定義函數(shù)時使用__declspec(dllexport)關(guān)鍵字。這種方法不需要使用DEF文件。
仍使用前面的例子,在工程中刪除msgbox.def文件,將msgbox.h文件修改如下:
#include
extern "C" __declspec(dllexport) int MsgBox(
// 消息框的文本
LPCTSTR lpText="雖然這個例子有一些幼稚,但它工作得非常的好!",
// 消息框的標(biāo)題
LPCTSTR lpCaption="一個簡單的例子",
// 消息框的樣式
UINT uType=MB_OK);
msgbox.cpp文件并不需要做任何修改,重新編譯該工程,在Debug目錄下仍生成兩個文件msgbox.lib和msgbox.dll。
在下一小節(jié)“13.2.2 鏈接應(yīng)用程序到DLL”中講述了如何在應(yīng)用程序中使用所創(chuàng)建的DLL:msgbox.dll的導(dǎo)出函數(shù)MsgBox()。
使用__declspec(dllexport)從DLL中導(dǎo)出類的語法如下:
class __declspec(dllexport) CDemoClass
{
...
}
注意:
如果在使用__declspec(dllexport)的同時指定了調(diào)用協(xié)議關(guān)鍵字,則必須將__declspec(dllexport)關(guān)鍵字放在調(diào)用協(xié)議關(guān)鍵字的左邊。如:
int __declspec(dllexport) __cdacl MyFunc();
在32位版本的Visual C++中,__declspec(dllexport)和__declspec(dllimport)代替了16版本中使用的__export關(guān)鍵字。因 此,在將16位的DLL源代碼移植到Win32平臺時,需要把每一處__export替換為__declsped(dllexport)。
如何從這兩種導(dǎo)出函數(shù)的方法中作出選擇,可以從下面的幾個方面考慮:
如果需要使用導(dǎo)出順序值(export ordinal value),那么應(yīng)該使用DEF文件來導(dǎo)出函數(shù)。只在使用DEF文件導(dǎo)出函數(shù)才能指定導(dǎo)出函數(shù)的順序值。使用順序值的一個好處是當(dāng)向DLL中添加新的函 數(shù)時,只要新的導(dǎo)出函數(shù)的順序值大于原有的導(dǎo)出函數(shù),就沒有必要重新鏈接使用隱含鏈接的應(yīng)用程序。相反,如果使用__declspec (dllexport)來導(dǎo)出函數(shù),如果向DLL中添加了新的函數(shù),使用隱含鏈接的應(yīng)用程序有可以需要重新編譯和鏈接。
使用DEF文件來導(dǎo)出函數(shù),可以創(chuàng)建具有NONAME屬性的DLL。具有NONAME屬性的DLL在導(dǎo)出表中僅包含了導(dǎo)出函數(shù)的順序值,這種類型的DLL在包括有大量的導(dǎo)出函數(shù)時,其文件長度要小于通常的DLL。
使用DEF文件從C++文件導(dǎo)出函數(shù),應(yīng)該在定義函數(shù)時使用extern "C"或者在DEF文件中指定導(dǎo)出函數(shù)的decorated name。否則,由于編譯器所產(chǎn)生的decorated name是基于特定編譯器的,鏈接到該DLL的應(yīng)用程序也必須使用創(chuàng)建DLL的同一版本的Visual C++來編譯和鏈接。
由于使用__declspec(dllexport)關(guān)鍵字導(dǎo)出函數(shù)不需要編寫DEF文件,因此,如果編寫的DLL只供自己使用,使用__declspec(dllexport)較為簡單。
注意:
MFC本身使用了DEF文件從MFCx0.DLL中導(dǎo)出函數(shù)和類。
13.2.2 鏈接應(yīng)用程序到DLL
同樣,鏈接應(yīng)用程序到DLL也有兩種方法:
隱含鏈接
顯式鏈接
隱含鏈接有時又稱為靜態(tài)加載。如果應(yīng)用程序使用了隱含鏈接,操作系統(tǒng)在加載應(yīng)用程序的同時加載應(yīng)用程序所使用的DLL。顯式鏈接有時又稱為動態(tài)加載。使 用動態(tài)加載的應(yīng)用程序必須在代碼中明確的加載所使用的DLL,并使用指針來調(diào)用DLL中的導(dǎo)出函數(shù),在使用完畢之后,應(yīng)用程序必須卸載所使用的DLL。
同一個DLL可以被應(yīng)用程序隱含鏈接,也可以被顯式鏈接,這取決于應(yīng)用程序的目的和實現(xiàn)。
下面我們在分別講述兩種不同的鏈接方式之后再作對比。
(1) 使用隱含鏈接
在使用隱含鏈接除了需要相應(yīng)的DLL文件外,還必須具備如下的條件:
一個包括導(dǎo)出的函數(shù)或C++類的頭文件
一個輸入庫文件(.LIB文件)
通常情況下,我們需要從DLL的提供者那里得到上面的文件。輸入庫文件是在DLL文件被鏈接時由鏈接程序生成的。
在“13.2.1 DLL的結(jié)構(gòu)和導(dǎo)出方式”中所創(chuàng)建的DLL:msgbox.dll所對應(yīng)的頭文件msgbox.h如下:
#include
extern "C" __declspec(dllimport) int MsgBox(
// 消息框的文本
LPCTSTR lpText="雖然這個例子有一些幼稚,但它工作得非常的好!",
// 消息框的標(biāo)題
LPCTSTR lpCaption="一個簡單的例子",
// 消息框的樣式
UINT uType=MB_OK);
需要注意的是,這個msgbox.h文件和創(chuàng)建DLL時所使用msgbox.h是不同的,唯一的差別在于,創(chuàng)建DLL時的msgbox.h中使用的是 __declspec(dllexport)關(guān)鍵字,而供應(yīng)用程序所使用的msgbox.h中使用的是__declspec(dllimport)關(guān)鍵 字。無論創(chuàng)建DLL時使用的是DEF文件還是__declspec(dllexport)關(guān)鍵字,均可使用__declspec(dllimport)關(guān) 鍵字從DLL中引入函數(shù)。引入函數(shù)時也可以省略__declspec(dllimport)關(guān)鍵字,但是使用它可以使編譯器生成效率更高的代碼。
注意:
如果需要引入的是DLL中的公用數(shù)據(jù)和對象,則必須使用__declspec(dllimport)關(guān)鍵字。
現(xiàn)在使用Microsoft Developer Studio創(chuàng)建一個Win32 Application工程,命名為tester。向工程中添加一個C++源文件,如tester.cpp。在tester.cpp文件中輸入下面的代碼:
#include "msgbox.h" // 應(yīng)將msgbox.h文件拷貝到工程tester的目錄下。
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
return MsgBox();
}
在上面的代碼中,MsgBox()函數(shù)的所有參數(shù)都使用了缺省值。
?
評論
查看更多