日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

基于VC++6.0的DLL开发

發(fā)布時間:2025/3/15 c/c++ 12 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基于VC++6.0的DLL开发 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

原文地址:http://blog.163.com/twnhr@126/blog/static/78927547200910254346804/


基于VC++6.0的DLL開發(fā)
最近在開發(fā)一個基于網(wǎng)絡(luò)的模塊,目標(biāo)是將這個在CAsyncSocket基礎(chǔ)上開發(fā)的擴展類,進行有效的封裝,向應(yīng)用程序提供模塊化的功能,之間查詢了很多資料。了解DLL的開發(fā)。

下面是一篇我覺得不錯的文章,我引用在這里,是向大家起一個拋磚引玉的作用。

總體感覺這篇文章對DLL的整個開發(fā)過程都作了比較通俗易懂的描述。

一、前言

  自從微軟推出16位的Windows操作系統(tǒng)起,此后每種版本的Windows操作系統(tǒng)都非常依賴于動態(tài)鏈接庫(DLL)中的函數(shù)和數(shù)據(jù),實際上Windows操作系統(tǒng)中幾乎所有的內(nèi)容都由DLL以一種或另外一種形式代表著,例如顯示的字體和圖標(biāo)存儲在GDI DLL中、顯示W(wǎng)indows桌面和處理用戶的輸入所需要的代碼被存儲在一個User DLL中、Windows編程所需要的大量的API函數(shù)也被包含在Kernel DLL中。

  在Windows操作系統(tǒng)中使用DLL有很多優(yōu)點,最主要的一點是多個應(yīng)用程序、甚至是不同語言編寫的應(yīng)用程序可以共享一個DLL文件,真正實現(xiàn)了資源"共享",大大縮小了應(yīng)用程序的執(zhí)行代碼,更加有效的利用了內(nèi)存;使用DLL的另一個優(yōu)點是DLL文件作為一個單獨的程序模塊,封裝性、獨立性好,在軟件需要升級的時候,開發(fā)人員只需要修改相應(yīng)的DLL文件就可以了,而且,當(dāng)DLL中的函數(shù)改變后,只要不是參數(shù)的改變,程序代碼并不需要重新編譯。這在編程時十分有用,大大提高了軟件開發(fā)和維護的效率。

  既然DLL那么重要,所以搞清楚什么是DLL、如何在Windows操作系統(tǒng)中開發(fā)使用DLL是程序開發(fā)人員不得不解決的一個問題。本文針對這些問題,通過一個簡單的例子,即在一個DLL中實現(xiàn)比較最大、最小整數(shù)這兩個簡單函數(shù),全面地解析了在Visual C++編譯環(huán)境下編程實現(xiàn)DLL的過程,文章中所用到的程序代碼在Windows98系統(tǒng)、Visual C++6.0編譯環(huán)境下通過。

  二、DLL的概念

  DLL是建立在客戶/服務(wù)器通信的概念上,包含若干函數(shù)、類或資源的庫文件,函數(shù)和數(shù)據(jù)被存儲在一個DLL(服務(wù)器)上并由一個或多個客戶導(dǎo)出而使用,這些客戶可以是應(yīng)用程序或者是其它的DLL。DLL庫不同于靜態(tài)庫,在靜態(tài)庫情況下,函數(shù)和數(shù)據(jù)被編譯進一個二進制文件(通常擴展名為*.LIB),Visual C++的編譯器在處理程序代碼時將從靜態(tài)庫中恢復(fù)這些函數(shù)和數(shù)據(jù)并把他們和應(yīng)用程序中的其他模塊組合在一起生成可執(zhí)行文件。這個過程稱為"靜態(tài)鏈接",此時因為應(yīng)用程序所需的全部內(nèi)容都是從庫中復(fù)制了出來,所以靜態(tài)庫本身并不需要與可執(zhí)行文件一起發(fā)行。

  在動態(tài)庫的情況下,有兩個文件,一個是引入庫(.LIB)文件,一個是DLL文件,引入庫文件包含被DLL導(dǎo)出的函數(shù)的名稱和位置,DLL包含實際的函數(shù)和數(shù)據(jù),應(yīng)用程序使用LIB文件鏈接到所需要使用的DLL文件,庫中的函數(shù)和數(shù)據(jù)并不復(fù)制到可執(zhí)行文件中,因此在應(yīng)用程序的可執(zhí)行文件中,存放的不是被調(diào)用的函數(shù)代碼,而是DLL中所要調(diào)用的函數(shù)的內(nèi)存地址,這樣當(dāng)一個或多個應(yīng)用程序運行是再把程序代碼和被調(diào)用的函數(shù)代碼鏈接起來,從而節(jié)省了內(nèi)存資源。從上面的說明可以看出,DLL和.LIB文件必須隨應(yīng)用程序一起發(fā)行,否則應(yīng)用程序?qū)a(chǎn)生錯誤。

  微軟的Visual C++支持三種DLL,它們分別是Non-MFC Dll(非MFC動態(tài)庫)、Regular Dll(常規(guī)DLL)、Extension Dll(擴展DLL)。
