動態(tài)調(diào)用動態(tài)庫方法?c/c++?linux?windows
關(guān)于動態(tài)調(diào)用動態(tài)庫方法說明 一棠众、??????? 動態(tài)庫概述 1、? 動態(tài)庫的概念
日常編程中,常有一些函數(shù)不需要進(jìn)行編譯或者可以在多個文件中使用(如數(shù)據(jù)庫輸入/輸出操作或屏幕控制等標(biāo)準(zhǔn)任務(wù)函數(shù))±杵可以事先對這些函數(shù)進(jìn)行編譯,然后將它們放置在一些特殊的目標(biāo)代碼文件中缤谎,這些目標(biāo)代碼文件就稱為庫抒倚。庫文件中的函數(shù)可以通過連接程序與應(yīng)用程序進(jìn)行鏈接,這樣就不必在每次開發(fā)程序時都對這些通用的函數(shù)進(jìn)行編譯了坷澡。
動態(tài)庫是一種在已經(jīng)編譯完畢的程序開始啟動運(yùn)行時托呕,才被加載來調(diào)用其中函數(shù)的庫。其加載方式與靜態(tài)庫截然不同频敛。
2镣陕、? 動態(tài)庫的命名
Linux下,動態(tài)庫通常以.so(share object)結(jié)尾姻政。(通常/lib和/usr/lib等目錄下存在大量系統(tǒng)提供的以.so結(jié)尾的動態(tài)庫文件)
Windows下呆抑,動態(tài)庫常以.dll結(jié)尾。(通常C:\windows\System32等目錄下存在大量系統(tǒng)提供的以.dll結(jié)尾的動態(tài)庫文件)
3汁展、? 動態(tài)庫與靜態(tài)庫之間的區(qū)別
靜態(tài)庫是指編譯連接時鹊碍,把庫文件的代碼全部加入到可執(zhí)行文件中,所以生成的文件較大食绿,但運(yùn)行時侈咕,就不再需要庫文件了。即器紧,程序與靜態(tài)庫編譯鏈接后耀销,即使刪除靜態(tài)庫文件,程序也可正常執(zhí)行铲汪。
動態(tài)庫正好相反熊尉,在編譯鏈接時罐柳,沒有把庫文件的代碼加入到可執(zhí)行文件中,所以生成的文件較小狰住,但運(yùn)行時张吉,仍需要加載庫文件。即催植,程序只在執(zhí)行啟動時才加載動態(tài)庫肮蛹,如果刪除動態(tài)庫文件,程序?qū)驗(yàn)闊o法讀取動態(tài)庫而產(chǎn)生異常创南。
二伦忠、??????? Linux下動態(tài)調(diào)用動態(tài)庫
備注:以下linux實(shí)例說明都是在RedHat 5.1系統(tǒng)+?gcc?版本 4.1.2 20080704 (Red Hat 4.1.2-46)上實(shí)現(xiàn)。
1稿辙、? .so動態(tài)庫的生成
可使用gcc或者g++編譯器生成動態(tài)庫文件(此處以g++編譯器為例)
g++ -shared -fPIC -c XXX.cpp
g++ -shared -fPIC -o XXX.so XXX.o
2缓苛、? .so動態(tài)庫的動態(tài)調(diào)用接口函數(shù)說明
動態(tài)庫的調(diào)用關(guān)系可以在需要調(diào)用動態(tài)庫的程序編譯時,通過g++的-L和-l命令來指定邓深。例如:程序test啟動時需要加載目錄/root/src/lib中的libtest_so1.so動態(tài)庫,編譯命令可照如下編寫執(zhí)行:
g++ -g -o test test.cpp –L/root/src/lib –ltest_so1
(此處笔刹,我們重點(diǎn)講解動態(tài)庫的動態(tài)調(diào)用的方法芥备,關(guān)于靜態(tài)的通過g++編譯命令調(diào)用的方式不作詳細(xì)講解,具體相關(guān)內(nèi)容可上網(wǎng)查詢)
Linux下舌菜,提供專門的一組API用于完成打開動態(tài)庫萌壳,查找符號,處理出錯日月,關(guān)閉動態(tài)庫等功能袱瓮。
下面對這些接口函數(shù)逐一介紹(調(diào)用這些接口時,需引用頭文件#include
1)????????dlopen
函數(shù)原型:void *dlopen(const char *libname,int flag);
功能描述:dlopen必須在dlerror爱咬,dlsym和dlclose之前調(diào)用尺借,表示要將庫裝載到內(nèi)存,準(zhǔn)備使用精拟。如果要裝載的庫依賴于其它庫燎斩,必須首先裝載依賴庫。如果dlopen操作失敗蜂绎,返回NULL值栅表;如果庫已經(jīng)被裝載過,則dlopen會返回同樣的句柄师枣。
參數(shù)中的libname一般是庫的全路徑怪瓶,這樣dlopen會直接裝載該文件;如果只是指定了庫名稱践美,在dlopen會按照下面的機(jī)制去搜尋:
a.根據(jù)環(huán)境變量LD_LIBRARY_PATH查找
b.根據(jù)/etc/ld.so.cache查找
c.查找依次在/lib和/usr/lib目錄查找洗贰。
flag參數(shù)表示處理未定義函數(shù)的方式找岖,可以使用RTLD_LAZY或RTLD_NOW。RTLD_LAZY表示暫時不去處理未定義函數(shù)哆姻,先把庫裝載到內(nèi)存宣增,等用到?jīng)]定義的函數(shù)再說;RTLD_NOW表示馬上檢查是否存在未定義的函數(shù)矛缨,若存在爹脾,則dlopen以失敗告終。
2)????????dlerror
函數(shù)原型:char *dlerror(void);
功能描述:dlerror可以獲得最近一次dlopen,dlsym或dlclose操作的錯誤信息箕昭,返回NULL表示無錯誤灵妨。dlerror在返回錯誤信息的同時,也會清除錯誤信息落竹。
3)????????dlsym
函數(shù)原型:void *dlsym(void *handle,const char *symbol);
功能描述:在dlopen之后泌霍,庫被裝載到內(nèi)存。dlsym可以獲得指定函數(shù)(symbol)在內(nèi)存中的位置(指針)述召。如果找不到指定函數(shù)朱转,則dlsym會返回NULL值。但判斷函數(shù)是否存在最好的方法是使用dlerror函數(shù)积暖,
4)????????dlclose
函數(shù)原型:int dlclose(void *);
功能描述:將已經(jīng)裝載的庫句柄減一藤为,如果句柄減至零,則該庫會被卸載夺刑。如果存在析構(gòu)函數(shù)缅疟,則在dlclose之后,析構(gòu)函數(shù)會被調(diào)用遍愿。
3存淫、? 普通函數(shù)的調(diào)用
此處以源碼實(shí)例說明。各源碼文件關(guān)系如下:
test_so1.h和test_so1.cpp生成test_so1.so動態(tài)庫沼填。
test_so2.h和test_so2.cpp生成test_so2.so動態(tài)庫桅咆。
test_dl.cpp生成test_dl可執(zhí)行程序,test_dl通過dlopen系列等API函數(shù)坞笙,并使用函數(shù)指針以到達(dá)動態(tài)調(diào)用不同so庫中test函數(shù)的目的轧邪。
////////////////////////////////test_so1.h//////////////////////////////////////////////////////
#include
#include
extern "C" {
int test(void);
}
////////////////////////////////ttest_so1.cpp//////////////////////////////////////////////////////
#include "test_so1.h"
int test(void)
{
printf("USING TEST_SO1.SO NOW!\n");//注意此處與test_so2.cpp中的
//test函數(shù)的不同
return 1;
}
//////////////////////////////// test_so2.h //////////////////////////////////////////////////////
#include
#include
extern "C" {
int test(void);
}
////////////////////////////////ttest_so2.cpp//////////////////////////////////////////////////////
#include "test_so2.h"
int test(void)
{
printf("USING TEST_SO2.SO NOW!\n");//注意此處與test_so1.cpp中的
//test函數(shù)的不同
return 1;
}
////////////////////////////////test_dl.cpp//////////////////////////////////////////////////////
#include
#include
#include
int main(int argc, char **argv)
{
if(argc!=2)
{
printf("Argument Error! You must enter like this:\n");
printf("./test_dl test_so1.so\n");
exit(1);
}
void *handle;
char *error;
typedef void (*pf_t)();?? //聲明函數(shù)指針類型
handle = dlopen (argv[1], RTLD_NOW);???? //打開argv[1]指定的動態(tài)庫
if (!handle)
{
fprintf (stderr, "%s\n", dlerror());
exit(1);
}
dlerror();
pf_t pf=(pf_t)dlsym(handle,"test" );??? //指針pf指向test在當(dāng)前內(nèi)存中的地址
if ((error = dlerror()) != NULL)
{
fprintf (stderr, "%s\n", error);
exit(1);
}
pf();??????? //通過指針pf的調(diào)用來調(diào)用動態(tài)庫中的test函數(shù)
dlclose(handle);????? //關(guān)閉調(diào)用動態(tài)庫句柄
return 0;
}
////////////////////////////////makefile//////////////////////////////////////////////////////
.SUFFIXES: .c .cpp .o
CC=g++ ?-shared -fPIC
GCC=g++
all:test_so1.so test_so2.so test_dl clean
OBJ1=test_so1.o
OBJ2=test_so2.o
OBJ3=test_dl.o
test_so1.so:$(OBJ1)
$(CC) -o $@ $?
cp $@ /usr/lib
test_so2.so:$(OBJ2)
$(CC) -o $@ $?
cp $@ /usr/lib
test_dl:$(OBJ3)
$(GCC)? -o $@ $? -ldl
.cpp.o:
$(CC) -c $*.cpp
.c.o:
$(CC) -c $*.c
clean:
rm -f *.o
上述源程序中,需重點(diǎn)注意兩個問題:
1羞海、test_dl.cpp中忌愚,對于動態(tài)庫中的test函數(shù)調(diào)用是通過函數(shù)指針來完成的。
2却邓、test_so1.h和test_so2.h中都使用了extern "C"硕糊。
在每個C++程序(或庫、目標(biāo)文件)中,所有非靜態(tài)(non-static)函數(shù)在二進(jìn)制文件中都是以“符號(symbol)”形式出現(xiàn)的简十。這些符號都是唯一的字符串檬某,從而把各個函數(shù)在程序、庫螟蝙、目標(biāo)文件中區(qū)分開來恢恼。
在C中,符號名正是函數(shù)名:strcpy函數(shù)的符號名就是“strcpy”胰默。這可能是因?yàn)閮蓚€非靜態(tài)函數(shù)的名字一定各不相同的緣故场斑。
而C++允許重載(不同的函數(shù)有相同的名字但不同的參數(shù)),并且有很多C所沒有的特性──比如類牵署、成員函數(shù)漏隐、異常說明──幾乎不可能直接用函數(shù)名作符號名。為了解決這個問題奴迅,C++采用了所謂的name mangling青责。它把函數(shù)名和一些信息(如參數(shù)數(shù)量和大小)雜糅在一起取具,改造成奇形怪狀脖隶,只有編譯器才懂的符號名。例如暇检,被mangle后的foo可能看起來像foo@4%6^产阱,或者,符號名里頭甚至不包括“foo”占哟。
其中一個問題是,C++標(biāo)準(zhǔn)(目前是[ISO14882])并沒有定義名字必須如何被mangle酿矢,所以每個編譯器都按自己的方式來進(jìn)行name mangling榨乎。有些編譯器甚至在不同版本間更換mangling算法(尤其是g++ 2.x和3.x)。即使您搞清楚了您的編譯器到底怎么進(jìn)行mangling的瘫筐,從而可以用dlsym調(diào)用函數(shù)了蜜暑,但可能僅僅限于您手頭的這個編譯器而已,而無法在下一版編譯器下工作策肝。
用 extern "C"聲明的函數(shù)將使用函數(shù)名作符號名肛捍,就像C函數(shù)一樣。因此之众,只有非成員函數(shù)才能被聲明為extern "C"拙毫,并且不能被重載。盡管限制多多棺禾,extern "C"函數(shù)還是非常有用缀蹄,因?yàn)樗鼈兛梢韵驝函數(shù)一樣被dlopen動態(tài)加載。冠以extern "C"限定符后,并不意味著函數(shù)中無法使用C++代碼了缺前,相反蛀醉,它仍然是一個完全的C++函數(shù),可以使用任何C++特性和各種類型的參數(shù)衅码。
執(zhí)行makefile正常編譯后拯刁,可生成test_so1.so、test_so2.so動態(tài)庫以及test_dl執(zhí)行程序逝段《獠#可執(zhí)行test_dl,顯示結(jié)果如下:
[root@localhost so_src]# ./test_dl test_so1.so
USING TEST_SO1.SO NOW!
[root@localhost so_src]# ./test_dl test_so2.so
USING TEST_SO2.SO NOW!
[root@localhost so_src]# ./test_dl
Argument Error! You must enter like this:
./test_dl test_so1.so
備注:如果我們?nèi)サ魌est_so1.h和test_so2.h中的extern "C"惹恃,重新編譯執(zhí)行后將可能會出現(xiàn)什么情況夭谤?有興趣的朋友可以試下:
[root@localhost so_src]# ./test_dl test_so1.so
/usr/lib/test_so1.so: undefined symbol: test
[root@localhost so_src]# ./test_dl test_so2.so
/usr/lib/test_so2.so: undefined symbol: test
4、? 類的調(diào)用
加載類有點(diǎn)困難巫糙,因?yàn)槲覀冃枰惖囊粋€實(shí)例朗儒,而不僅僅是一個函數(shù)指針。我們無法通過new來創(chuàng)建類的實(shí)例参淹,因?yàn)轭愂窃趧討B(tài)庫中定義的而不是在可執(zhí)行程序中定義的醉锄,況且有時候我們連動態(tài)庫中具體的類的名字都不知道。
解決方案是:利用多態(tài)性浙值!我們在可執(zhí)行文件中定義一個帶虛成員函數(shù)的接口基類恳不,而在模塊中定義派生實(shí)現(xiàn)類。通常來說开呐,接口類是抽象的(如果一個類含有虛函數(shù)烟勋,那它就是抽象的)。因?yàn)閯討B(tài)加載類往往用于實(shí)現(xiàn)插件筐付,這意味著必須提供一個清晰定義的接口──我們將定義一個接口類和派生實(shí)現(xiàn)類卵惦。
接下來,在模塊中瓦戚,我們會定義兩個附加的類工廠函數(shù)(class factory functions)(或稱對象工廠函數(shù))沮尿。其中一個函數(shù)創(chuàng)建一個類實(shí)例,并返回其指針较解;另一個函數(shù)則用以銷毀該指針畜疾。這兩個函數(shù)都以extern "C"來限定修飾。
實(shí)例如下:
test_base.hpp中定義一個含有純虛函數(shù)virtual void display() const = 0的基類印衔。
test_1.cpp中定義繼承類test1啡捶,并實(shí)現(xiàn)虛函數(shù)virtual void display() const的定義,并實(shí)現(xiàn)一個創(chuàng)建類函數(shù)和一個銷毀類指針函數(shù)奸焙。
test_2.cpp中定義繼承類test2届慈,并實(shí)現(xiàn)虛函數(shù)virtual void display() const的定義徒溪,并實(shí)現(xiàn)一個創(chuàng)建類函數(shù)和一個銷毀類指針函數(shù)。
main.cpp中實(shí)現(xiàn)動態(tài)的調(diào)用不同庫中的display()方法金顿。
////////////////////////////////test_base.hpp//////////////////////////////////////////////////////
#ifndef TEST_BASE_HPP
#define TEST_BASE_HPP
#include
using namespace std;
class test_base {
public:
test_base(){}
virtual ~test_base() {}
void call_base() {
cout << "call base" << endl;
}
virtual void display() const = 0? ;
};
// the types of the class factories
typedef test_base* create_t();
typedef void destroy_t(test_base*);
#endif
////////////////////////////////test1.cpp//////////////////////////////////////////////////////
#include "test_base.hpp"
class test1 : public test_base {
public:
virtual void display() const {
cout << "Running in test1.so Now" << endl;
}
};
// the class factories
extern "C" test_base* create() {
return new test1;
}
extern "C" void destroy(test_base* p) {
delete p;
}
////////////////////////////////test1.cpp//////////////////////////////////////////////////////
#include "test_base.hpp"
class test2 : public test_base {
public:
virtual void display() const {
cout << "Running in test2.so Now" << endl;
}
};
// the class factories
extern "C" test_base* create() {
return new test2;
}
extern "C" void destroy(test_base* p) {
delete p;
}
////////////////////////////////main.cpp//////////////////////////////////////////////////////
#include "test_base.hpp"
#include
#include
int main(int argc , char** argv) {
// load the test library
if(argc!=2)
{
cout << "Argument Error! You must enter like this: " << '\n';
cout << "./a.out test_1.so " << '\n';
return 1;
}
void* test_index = dlopen(argv[1], RTLD_NOW);
if (!test_index) {
cerr << "Cannot load library: " << dlerror() << '\n';
return 1;
}
// reset errors
dlerror();
// load the symbols
create_t* create_test = (create_t*) dlsym(test_index, "create");
const char* dlsym_error = dlerror();
if (dlsym_error) {
cerr << "Cannot load symbol create: " << dlsym_error << '\n';
return 1;
}
destroy_t* destroy_test = (destroy_t*) dlsym(test_index, "destroy");
dlsym_error = dlerror();
if (dlsym_error) {
cerr << "Cannot load symbol destroy: " << dlsym_error << '\n';
return 1;
}
// create an instance of the class
test_base* c_test = create_test();
// use the class
c_test->display();
destroy_test(c_test);
// unload the test library
dlclose(test_index);
}
////////////////////////////////makefile//////////////////////////////////////////////////////
.SUFFIXES: .c .cpp .o
CC=g++ -g -shared -fPIC
GCC=g++ -g
all:clear test_1.so a.out test_2.so clean
OBJ1=test_1.o
OBJ2=main.o
OBJ3=test_2.o
clear:
rm -rf *.so a.out b.out
test_1.so:$(OBJ1)
$(CC) -o $@ $?
cp $@ /usr/lib
a.out:$(OBJ2)
$(GCC)? -o $@ $? -ldl
test_2.so:$(OBJ3)
$(CC) -o $@ $?
cp $@ /usr/lib
.cpp.o:
$(CC) -c $*.cpp
.c.o:
$(CC) -c $*.c
clean:
rm -f *.o
執(zhí)行makefile正常編譯后臊泌,可生成test_1.so、test_2.so動態(tài)庫以及a.out執(zhí)行程序揍拆∏牛可執(zhí)行a.out,顯示結(jié)果如下:
[root@localhost c++_so_src]# ./a.out test_1.so
Running in test1.so Now
[root@localhost c++_so_src]# ./a.out test_2.so
Running in test2.so Now
[root@localhost c++_so_src]# ./a.out
Argument Error! You must enter like this:
./a.out test_1.so
三嫂拴、??????? Windows下動態(tài)調(diào)用動態(tài)庫
備注:以下windows實(shí)例說明都是在Win7系統(tǒng)+visual studio 2005上實(shí)現(xiàn)播揪。
1、? .dll動態(tài)庫的生成
使用visual studio 2005工具筒狠,創(chuàng)建一個新項(xiàng)目猪狈,選擇Win32——Win32控制臺應(yīng)用程序(此處需選擇名稱及位置)——應(yīng)用程序類型:DLL+附加選項(xiàng):空項(xiàng)目,完成以上步驟即可創(chuàng)建一個dll項(xiàng)目辩恼。
在項(xiàng)目中的頭文件和源文件雇庙、資源文件中新增相應(yīng)代碼后,通過工具欄中Build(生成)即可生成相應(yīng)dll文件灶伊。dll文件生成的位置通常在該項(xiàng)目位置中的debug目錄下疆前。
2、? .dll動態(tài)庫的動態(tài)調(diào)用接口函數(shù)說明
1)????????LoadLibrary
函數(shù)原型:HMODUBLE WINAPI LoadLibrary(LPCTSTR lpFileName);
(其中HMODUBLE通常是被載入模塊的線性地址類型聘萨;LPCTSTR =const tchar *竹椒。)
功能描述:表示要將庫裝載到內(nèi)存,準(zhǔn)備使用米辐。如果要裝載的庫依賴于其它庫胸完,必須首先裝載依賴庫。如果LoadLibrary操作失敗翘贮,返回NULL值赊窥;如果庫已經(jīng)被裝載過,則LoadLibrary會返回同樣的句柄择膝。
參數(shù)中的lpFileName一般是庫的全路徑誓琼,這樣LoadLibrary會直接裝載該文件检激;如果只是指定了庫名稱肴捉,在LoadLibrary會在當(dāng)前目錄下查找。
2)????????GetProcAddress
函數(shù)原型:FARPROC WINAPI GetProcAddress (HMODUBLE hModule,LPCTSTR lpProcName);
(其中FARPROC 通常代表函數(shù)指針)
功能描述:表示已獲取指向應(yīng)用程序要調(diào)用的每個導(dǎo)出函數(shù)的函數(shù)指針叔收。由于應(yīng)用程序是通過指針調(diào)用 DLL 的函數(shù)齿穗,編譯器不生成外部引用,故無需與導(dǎo)入庫鏈接饺律。
參數(shù)中的hModule是由LoadLibrary加載庫后返回的模塊線性地址句柄窃页;lpProcName是要調(diào)用的庫函數(shù)名稱。
3)????????GetProcAddress
函數(shù)原型:?BOOL WINAPI FreeLibrary(HMODUBLE hModule)
功能描述:使用完 DLL 后調(diào)用 FreeLibrary卸載動態(tài)庫。卸載成功返回true脖卖,否則返回false乒省。
3、? 普通函數(shù)的調(diào)用
使用visual studio 2005工具畦木,創(chuàng)建一個新項(xiàng)目袖扛,選擇Win32——Win32控制臺應(yīng)用程序(此處需選擇名稱及位置,假設(shè)該處名稱為dll_load)——應(yīng)用程序類型:控制臺應(yīng)用程序+附加選項(xiàng):預(yù)編譯頭十籍,完成以上步驟即可創(chuàng)建一個dll_load項(xiàng)目蛆封。
創(chuàng)建dll_load項(xiàng)目完畢后,修改項(xiàng)目的字符集屬性勾栗,步驟如下:
項(xiàng)目——dll_load屬性(最后一行就是)——配置屬性——常規(guī)——字符集惨篱,設(shè)置為“未設(shè)置”。項(xiàng)目默認(rèn)創(chuàng)建的字符集為“使用UNICODE字符集”围俘。(如果字符集設(shè)置為UNICODE字符集的話砸讳,調(diào)試程序時無法自動實(shí)現(xiàn) “char *”轉(zhuǎn)換為“LPCWSTR”,需使用_T()或其它方法解決)
然后楷拳,在該dll_load項(xiàng)目中绣夺,繼續(xù)添加dll1和dll2項(xiàng)目,添加步驟如下:
文件——添加——新建項(xiàng)目——Win32——Win32控制臺應(yīng)用程序(此處填寫名稱dll1欢揖,位置默認(rèn)) ——應(yīng)用程序類型:DLL+附加選項(xiàng):空項(xiàng)目陶耍。
完成以上步驟即可在當(dāng)前dll_deal項(xiàng)目中增加dll1項(xiàng)目。dll2項(xiàng)目也可參照dll1項(xiàng)目的添加即可她混。
在dll_load烈钞、dll1和dll2項(xiàng)目中增加下圖.h和.cpp源程序文件(其中dll_deal中的stdafx.h和stdafx.cpp為項(xiàng)目創(chuàng)建時默認(rèn)生成,無需增加)坤按。
各源程序文件代碼如下:
dll1.h/dll1.cpp聲明定義int test()方法毯欣,并生成dll1.dll動態(tài)庫。
dll2.h/dll2.cpp聲明定義int test()方法臭脓,并生成dll2.dll動態(tài)庫酗钞。
dll_load.cpp中實(shí)現(xiàn)調(diào)用不同動態(tài)庫的test()方法。
////////////////////////////////dll_deal.cpp//////////////////////////////////////////////////////
// dll_load.cpp : 定義控制臺應(yīng)用程序的入口點(diǎn)来累。
//
#include "stdafx.h"
#include
#include
#include
#include
#include
typedef int(*lpFun)(); //定義函數(shù)指針類型
int main()
{
HINSTANCE hDll; //DLL句柄
lpFun testFun; //函數(shù)指針
char *dll_name=(char *)malloc(1024);
printf("Please choose the dll_name(dll1.dll or dll2.dll):\n");
scanf("%s",dll_name);
printf("\n");
hDll = LoadLibrary(dll_name);//加載DLL,需要將DLL放到工程目錄下.
free(dll_name);
if (hDll != NULL)
{
printf("LOAD DLL success\n");
testFun = (lpFun)GetProcAddress(hDll, "test");
if (testFun != NULL)
{
testFun();
}
else
{
printf("the calling is error\n");
}
FreeLibrary(hDll);
}
else
{
printf("Load DLL Error or DLL not exist!\n");
}
return 0;
}
////////////////////////////////dll1.h//////////////////////////////////////////////////////
#ifdef DLL1_API
#else
#define DLL1_API extern "C" _declspec(dllimport)? //同.cpp文件中同步
#endif
DLL1_API????? int test();??? //表明函數(shù)是從DLL導(dǎo)入砚作,給客戶端使用
////////////////////////////////dll1.cpp//////////////////////////////////////////////////////
#include "dll1.h"
#include
#include
int test()
{
printf("RUNNING in dll1.dll NOW\n");
return 0;
}
////////////////////////////////dll2.h//////////////////////////////////////////////////////
#ifdef DLL2_API
#else
#define DLL2_API extern "C" _declspec(dllimport)? //同.cpp文件中同步
#endif
DLL2_API int test();? ??//表明函數(shù)是從DLL導(dǎo)入,給客戶端使用
////////////////////////////////dll2.cpp//////////////////////////////////////////////////////
#include "dll2.h"
#include
#include
int test()
{
printf("RUNNING in dll2.dll NOW\n");
return 0;
}
各源程序中代碼填充完成之后嘹锁,在dll1項(xiàng)目中完成dll1.dll的生成葫录;在dll2項(xiàng)目中完成dll2.dll的生成;在dll_load項(xiàng)目中進(jìn)行Debug领猾,結(jié)果如下:
輸入dll1.dll或者dll2.dll后米同,結(jié)果如下:
輸入其它無效dll后骇扇,結(jié)果如下:
4、? 類的調(diào)用
使用visual studio 2005工具面粮,創(chuàng)建一個新項(xiàng)目少孝,選擇Win32——Win32控制臺應(yīng)用程序(此處需選擇名稱及位置,假設(shè)該處名稱為dll_deal)——應(yīng)用程序類型:控制臺應(yīng)用程序+附加選項(xiàng):預(yù)編譯頭熬苍,完成以上步驟即可創(chuàng)建一個dll_deal項(xiàng)目韭山。
創(chuàng)建dll_deal項(xiàng)目完畢后,修改項(xiàng)目的字符集屬性冷溃,步驟如下:
項(xiàng)目——dll_deal屬性(最后一行就是)——配置屬性——常規(guī)——字符集钱磅,設(shè)置為“未設(shè)置”。項(xiàng)目默認(rèn)創(chuàng)建的字符集為“使用UNICODE字符集”似枕。(如果字符集設(shè)置為UNICODE字符集的話盖淡,調(diào)試程序時無法自動實(shí)現(xiàn) “char *”轉(zhuǎn)換為“LPCWSTR”,需使用_T()或其它方法解決)
然后凿歼,在該dll_deal項(xiàng)目中褪迟,繼續(xù)添加dll1和dll2項(xiàng)目,添加步驟如下:
文件——添加——新建項(xiàng)目——Win32——Win32控制臺應(yīng)用程序(此處填寫名稱dll1答憔,位置默認(rèn)) ——應(yīng)用程序類型:DLL+附加選項(xiàng):空項(xiàng)目味赃。
完成以上步驟即可在當(dāng)前dll_deal項(xiàng)目中增加dll1項(xiàng)目。dll2項(xiàng)目也可參照dll1項(xiàng)目的添加即可虐拓。
在dll_deal心俗、dll1和dll2項(xiàng)目中增加下圖.h和.cpp源程序文件(其中dll_deal中的stdafx.h和stdafx.cpp為項(xiàng)目創(chuàng)建時默認(rèn)生成,無需增加)蓉驹。
各源程序文件代碼如下:
dll_deal.h/dll1.h/dll2.h中定義相同的含有純虛函數(shù)virtual void display() const = 0的基類城榛。
dll1.cpp中定義繼承類test1,并實(shí)現(xiàn)虛函數(shù)virtual void display() const的定義态兴,并實(shí)現(xiàn)一個創(chuàng)建類函數(shù)和一個銷毀類指針函數(shù)狠持。
dll2.cpp中定義繼承類test2,并實(shí)現(xiàn)虛函數(shù)virtual void display() const的定義瞻润,并實(shí)現(xiàn)一個創(chuàng)建類函數(shù)和一個銷毀類指針函數(shù)喘垂。
dll_deal.cpp中實(shí)現(xiàn)調(diào)用不同動態(tài)庫的display()方法。
////////////////////////////////dll_deal.h//////////////////////////////////////////////////////
#ifndef DLL_DEAL_H
#define DLL_DEAL_H
#include
using namespace std;
class test_base {
public:
test_base(){}
virtual ~test_base() {}
void call_base() {
cout << "call base" << endl;
}
virtual void display() const = 0? ;
};
// the types of the class factories
typedef test_base* create_t();
typedef void destroy_t(test_base*);
#endif
////////////////////////////////dll_deal.cpp//////////////////////////////////////////////////////
// dll_deal.cpp : 定義控制臺應(yīng)用程序的入口點(diǎn)绍撞。
//
#include "stdafx.h"
#include
#include
#include
#include
#include "dll_deal.h"
int main()
{
HINSTANCE hDll; //DLL句柄
string dll_name;
cout << "Please choose the dll_name(dll1.dll or dll2.dll):" << endl;
cin >> dll_name;
cout << endl;
hDll = LoadLibrary(dll_name.c_str());//加載DLL,需要將DLL放到工程目錄下.
if (hDll != NULL)
{
cout << "LOAD DLL success!" << endl;
// load the symbols
create_t* create_test = (create_t*)GetProcAddress(hDll, "create");
if (create_test == NULL)
{
cout << "Cannot load symbol create: "? << endl;
return 1;
}
destroy_t* destroy_test = (destroy_t*)GetProcAddress(hDll, "destroy");
if (destroy_test == NULL)
{
cout << "Cannot load symbol destroy: "? << endl;
return 1;
}
// create an instance of the class
test_base* c_test = create_test();
// use the class
c_test->display();
// destroy the class
destroy_test(c_test);
// unload the? library
FreeLibrary(hDll);
}
else
{
cout << "Load DLL Error or DLL not exist!"? << endl;
}
return 0;
}
////////////////////////////////dll1.h//////////////////////////////////////////////////////
#ifndef DLL1_H
#define DLL1_H
#include
using namespace std;
class test_base {
public:
test_base(){}
virtual ~test_base() {}
void call_base() {
cout << "call base" << endl;
}
virtual void display() const = 0? ;
};
// the types of the class factories
typedef test_base* create_t();
typedef void destroy_t(test_base*);
#endif
////////////////////////////////dll1.cpp//////////////////////////////////////////////////////
#include "dll1.h"
#include
class test1 : public test_base {
public:
virtual void display() const {
cout << "Running in test1.so Now" << endl;
}
};
// the class factories
extern "C" __declspec(dllexport)? test_base* create() {
return new test1;
}
extern "C" __declspec(dllexport)? void destroy(test_base* p) {
delete p;
}
////////////////////////////////dll2.h//////////////////////////////////////////////////////
#ifndef DLL2_H
#define DLL2_H
#include
using namespace std;
class test_base {
public:
test_base(){}
virtual ~test_base() {}
void call_base() {
cout << "call base" << endl;
}
virtual void display() const = 0? ;
};
// the types of the class factories
typedef test_base* create_t();
typedef void destroy_t(test_base*);
#endif
////////////////////////////////dll2.cpp//////////////////////////////////////////////////////
#include "dll2.h"
#include
class test2 : public test_base {
public:
virtual void display() const {
cout << "Running in test2.so Now" << endl;
}
};
// the class factories
extern "C" __declspec(dllexport)? test_base* create() {
return new test2;
}
extern "C" __declspec(dllexport)? void destroy(test_base* p) {
delete p;
}
各源程序中代碼填充完成之后正勒,在dll1項(xiàng)目中完成dll1.dll的生成;在dll2項(xiàng)目中完成dll2.dll的生成楚午;在dll_deal項(xiàng)目中進(jìn)行Debug昭齐,結(jié)果如下:
輸入dll1.dll或者dll2.dll后尿招,結(jié)果如下:
輸入其它無效dll后矾柜,結(jié)果如下: