|
動態(tài)調用動態(tài)庫方法 c/c++ linux windows
2012-04-01 09:23:33
分類: 嵌入式 question: 1) to such standard *.so in linux, which not neccessary to call open the "dlopen", in which step, the open library really do. 2)if i make a *.so in cross compile way, that I can not put in the host directory /usr/lib, then how to make the compile know it will reference a *.so? 動態(tài)調用動態(tài)庫方法 c/c++ linux windows(2011-05-25 18:45:30)
關于動態(tài)調用動態(tài)庫方法說明
一、 動態(tài)庫概述
1、 動態(tài)庫的概念
日常編程中,常有一些函數不需要進行編譯或者可以在多個文件中使用(如數據庫輸入/輸出操作或屏幕控制等標準任務函數)??梢允孪葘@些函數進行編譯,然后將它們放置在一些特殊的目標代碼文件中,這些目標代碼文件就稱為庫。庫文件中的函數可以通過連接程序與應用程序進行鏈接,這樣就不必在每次開發(fā)程序時都對這些通用的函數進行編譯了。 動態(tài)庫是一種在已經編譯完畢的程序開始啟動運行時,才被加載來調用其中函數的庫。其加載方式與靜態(tài)庫截然不同。 2、 動態(tài)庫的命名Linux下,動態(tài)庫通常以.so(share object)結尾。(通常/lib和/usr/lib等目錄下存在大量系統提供的以.so結尾的動態(tài)庫文件) Windows下,動態(tài)庫常以.dll結尾。(通常C:\windows\System32等目錄下存在大量系統提供的以.dll結尾的動態(tài)庫文件) 3、 動態(tài)庫與靜態(tài)庫之間的區(qū)別靜態(tài)庫是指編譯連接時,把庫文件的代碼全部加入到可執(zhí)行文件中,所以生成的文件較大,但運行時,就不再需要庫文件了。即,程序與靜態(tài)庫編譯鏈接后,即使刪除靜態(tài)庫文件,程序也可正常執(zhí)行。 動態(tài)庫正好相反,在編譯鏈接時,沒有把庫文件的代碼加入到可執(zhí)行文件中,所以生成的文件較小,但運行時,仍需要加載庫文件。即,程序只在執(zhí)行啟動時才加載動態(tài)庫,如果刪除動態(tài)庫文件,程序將會因為無法讀取動態(tài)庫而產生異常。 二、 Linux下動態(tài)調用動態(tài)庫備注:以下linux實例說明都是在RedHat 5.1系統+ gcc 版本 4.1.2 20080704 (Red Hat 4.1.2-46)上實現。 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)調用接口函數說明動態(tài)庫的調用關系可以在需要調用動態(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 (此處,我們重點講解動態(tài)庫的動態(tài)調用的方法,關于靜態(tài)的通過g++編譯命令調用的方式不作詳細講解,具體相關內容可上網查詢)
Linux下,提供專門的一組API用于完成打開動態(tài)庫,查找符號,處理出錯,關閉動態(tài)庫等功能。 下面對這些接口函數逐一介紹(調用這些接口時,需引用頭文件#include 1) dlopen 函數原型:void *dlopen(const char *libname,int flag); 功能描述:dlopen必須在dlerror,dlsym和dlclose之前調用,表示要將庫裝載到內存,準備使用。如果要裝載的庫依賴于其它庫,必須首先裝載依賴庫。如果dlopen操作失敗,返回NULL值;如果庫已經被裝載過,則dlopen會返回同樣的句柄。 參數中的libname一般是庫的全路徑,這樣dlopen會直接裝載該文件;如果只是指定了庫名稱,在dlopen會按照下面的機制去搜尋: a.根據環(huán)境變量LD_LIBRARY_PATH查找 b.根據/etc/ld.so.cache查找 c.查找依次在/lib和/usr/lib目錄查找。 flag參數表示處理未定義函數的方式,可以使用RTLD_LAZY或RTLD_NOW。RTLD_LAZY表示暫時不去處理未定義函數,先把庫裝載到內存,等用到沒定義的函數再說;RTLD_NOW表示馬上檢查是否存在未定義的函數,若存在,則dlopen以失敗告終。 2) dlerror 函數原型:char *dlerror(void); 功能描述:dlerror可以獲得最近一次dlopen,dlsym或dlclose操作的錯誤信息,返回NULL表示無錯誤。dlerror在返回錯誤信息的同時,也會清除錯誤信息。 3) dlsym 函數原型:void *dlsym(void *handle,const char *symbol); 功能描述:在dlopen之后,庫被裝載到內存。dlsym可以獲得指定函數(symbol)在內存中的位置(指針)。如果找不到指定函數,則dlsym會返回NULL值。但判斷函數是否存在最好的方法是使用dlerror函數, 4) dlclose 函數原型:int dlclose(void *); 功能描述:將已經裝載的庫句柄減一,如果句柄減至零,則該庫會被卸載。如果存在析構函數,則在dlclose之后,析構函數會被調用。 3、 普通函數的調用此處以源碼實例說明。各源碼文件關系如下: 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函數,并使用函數指針以到達動態(tài)調用不同so庫中test函數的目的。 ////////////////////////////////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函數的不同 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函數的不同 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)(); //聲明函數指針類型
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在當前內存中的地址 if ((error = dlerror()) != NULL) { fprintf (stderr, "%s\n", error); exit(1); } pf(); //通過指針pf的調用來調用動態(tài)庫中的test函數 dlclose(handle); //關閉調用動態(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
上述源程序中,需重點注意兩個問題: 1、test_dl.cpp中,對于動態(tài)庫中的test函數調用是通過函數指針來完成的。 2、test_so1.h和test_so2.h中都使用了extern "C"。 在每個C++程序(或庫、目標文件)中,所有非靜態(tài)(non-static)函數在二進制文件中都是以“符號(symbol)”形式出現的。這些符號都是唯一的字符串,從而把各個函數在程序、庫、目標文件中區(qū)分開來。 在C中,符號名正是函數名:strcpy函數的符號名就是“strcpy”。這可能是因為兩個非靜態(tài)函數的名字一定各不相同的緣故。 而C++允許重載(不同的函數有相同的名字但不同的參數),并且有很多C所沒有的特性──比如類、成員函數、異常說明──幾乎不可能直接用函數名作符號名。為了解決這個問題,C++采用了所謂的name mangling。它把函數名和一些信息(如參數數量和大?。╇s糅在一起,改造成奇形怪狀,只有編譯器才懂的符號名。例如,被mangle后的foo可能看起來像foo@4%6^,或者,符號名里頭甚至不包括“foo”。 其中一個問題是,C++標準(目前是[ISO14882])并沒有定義名字必須如何被mangle,所以每個編譯器都按自己的方式來進行name mangling。有些編譯器甚至在不同版本間更換mangling算法(尤其是g++ 2.x和3.x)。即使您搞清楚了您的編譯器到底怎么進行mangling的,從而可以用dlsym調用函數了,但可能僅僅限于您手頭的這個編譯器而已,而無法在下一版編譯器下工作。 用 extern "C"聲明的函數將使用函數名作符號名,就像C函數一樣。因此,只有非成員函數才能被聲明為extern "C",并且不能被重載。盡管限制多多,extern "C"函數還是非常有用,因為它們可以象C函數一樣被dlopen動態(tài)加載。冠以extern "C"限定符后,并不意味著函數中無法使用C++代碼了,相反,它仍然是一個完全的C++函數,可以使用任何C++特性和各種類型的參數。
執(zhí)行makefile正常編譯后,可生成test_so1.so、test_so2.so動態(tài)庫以及test_dl執(zhí)行程序??蓤?zhí)行test_dl,顯示結果如下: [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
備注:如果我們去掉test_so1.h和test_so2.h中的extern "C",重新編譯執(zhí)行后將可能會出現什么情況?有興趣的朋友可以試下: [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、 類的調用 加載類有點困難,因為我們需要類的一個實例,而不僅僅是一個函數指針。我們無法通過new來創(chuàng)建類的實例,因為類是在動態(tài)庫中定義的而不是在可執(zhí)行程序中定義的,況且有時候我們連動態(tài)庫中具體的類的名字都不知道。 解決方案是:利用多態(tài)性!我們在可執(zhí)行文件中定義一個帶虛成員函數的接口基類,而在模塊中定義派生實現類。通常來說,接口類是抽象的(如果一個類含有虛函數,那它就是抽象的)。因為動態(tài)加載類往往用于實現插件,這意味著必須提供一個清晰定義的接口──我們將定義一個接口類和派生實現類。 接下來,在模塊中,我們會定義兩個附加的類工廠函數(class factory functions)(或稱對象工廠函數)。其中一個函數創(chuàng)建一個類實例,并返回其指針;另一個函數則用以銷毀該指針。這兩個函數都以extern "C"來限定修飾。
實例如下: test_base.hpp中定義一個含有純虛函數virtual void display() const = 0的基類。 test_1.cpp中定義繼承類test1,并實現虛函數virtual void display() const的定義,并實現一個創(chuàng)建類函數和一個銷毀類指針函數。 test_2.cpp中定義繼承類test2,并實現虛函數virtual void display() const的定義,并實現一個創(chuàng)建類函數和一個銷毀類指針函數。 main.cpp中實現動態(tài)的調用不同庫中的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,顯示結果如下: [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)調用動態(tài)庫備注:以下windows實例說明都是在Win7系統+visual studio 2005上實現。 1、 .dll動態(tài)庫的生成使用visual studio 2005工具,創(chuàng)建一個新項目,選擇Win32——Win32控制臺應用程序(此處需選擇名稱及位置)——應用程序類型:DLL+附加選項:空項目,完成以上步驟即可創(chuàng)建一個dll項目。 在項目中的頭文件和源文件、資源文件中新增相應代碼后,通過工具欄中Build(生成)即可生成相應dll文件。dll文件生成的位置通常在該項目位置中的debug目錄下。 2、 .dll動態(tài)庫的動態(tài)調用接口函數說明1) LoadLibrary 函數原型:HMODUBLE WINAPI LoadLibrary(LPCTSTR lpFileName); (其中HMODUBLE通常是被載入模塊的線性地址類型;LPCTSTR =const tchar *。) 功能描述:表示要將庫裝載到內存,準備使用。如果要裝載的庫依賴于其它庫,必須首先裝載依賴庫。如果LoadLibrary操作失敗,返回NULL值;如果庫已經被裝載過,則LoadLibrary會返回同樣的句柄。 參數中的lpFileName一般是庫的全路徑,這樣LoadLibrary會直接裝載該文件;如果只是指定了庫名稱,在LoadLibrary會在當前目錄下查找。 2) GetProcAddress 函數原型:FARPROC WINAPI GetProcAddress (HMODUBLE hModule,LPCTSTR lpProcName); (其中FARPROC 通常代表函數指針) 功能描述:表示已獲取指向應用程序要調用的每個導出函數的函數指針。由于應用程序是通過指針調用 DLL 的函數,編譯器不生成外部引用,故無需與導入庫鏈接。 參數中的hModule是由LoadLibrary加載庫后返回的模塊線性地址句柄;lpProcName是要調用的庫函數名稱。 3) GetProcAddress 函數原型: BOOL WINAPI FreeLibrary(HMODUBLE hModule) 功能描述:使用完 DLL 后調用 FreeLibrary卸載動態(tài)庫。卸載成功返回true,否則返回false。 3、 普通函數的調用 使用visual studio 2005工具,創(chuàng)建一個新項目,選擇Win32——Win32控制臺應用程序(此處需選擇名稱及位置,假設該處名稱為dll_load)——應用程序類型:控制臺應用程序+附加選項:預編譯頭,完成以上步驟即可創(chuàng)建一個dll_load項目。
創(chuàng)建dll_load項目完畢后,修改項目的字符集屬性,步驟如下: 項目——dll_load屬性(最后一行就是)——配置屬性——常規(guī)——字符集,設置為“未設置”。項目默認創(chuàng)建的字符集為“使用UNICODE字符集”。(如果字符集設置為UNICODE字符集的話,調試程序時無法自動實現 “char *”轉換為“LPCWSTR”,需使用_T()或其它方法解決)
然后,在該dll_load項目中,繼續(xù)添加dll1和dll2項目,添加步驟如下: 文件——添加——新建項目——Win32——Win32控制臺應用程序(此處填寫名稱dll1,位置默認) ——應用程序類型:DLL+附加選項:空項目。 完成以上步驟即可在當前dll_deal項目中增加dll1項目。dll2項目也可參照dll1項目的添加即可。 在dll_load、dll1和dll2項目中增加下圖.h和.cpp源程序文件(其中dll_deal中的stdafx.h和stdafx.cpp為項目創(chuàng)建時默認生成,無需增加)。
各源程序文件代碼如下: dll1.h/dll1.cpp聲明定義int test()方法,并生成dll1.dll動態(tài)庫。 dll2.h/dll2.cpp聲明定義int test()方法,并生成dll2.dll動態(tài)庫。 dll_load.cpp中實現調用不同動態(tài)庫的test()方法。 ////////////////////////////////dll_deal.cpp////////////////////////////////////////////////////// // dll_load.cpp : 定義控制臺應用程序的入口點。 //
#include "stdafx.h" #include #include #include #include #include
typedef int(*lpFun)(); //定義函數指針類型
int main() { HINSTANCE hDll; //DLL句柄
lpFun testFun; //函數指針
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(); //表明函數是從DLL導入,給客戶端使用
////////////////////////////////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(); //表明函數是從DLL導入,給客戶端使用
////////////////////////////////dll2.cpp////////////////////////////////////////////////////// #include "dll2.h" #include #include
int test() { printf("RUNNING in dll2.dll NOW\n"); return 0;
}
各源程序中代碼填充完成之后,在dll1項目中完成dll1.dll的生成;在dll2項目中完成dll2.dll的生成;在dll_load項目中進行Debug,結果如下:
輸入dll1.dll或者dll2.dll后,結果如下:
輸入其它無效dll后,結果如下:
4、 類的調用 使用visual studio 2005工具,創(chuàng)建一個新項目,選擇Win32——Win32控制臺應用程序(此處需選擇名稱及位置,假設該處名稱為dll_deal)——應用程序類型:控制臺應用程序+附加選項:預編譯頭,完成以上步驟即可創(chuàng)建一個dll_deal項目。
創(chuàng)建dll_deal項目完畢后,修改項目的字符集屬性,步驟如下: 項目——dll_deal屬性(最后一行就是)——配置屬性——常規(guī)——字符集,設置為“未設置”。項目默認創(chuàng)建的字符集為“使用UNICODE字符集”。(如果字符集設置為UNICODE字符集的話,調試程序時無法自動實現 “char *”轉換為“LPCWSTR”,需使用_T()或其它方法解決)
然后,在該dll_deal項目中,繼續(xù)添加dll1和dll2項目,添加步驟如下: 文件——添加——新建項目——Win32——Win32控制臺應用程序(此處填寫名稱dll1,位置默認) ——應用程序類型:DLL+附加選項:空項目。 完成以上步驟即可在當前dll_deal項目中增加dll1項目。dll2項目也可參照dll1項目的添加即可。 在dll_deal、dll1和dll2項目中增加下圖.h和.cpp源程序文件(其中dll_deal中的stdafx.h和stdafx.cpp為項目創(chuàng)建時默認生成,無需增加)。
各源程序文件代碼如下: dll_deal.h/dll1.h/dll2.h中定義相同的含有純虛函數virtual void display() const = 0的基類。 dll1.cpp中定義繼承類test1,并實現虛函數virtual void display() const的定義,并實現一個創(chuàng)建類函數和一個銷毀類指針函數。 dll2.cpp中定義繼承類test2,并實現虛函數virtual void display() const的定義,并實現一個創(chuàng)建類函數和一個銷毀類指針函數。 dll_deal.cpp中實現調用不同動態(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 : 定義控制臺應用程序的入口點。 //
#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項目中完成dll1.dll的生成;在dll2項目中完成dll2.dll的生成;在dll_deal項目中進行Debug,結果如下:
輸入dll1.dll或者dll2.dll后,結果如下:
輸入其它無效dll后,結果如下: |
|
|