Non-MFC DLL: 指的是不用MFC的類庫結(jié)構(gòu),直接用C語言寫的DLL,其導(dǎo)出的函數(shù)是標(biāo)準(zhǔn)的C接口,能被非MFC或MFC編寫的應(yīng)用程序所調(diào)用。
Regular DLL: 和下述的Extension Dlls一樣,是用MFC類庫編寫的,它的一個明顯的特點是在源文件里有一個繼承CWinApp的類(注意:此類DLL雖然從CWinApp派生,但沒有消息循環(huán)),被導(dǎo)出的函數(shù)是C函數(shù)、C++類或者C++成員函數(shù)(注意不要把術(shù)語C++類與MFC的微軟基礎(chǔ)C++類相混淆),調(diào)用常規(guī)DLL的應(yīng)用程序不必是MFC應(yīng)用程序,只要是能調(diào)用類C函數(shù)的應(yīng)用程序就可以,它們可以是在Visual C++、Dephi、Visual Basic、Borland C等編譯環(huán)境下利用DLL開發(fā)應(yīng)用程序。

   常規(guī)DLL又可細(xì)分成靜態(tài)鏈接到MFC和動態(tài)鏈接到MFC上的,這兩種常規(guī)DLL的區(qū)別將在下面介紹。與常規(guī)DLL相比,使用擴展DLL用于導(dǎo)出增強MFC基礎(chǔ)類的函數(shù)或子類,用這種類型的動態(tài)鏈接庫,可以用來輸出一個從MFC所繼承下來的類。

   擴展DLL:是使用MFC的動態(tài)鏈接版本所創(chuàng)建的,并且它只被用MFC類庫所編寫的應(yīng)用程序所調(diào)用。例如你已經(jīng)創(chuàng)建了一個從MFC的CtoolBar類的派生類用于創(chuàng)建一個新的工具欄,為了導(dǎo)出這個類,你必須把它放到一個MFC擴展的DLL中。擴展DLL 和常規(guī)DLL不一樣,它沒有一個從CWinApp繼承而來的類的對象,所以,開發(fā)人員必須在DLL中的DllMain函數(shù)添加初始化代碼和結(jié)束代碼。

三、動態(tài)鏈接庫的創(chuàng)建

  在Visual C++6.0開發(fā)環(huán)境下,打開FileNewProject選項,可以選擇Win32 Dynamic-Link Library或MFC AppWizard[dll]來以不同的方式來創(chuàng)建Non-MFC Dll、Regular Dll、Extension Dll等不同種類的動態(tài)鏈接庫。

  1. Win32 Dynamic-Link Library方式創(chuàng)建Non-MFC DLL動態(tài)鏈接庫

  每一個DLL必須有一個入口點,這就象我們用C編寫的應(yīng)用程序一樣,必須有一個WINMAIN函數(shù)一樣。在Non-MFC DLL中DllMain是一個缺省的入口函數(shù),你不需要編寫自己的DLL入口函數(shù),用這個缺省的入口函數(shù)就能使動態(tài)鏈接庫被調(diào)用時得到正確的初始化。如果應(yīng)用程序的DLL需要分配額外的內(nèi)存或資源時,或者說需要對每個進程或線程初始化和清除操作時,需要在相應(yīng)的DLL工程的.CPP文件中對DllMain()函數(shù)按照下面的格式書寫。  


