电竞比分网-中国电竞赛事及体育赛事平台

分享

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

 quasiceo 2016-07-16

分類: 嵌入式

 

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后,結果如下:

    本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯系方式、誘導購買等信息,謹防詐騙。如發(fā)現有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多