動態(tài)調(diào)用動態(tài)庫方法 c/c++ linux windows

動態(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é)果如下:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末阱驾,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子怪蔑,更是在濱河造成了極大的恐慌里覆,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缆瓣,死亡現(xiàn)場離奇詭異喧枷,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)弓坞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門隧甚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人渡冻,你說我怎么就攤上這事戚扳。” “怎么了族吻?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵帽借,是天一觀的道長。 經(jīng)常有香客問我超歌,道長常熙,這世上最難降的妖魔是什么简软? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上队伟,老公的妹妹穿的比我還像新娘。我一直安慰自己击胜,他們只是感情好换怖,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著定枷,像睡著了一般孤澎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上欠窒,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天覆旭,我揣著相機(jī)與錄音,去河邊找鬼岖妄。 笑死型将,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的荐虐。 我是一名探鬼主播七兜,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼福扬!你這毒婦竟也來了腕铸?” 一聲冷哼從身側(cè)響起惜犀,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎狠裹,沒想到半個月后虽界,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡涛菠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年莉御,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片俗冻。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡礁叔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出迄薄,到底是詐尸還是另有隱情晴圾,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布噪奄,位于F島的核電站死姚,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏勤篮。R本人自食惡果不足惜都毒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望碰缔。 院中可真熱鬧账劲,春花似錦、人聲如沸金抡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽梗肝。三九已至榛瓮,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間巫击,已是汗流浹背禀晓。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留坝锰,地道東北人粹懒。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像顷级,于是被迫代替她去往敵國和親凫乖。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容