BOOL APIENTRY DllMain(HANDLE 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: .......
? ? }
? return TRUE;
} ?

  參數(shù)中,hMoudle是動態(tài)庫被調(diào)用時所傳遞來的一個指向自己的句柄(實際上,它是指向_DGROUP段的一個選擇符);ul_reason_for_call是一個說明動態(tài)庫被調(diào)原因的標(biāo)志,當(dāng)進程或線程裝入或卸載動態(tài)鏈接庫的時候,操作系統(tǒng)調(diào)用入口函數(shù),并說明動態(tài)鏈接庫被調(diào)用的原因,它所有的可能值為:DLL_PROCESS_ATTACH: 進程被調(diào)用、DLL_THREAD_ATTACH: 線程被調(diào)用、DLL_PROCESS_DETACH: 進程被停止、DLL_THREAD_DETACH: 線程被停止;lpReserved為保留參數(shù)。到此為止,DLL的入口函數(shù)已經(jīng)寫了,剩下部分的實現(xiàn)也不難,你可以在DLL工程中加入你所想要輸出的函數(shù)或變量了。

  我們已經(jīng)知道DLL是包含若干個函數(shù)的庫文件,應(yīng)用程序使用DLL中的函數(shù)之前,應(yīng)該先導(dǎo)出這些函數(shù),以便供給應(yīng)用程序使用。要導(dǎo)出這些函數(shù)有兩種方法,一是在定義函數(shù)時使用導(dǎo)出關(guān)鍵字_declspec(dllexport),另外一種方法是在創(chuàng)建DLL文件時使用模塊定義文件.Def。需要讀者注意的是在使用第一種方法的時候,不能使用DEF文件。下面通過兩個例子來說明如何使用這兩種方法創(chuàng)建DLL文件。

  1)使用導(dǎo)出函數(shù)關(guān)鍵字_declspec(dllexport)創(chuàng)建MyDll.dll,該動態(tài)鏈接庫中有兩個函數(shù),分別用來實現(xiàn)得到兩個數(shù)的最大和最小數(shù)。在MyDll.h和MyDLL.cpp文件中分別輸入如下原代碼:  

//MyDLL.h
extern "C" _declspec(dllexport) int Max(int a, int b);
extern "C" _declspec(dllexport) int Min(int a, int b);

//MyDll.cpp
#include
#include"MyDll.h"
int Max(int a, int b)
{
? ?if(a>=b)
? ? return a;
? ?else
? ? return b;
}

int Min(int a, int b)
{
? if(a>=b)
? ?return b;
? else
? ?return a;
} ?

  該動態(tài)鏈接庫編譯成功后,打開MyDll工程中的debug目錄,可以看到MyDll.dll、MyDll.lib兩個文件。LIB文件中包含DLL文件名和DLL文件中的函數(shù)名等,該LIB文件只是對應(yīng)該DLL文件的"映像文件",與DLL文件中,LIB文件的長度要小的多,在進行隱式鏈接DLL時要用到它。讀者可能已經(jīng)注意到在MyDll.h中有關(guān)鍵字"extern C",它可以使其他編程語言訪問你編寫的DLL中的函數(shù)。

  2)用.def文件創(chuàng)建工程MyDll

  為了用.def文件創(chuàng)建DLL,請先刪除上個例子創(chuàng)建的工程中的MyDll.h文件,保留MyDll.cpp并在該文件頭刪除#include MyDll.h語句,同時往該工程中加入一個文本文件,命名為MyDll.def,再在該文件中加入如下代碼:

LIBRARY MyDll
EXPORTS
Max
Min

  其中LIBRARY語句說明該def文件是屬于相應(yīng)DLL的,EXPORTS語句下列出要導(dǎo)出的函數(shù)名稱。我們可以在.def文件中的導(dǎo)出函數(shù)后加@n,如Max@1,Min@2,表示要導(dǎo)出的函數(shù)順序號,在進行顯式連時可以用到它。該DLL編譯成功后,打開工程中的Debug目錄,同樣也會看到MyDll.dll和MyDll.lib文件。

  2.MFC AppWizard[dll]方式生成常規(guī)/擴展DLL

  在MFC AppWizard[dll]下生成DLL文件又有三種方式,在創(chuàng)建DLL是,要根據(jù)實際情況選擇創(chuàng)建DLL的方式。一種是常規(guī)DLL靜態(tài)鏈接到MFC,另一種是常規(guī)DLL動態(tài)鏈接到MFC。兩者的區(qū)別是:前者使用的是MFC的靜態(tài)鏈接庫,生成的DLL文件長度大,一般不使用這種方式,后者使用MFC的動態(tài)鏈接庫,生成的DLL文件長度小;動態(tài)鏈接到MFC的規(guī)則DLL所有輸出的函數(shù)應(yīng)該以如下語句開始: ?  

AFX_MANAGE_STATE(AfxGetStaticModuleState( )) //此語句用來正確地切換MFC模塊狀態(tài) ?

  最后一種是MFC擴展DLL,這種DLL特點是用來建立MFC的派生類,Dll只被用MFC類庫所編寫的應(yīng)用程序所調(diào)用。前面我們已經(jīng)介紹過,Extension DLLs 和Regular DLLs不一樣,它沒有一個從CWinApp繼承而來的類的對象,編譯器默認(rèn)了一個DLL入口函數(shù)DLLMain()作為對DLL的初始化,你可以在此函數(shù)中實現(xiàn)初始化,代碼如下:  

BOOL WINAPI APIENTRY DLLMain(HINSTANCE hinstDll,DWORD reason ,LPVOID flmpload)
{
switch(reason)
{
? ?……………//初始化代碼;
}
return true;
} ?

  參數(shù)hinstDll存放DLL的句柄,參數(shù)reason指明調(diào)用函數(shù)的原因,lpReserved是一個被系統(tǒng)所保留的參數(shù)。對于隱式鏈接是一個非零值,對于顯式鏈接值是零。

  在MFC下建立DLL文件,會自動生成def文件框架,其它與建立傳統(tǒng)的Non-MFC DLL沒有什么區(qū)別,只要在相應(yīng)的頭文件寫入關(guān)鍵字_declspec(dllexport)函數(shù)類型和函數(shù)名等,或在生成的def文件中EXPORTS下輸入函數(shù)名就可以了。需要注意的是在向其它開發(fā)人員分發(fā)MFC擴展DLL 時,不要忘記提供描述DLL中類的頭文件以及相應(yīng)的.LIB文件和DLL本身,此后開發(fā)人員就能充分利用你開發(fā)的擴展DLL了。

四、動態(tài)鏈接庫DLL的鏈接

  應(yīng)用程序使用DLL可以采用兩種方式:
一種是隱式鏈接,另一種是顯式鏈接。
在使用DLL之前首先要知道DLL中函數(shù)的結(jié)構(gòu)信息。Visual C++6.0在VCin目錄下提供了一個名為Dumpbin.exe的小程序,用它可以查看DLL文件中的函數(shù)結(jié)構(gòu)。
Windows系統(tǒng)將遵循下面的搜索順序來定位DLL:
? 1.包含EXE文件的目錄,
? 2.進程的當(dāng)前工作目錄,
? 3.Windows系統(tǒng)目錄,
? 4.Windows目錄,
? 5.列在Path環(huán)境變量中的一系列目錄。

  1.隱式鏈接

  隱式鏈接就是在程序開始執(zhí)行時就將DLL文件加載到應(yīng)用程序當(dāng)中。實現(xiàn)隱式鏈接很容易,只要將導(dǎo)入函數(shù)關(guān)鍵字_declspec(dllimport)函數(shù)名等寫到應(yīng)用程序相應(yīng)的頭文件中就可以了。下面的例子通過隱式鏈接調(diào)用MyDll.dll庫中的Min函數(shù)。首先生成一個項目為TestDll,在DllTest.h、DllTest.cpp文件中分別輸入如下代碼:  


//Dlltest.h
#pragma comment(lib,"MyDll.lib")
extern "C"_declspec(dllimport) int Max(int a,int b);
extern "C"_declspec(dllimport) int Min(int a,int b);

//TestDll.cpp
#include
#include"Dlltest.h"

void main()
{
int a;
a=min(8,10)
printf("比較的結(jié)果為%d ",a);
}   ?

  在創(chuàng)建DllTest.exe文件之前,要先將MyDll.dll和MyDll.lib拷貝到當(dāng)前工程所在的目錄下面,也可以拷貝到windows的System目錄下。如果DLL使用的是def文件,要刪除TestDll.h文件中關(guān)鍵字extern "C"。TestDll.h文件中的關(guān)鍵字Progam commit是要Visual C+的編譯器在link時,鏈接到MyDll.lib文件,當(dāng)然,開發(fā)人員也可以不使用#pragma comment(lib,"MyDll.lib")語句,而直接在工程的Setting->Link頁的Object/Moduls欄填入MyDll.lib既可。

  2.顯式鏈接

  顯式鏈接是應(yīng)用程序在執(zhí)行過程中隨時可以加載DLL文件,也可以隨時卸載DLL文件,這是隱式鏈接所無法作到的,所以顯式鏈接具有更好的靈活性,對于解釋性語言更為合適。不過實現(xiàn)顯式鏈接要麻煩一些。在應(yīng)用程序中用LoadLibrary或MFC提供的AfxLoadLibrary顯式的將自己所做的動態(tài)鏈接庫調(diào)進來,動態(tài)鏈接庫的文件名即是上述兩個函數(shù)的參數(shù),此后再用GetProcAddress()獲取想要引入的函數(shù)。自此,你就可以象使用如同在應(yīng)用程序自定義的函數(shù)一樣來調(diào)用此引入函數(shù)了。在應(yīng)用程序退出之前,應(yīng)該用FreeLibrary或MFC提供的AfxFreeLibrary釋放動態(tài)鏈接庫。下面是通過顯式鏈接調(diào)用DLL中的Max函數(shù)的例子。  

#include ?
#include

void main(void)
{
typedef int(*pMax)(int a,int b);
typedef int(*pMin)(int a,int b);
HINSTANCE hDLL;
PMax Max ;
HDLL=LoadLibrary("MyDll.dll");//加載動態(tài)鏈接庫MyDll.dll文件;
Max=(pMax)GetProcAddress(hDLL,"Max");
A=Max(5,8);
Printf("比較的結(jié)果為%d ",a);
FreeLibrary(hDLL);//卸載MyDll.dll文件;
} ?

  在上例中使用類型定義關(guān)鍵字typedef,定義指向和DLL中相同的函數(shù)原型指針,然后通過LoadLibray()將DLL加載到當(dāng)前的應(yīng)用程序中并返回當(dāng)前DLL文件的句柄,然后通過GetProcAddress()函數(shù)獲取導(dǎo)入到應(yīng)用程序中的函數(shù)指針,函數(shù)調(diào)用完畢后,使用FreeLibrary()卸載DLL文件。在編譯程序之前,首先要將DLL文件拷貝到工程所在的目錄或Windows系統(tǒng)目錄下。

  使用顯式鏈接應(yīng)用程序編譯時不需要使用相應(yīng)的Lib文件。另外,使用GetProcAddress()函數(shù)時,可以利用MAKEINTRESOURCE()函數(shù)直接使用DLL中函數(shù)出現(xiàn)的順序號,如將GetProcAddress(hDLL,"Min")改為GetProcAddress(hDLL, MAKEINTRESOURCE(2))(函數(shù)Min()在DLL中的順序號是2),這樣調(diào)用DLL中的函數(shù)速度很快,但是要記住函數(shù)的使用序號,否則會發(fā)生錯誤。


? ? DLL的顯式鏈接在某些時候比隱式鏈接具有更大的靈活性。比如,如果在運行時發(fā)現(xiàn)DLL無法找到,程序可以顯示一個錯誤信息并能繼續(xù)運行。當(dāng)你想為你的程序提供插件服務(wù)時,顯式鏈接也很有用處。

顯式鏈接到全局C/C++函數(shù)非常簡單。假設(shè)你想調(diào)用DLL中的一個函數(shù)ExportedFn,你可以像這樣很簡單地導(dǎo)出它:

extern "C" _declspec(dllexport)

void ExportedFn(int Param1, char* param2);

必須使用extern "C"鏈接標(biāo)記,否則C++編譯器會產(chǎn)生一個修飾過的函數(shù)名,這樣導(dǎo)出函數(shù)的名字將不再是ExportedFn,而是一個形如"??ExportedFn@QAEX”的名字。假設(shè)這個函數(shù)從DLL1.dll導(dǎo)出,那么客戶端可以像這樣調(diào)用這個函數(shù):

HMODULE hMod = LoadLibrary("Dll1.dll");

typedef void (*PExportedFn)(int, char*);

PExportedFn pfnEF = (PExportedFn)GetProcAdress("ExportedFn");

pfnEF(1, "SomeString");

如果你想導(dǎo)出并顯式鏈接一組C++成員函數(shù)又該怎么辦呢?這里有兩個問題。第一是C++成員函數(shù)名是經(jīng)過修飾的(即使指定extern "C"標(biāo)記也是這樣);第二是C++不允許將指向成員函數(shù)的指針轉(zhuǎn)換成其它類型。這兩個問題限制了C++類的顯式鏈接。下面介紹兩種方法來解決這個問題:①用虛函數(shù)表的方法,這也是COM使用的方法;②用GetProcAddress直接調(diào)用。我將以下面這個類為例進行講解:

class A

{

private:

int m_nNum; ? ?

public: ?

A();

A(int n);

virtual ~A();

void SetNum(int n);

int GetNum();

};

一.用虛函數(shù)表進行顯式鏈接

這個方法是COM的基礎(chǔ)。當(dāng)我們定義一組虛函數(shù)的時候,編譯器會創(chuàng)建一個虛函數(shù)表,將各虛函數(shù)的地址按聲明的順序放入其中。當(dāng)一個類對象被創(chuàng)建時,它的前四個字節(jié)是一個指針,指向這個虛函數(shù)表。如果我們將A的定義修改成這樣:

class A

{

private:

int m_nNum; ? ? ? ? ?

public: ? ? ? ?

A();

A(int n);

virtual ~A();

virtual void SetNum(int n);

virtual int GetNum();

};

那么一個虛函數(shù)表將被編譯器創(chuàng)建出來,其中包含三個函數(shù)的地址:析構(gòu)函數(shù),SetNum和GetNum。現(xiàn)在類對象要在dll中創(chuàng)建。既然我們要顯式鏈接,就需要一些全局導(dǎo)出函數(shù)來調(diào)用operator new以創(chuàng)建對象。因為A有兩種構(gòu)造函數(shù),所以我們定義兩個函數(shù)CreateObjectofA()和CreateObjectofA1(int)并將其導(dǎo)出。客戶可以這樣來使用類對象:

typedef A* (*PFNCreateA1)();
PFNCreateA1 pfnCreateA1 =
(PFNCreateA1)GetProcAddress(hMod, TEXT("CreateObjectofA1"));
A* a = (pfnCreateA1)();
a->SetNum(1);
_tprintf(TEXT("Value of m_nNum in a is %d
"),a->GetNum());
delete a;
要注意的是CreateObjectofA必須使用operator new來創(chuàng)建對象這樣客戶端才可以安全地調(diào)用operator delete來銷毀對象:

extern "C" __declspec(dllexport) A* CreateObjectofA1()

{

return new A();

}

這個方法的使用得用戶可以很容易地為你的程序制作插件。它的缺點是創(chuàng)建對象的內(nèi)存必須在dll中分配。

二.直接使用GetProcAddress進行顯式鏈接

這個方法的關(guān)鍵在于將GetProcAddress函數(shù)返回的FARPROC類型轉(zhuǎn)化為C++中指向成員函數(shù)的指針。幸運的是,通過C++的unio和模板機制,這個目標(biāo)可以很容易地實現(xiàn)。我們要做的只是定義如下的函數(shù):

template<class Src , class Dest>

Dest force_cast(Src src){

union{

? Dest d;

? Src s;

} convertor;

convertor.s = Src;

return convertor.d;

}

上面的函數(shù)允許我們在任何類型間進行轉(zhuǎn)換,比reinterpret_cast更加有效。例如,我們定義一種指針類型:

typedef void (A::*PSetNum)(int);

我們可以將FARPROC類型的指針fp轉(zhuǎn)化成PSetNum:

PSetNum psn = force_cast<PSetNum>(fp);

找到了將FARPROC轉(zhuǎn)化成成員函數(shù)指針的方法以后,我們要考慮如何將C++成員函數(shù)以更加友好的名字導(dǎo)出。這可以通過一個.def文件來實現(xiàn)。

第一步是找到待導(dǎo)出函數(shù)經(jīng)過修飾的函數(shù)名,這可以通過查看map file或者匯編代碼來實現(xiàn)。然后在.def文件中指定導(dǎo)出函數(shù)的新的函數(shù)名:

EXPORTS

ConstructorOfA1 = ??0A@@QAE@XZ ? ? ? ? PRIVATE

ConstructorOfA2 = ??0A@@QAE@H@Z ? ? ? ?PRIVATE

SetNumOfA ? ? ? ?= ?SetNum@A@@UAEXH@Z ? PRIVATE

GetNumOfA ? ? ? ?= ?GetNum@A@@UAEHXZ ? ?PRIVATE ? ? ? ? ?

DestructorOfA ? ?= ??1A@@UAE@XZ ? ? ? ? PRIVATE

下面是調(diào)用這些成員函數(shù)的方法:

typedef void (A::*PfnConstructorOfA1)();

typedef void (A::*PfnConstructorOfA2)(int);

typedef void (A::*PfnDestructorOfA)();

typedef void (A::*PfnSetNumOfA)(int);

typedef int ? (A::*PfnGetNumOfA)();

A* a1 = (A*)_alloca(sizeof(A));


PfnConstructorOfA1 pfnConsA =

? ? ?force_cast<PfnConstructorOfA1>(GetProcAddress(hMod, TEXT("ConstructorOfA1")));

(a1->*pfnConsA)();


PfnSetNumOfA pfnSetNumA =

? ? ? ? ? force_cast<PfnSetNumOfA>(GetProcAddress(hMod, TEXT("SetNumOfA")));

(a1->*pfnSetNumA)(1);


PfnGetNumOfA pfnGetNumA =

? ? ? ? ? force_cast<PfnGetNumOfA>(GetProcAddress(hMod, TEXT("GetNumOfA")));

_tprintf(TEXT("Value of m_nNum in a is %d "),(a1->*pfnGetNumA)());


PfnDestructorOfA pfnDestA = ?

? ? ? ? ? force_cast<PfnDestructorOfA>(GetProcAddress(hMod, TEXT("DestructorOfA")));

(a1->*pfnDestA)();

注意這里使用了alloca從棧中分配內(nèi)存,你也可以使用malloc從堆中分配內(nèi)存。但是不能使用C++的new操作符,因為能過new來分配內(nèi)存編譯器會自動插入對constructor的調(diào)用。但我們要的是顯式鏈接,所以必須避免這種情況。隨之產(chǎn)生的結(jié)果是我們只能顯式地去調(diào)用構(gòu)造函數(shù)和析構(gòu)函數(shù)。

轉(zhuǎn)載于:https://www.cnblogs.com/lushijie/archive/2013/05/25/3311195.html

創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎

總結(jié)

以上是生活随笔為你收集整理的基于VC++6.0的DLL开发的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。