動態調用動態庫方法 .so

關於動態調用動態庫方法說明

1、       動態庫概述

一、  動態庫的概念

平常編程中,常有一些函數不須要進行編譯或者能夠在多個文件中使用(如數據庫輸入/輸 出操做或屏幕控制等標準任務函數)。能夠事先對這些函數進行編譯,而後將它們放置在一些特殊的目標代碼文件中,這些目標代碼文件就稱爲庫。庫文件中的函數 能夠經過鏈接程序與應用程序進行連接,這樣就沒必要在每次開發程序時都對這些通用的函數進行編譯了。linux

      動態庫是一種在已經編譯完畢的程序開始啓動運行時,才被加載來調用其中函數的庫。其加載方式與靜態庫大相徑庭。ios

二、  動態庫的命名

Linux下,動態庫一般以.so(shareobject)結尾。(一般/lib和/usr/lib等目錄下存在大量系統提供的以.so結尾的動態庫文件)c++

Windows下,動態庫常以.dll結尾。(一般C:\windows\System32等目錄下存在大量系統提供的以.dll結尾的動態庫文件)算法

三、  動態庫與靜態庫之間的區別

靜態庫是指編譯鏈接時,把庫文件的代碼所有加入到可執行文件中,因此生成的文件較大,但運行時,就再也不須要庫文件了。即,程序與靜態庫編譯連接後,即便刪除靜態庫文件,程序也可正常執行。數據庫

動態庫正好相反,在編譯連接時,沒有把庫文件的代碼加入到可執行文件中,因此生成的文件較小,但運行時,仍須要加載庫文件。即,程序只在執行啓動時才加載動態庫,若是刪除動態庫文件,程序將會由於沒法讀取動態庫而產生異常。編程

2、       Linux下動態調用動態庫

備註:如下linux實例說明都是在RedHat 5.1系統+ gcc版本 4.1.2 20080704 (Red Hat 4.1.2-46)上實現。windows

一、  .so動態庫的生成

可以使用gcc或者g++編譯器生成動態庫文件(此處以g++編譯器爲例)函數

g++ -shared -fPIC -c XXX.cpp工具

g++ -shared -fPIC -o XXX.so XXX.oui

二、  .so動態庫的動態調用接口函數說明

動態庫的調用關係能夠在須要調用動態庫的程序編譯時,經過g++的-L和-l命令來指定。例如:程序test啓動時須要加載目錄/root/src/lib中的libtest_so1.so動態庫,編譯命令可照以下編寫執行:

g++ -g -o test test.cpp –L/root/src/lib –ltest_so1

(此處,咱們重點講解動態庫的動態調用的方法,關於靜態的經過g++編譯命令調用的方式不做詳細講解,具體相關內容可上網查詢)

 

Linux下,提供專門的一組API用於完成打開動態庫,查找符號,處理出錯,關閉動態庫等功能。

下面對這些接口函數逐一介紹(調用這些接口時,需引用頭文件#include<dlfcn.h>):

1)       dlopen

函數原型:void *dlopen(const char *libname,int flag);

功能描述:dlopen必須在dlerror,dlsym和dlclose以前調用,表示要將庫裝載到內存,準備使用。若是要裝載的庫依賴於其它庫,必須首先裝載依賴庫。若是dlopen操做失敗,返回NULL值;若是庫已經被裝載過,則dlopen會返回一樣的句柄。

參數中的libname通常是庫的全路徑,這樣dlopen會直接裝載該文件;若是隻是指定了庫名稱,在dlopen會按照下面的機制去搜尋:

a.根據環境變量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以後,析構函數會被調用。

三、  普通函數的調用

此處以源碼實例說明。各源碼文件關係以下:

test_so1.h和test_so1.cpp生成test_so1.so動態庫。

test_so2.h和test_so2.cpp生成test_so2.so動態庫。

test_dl.cpp生成test_dl可執行程序,test_dl經過dlopen系列等API函數,並使用函數指針以到達動態調用不一樣so庫中test函數的目的。

////////////////////////////////test_so1.h//////////////////////////////////////////////////////

#include <stdio.h>

#include <stdlib.h>

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 <stdio.h>

#include <stdlib.h>

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 <stdio.h>

#include <stdlib.h>

#include <dlfcn.h>

 

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]指定的動態庫

 

       if (!handle)

       {

               fprintf (stderr, "%s\n", dlerror());

               exit(1);

       }

 

       dlerror();   

        pf_tpf=(pf_t)dlsym(handle,"test");   //指針pf指向test在當前內存中的地址

       if ((error = dlerror()) != NULL) 

       {

               fprintf (stderr, "%s\n", error);

               exit(1);

       }

       pf();       //經過指針pf的調用來調用動態庫中的test函數

       dlclose(handle);     //關閉調用動態庫句柄

       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

 

上述源程序中,需重點注意兩個問題:

一、test_dl.cpp中,對於動態庫中的test函數調用是經過函數指針來完成的。

二、test_so1.h和test_so2.h中都使用了extern "C"。

在每一個C++程序(或庫、目標文件)中,全部非靜態(non-static)函數在二進制文件中都是以「符號(symbol)」形式出現的。這些符號都是惟一的字符串,從而把各個函數在程序、庫、目標文件中區分開來。

在C中,符號名正是函數名:strcpy函數的符號名就是「strcpy」。這多是由於兩個非靜態函數的名字必定各不相同的緣故。

而C++容許重載(不一樣的函數有相同的名字但不一樣的參數),而且有不少C所沒有的特性 ──好比類、成員函數、異常說明──幾乎不可能直接用函數名做符號名。爲了解決這個問題,C++採用了所謂的namemangling。它把函數名和一些 信息(如參數數量和大小)雜糅在一塊兒,改形成奇形怪狀,只有編譯器才懂的符號名。例如,被mangle後的foo可能看起來像foo@4%6^,或者,符 號名裏頭甚至不包括「foo」。

其中一個問題是,C++標準(目前是[ISO14882])並無定義名字必須如何被 mangle,因此每一個編譯器都按本身的方式來進行namemangling。有些編譯器甚至在不一樣版本間更換mangling算法(尤爲是g++2.x 和3.x)。即便您搞清楚了您的編譯器到底怎麼進行mangling的,從而能夠用dlsym調用函數了,但可能僅僅限於您手頭的這個編譯器而已,而沒法 在下一版編譯器下工做。

用 extern "C"聲明的函數將使用函數名做符號名,就像C函數同樣。所以,只有非成員函數才能被聲明爲extern"C",而且不能被重載。儘管限制多 多,extern"C"函數仍是很是有用,由於它們能夠象C函數同樣被dlopen動態加載。冠以extern"C"限定符後,並不意味着函數中沒法使用 C++代碼了,相反,它仍然是一個徹底的C++函數,可使用任何C++特性和各類類型的參數。

 

執行makefile正常編譯後,可生成test_so1.so、test_so2.so動態庫以及test_dl執行程序。可執行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",從新編譯執行後將可能會出現什麼狀況?有興趣的朋友能夠試下:

[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

 

四、  類的調用

加載類有點困難,由於咱們須要類的一個實例,而不只僅是一個函數指針。咱們沒法經過new來建立類的實例,由於類是在動態庫中定義的而不是在可執行程序中定義的,何況有時候咱們連動態庫中具體的類的名字都不知道。

解決方案是:利用多態性!咱們在可執行文件中定義一個帶虛成員函數的接口基類,而在模 塊中定義派生實現類。一般來講,接口類是抽象的(若是一個類含有虛函數,那它就是抽象的)。由於動態加載類每每用於實現插件,這意味着必須提供一個清晰定 義的接口──咱們將定義一個接口類和派生實現類。

接下來,在模塊中,咱們會定義兩個附加的類工廠函數(class factoryfunctions)(或稱對象工廠函數)。其中一個函數建立一個類實例,並返回其指針;另外一個函數則用以銷燬該指針。這兩個函數都以extern"C"來限定修飾。

 

      實例以下:

      test_base.hpp中定義一個含有純虛函數virtual void display() const = 0的基類。

      test_1.cpp中定義繼承類test1,並實現虛函數virtual void display()const的定義,並實現一個建立類函數和一個銷燬類指針函數。

      test_2.cpp中定義繼承類test2,並實現虛函數virtual void display()const的定義,並實現一個建立類函數和一個銷燬類指針函數。

main.cpp中實現動態的調用不一樣庫中的display()方法。

////////////////////////////////test_base.hpp//////////////////////////////////////////////////////

#ifndef TEST_BASE_HPP

#define TEST_BASE_HPP

 

#include <iostream>

using namespace std;

 

class test_base {

 

public:

   test_base(){}

 

    virtual~test_base() {}

 

    voidcall_base() {

       cout << "call base"<< endl;

    }

 

    virtualvoid 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:

    virtualvoid display() const {

       cout << "Running in test1.so Now"<< endl;

    }

};

 

 

// the class factories

extern "C" test_base* create() {

    returnnew test1;

}

 

extern "C" void destroy(test_base* p) {

    deletep;

}

 

////////////////////////////////test1.cpp//////////////////////////////////////////////////////

#include "test_base.hpp"

 

class test2 : public test_base {

public:

    virtualvoid display() const {

       cout << "Running in test2.so Now"<< endl;

    }

};

 

 

// the class factories

extern "C" test_base* create() {

    returnnew test2;

}

 

extern "C" void destroy(test_base* p) {

    deletep;

}

 

////////////////////////////////main.cpp//////////////////////////////////////////////////////

#include "test_base.hpp"

#include <iostream>

#include <dlfcn.h>

 

int main(int argc , char** argv) {

 

    // loadthe test library

 

   if(argc!=2)

    {

       cout << "Argument Error! You mustenter 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;

    }

 

    // reseterrors

   dlerror();

   

    // loadthe symbols

    create_t*create_test = (create_t*) dlsym(test_index, "create");

    constchar* 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;

    }

 

    // createan instance of the class

   test_base* c_test = create_test();

 

    // usethe class

   c_test->display();

 

   destroy_test(c_test);

 

    // unloadthe 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

 

執行makefile正常編譯後,可生成test_1.so、test_2.so動態庫以及a.out執行程序。可執行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

3、       Windows下動態調用動態庫

備註:如下windows實例說明都是在Win7系統+visual studio2005上實現。

一、  .dll動態庫的生成

使用visual studio2005工具,建立一個新項目,選擇Win32——Win32控制檯應用程序(此處需選擇名稱及位置)——應用程序類型:DLL+附加選項:空項目,完成以上步驟便可建立一個dll項目。

在項目中的頭文件和源文件、資源文件中新增相應代碼後,經過工具欄中Build(生成)便可生成相應dll文件。dll文件生成的位置一般在該項目位置中的debug目錄下。

二、  .dll動態庫的動態調用接口函數說明

1)       LoadLibrary

函數原型:HMODUBLE WINAPI LoadLibrary(LPCTSTR lpFileName);

      (其中HMODUBLE一般是被載入模塊的線性地址類型;LPCTSTR =const tchar *。)

功能描述:表示要將庫裝載到內存,準備使用。若是要裝載的庫依賴於其它庫,必須首先裝載依賴庫。若是LoadLibrary操做失敗,返回NULL值;若是庫已經被裝載過,則LoadLibrary會返回一樣的句柄。

參數中的lpFileName通常是庫的全路徑,這樣LoadLibrary會直接裝載該文件;若是隻是指定了庫名稱,在LoadLibrary會在當前目錄下查找。

2)       GetProcAddress

函數原型:FARPROC WINAPI GetProcAddress (HMODUBLEhModule,LPCTSTR lpProcName);

      (其中FARPROC 一般表明函數指針)

功能描述:表示已獲取指向應用程序要調用的每一個導出函數的函數指針。因爲應用程序是經過指針調用 DLL的函數,編譯器不生成外部引用,故無需與導入庫連接。

參數中的hModule是由LoadLibrary加載庫後返回的模塊線性地址句柄;lpProcName是要調用的庫函數名稱。

3)       GetProcAddress

函數原型: BOOL WINAPI FreeLibrary(HMODUBLE hModule)

功能描述:使用完 DLL 後調用FreeLibrary卸載動態庫。卸載成功返回true,不然返回false。

 

三、  普通函數的調用

使用visual studio2005工具,建立一個新項目,選擇Win32——Win32控制檯應用程序(此處需選擇名稱及位置,假設該處名稱爲dll_load)—— 應用程序類型:控制檯應用程序+附加選項:預編譯頭,完成以上步驟便可建立一個dll_load項目。

 

建立dll_load項目完畢後,修改項目的字符集屬性,步驟以下:

項目——dll_load屬性(最後一行就是)——配置屬性——常規——字符集,設置 爲「未設置」。項目默認建立的字符集爲「使用UNICODE字符集」。(若是字符集設置爲UNICODE字符集的話,調試程序時沒法自動實現「char *」轉換爲「LPCWSTR」,需使用_T()或其它方法解決)

 

而後,在該dll_load項目中,繼續添加dll1和dll2項目,添加步驟以下:

文件——添加——新建項目——Win32——Win32控制檯應用程序(此處填寫名稱dll1,位置默認)——應用程序類型:DLL+附加選項:空項目。

完成以上步驟便可在當前dll_deal項目中增長dll1項目。dll2項目也可參照dll1項目的添加便可。

在dll_load、dll1和dll2項目中增長下圖.h和.cpp源程序文件(其中dll_deal中的stdafx.h和stdafx.cpp爲項目建立時默認生成,無需增長)。

 

   各源程序文件代碼以下:

      dll1.h/dll1.cpp聲明定義int test()方法,並生成dll1.dll動態庫。

      dll2.h/dll2.cpp聲明定義int test()方法,並生成dll2.dll動態庫。

   dll_load.cpp中實現調用不一樣動態庫的test()方法。

////////////////////////////////dll_deal.cpp//////////////////////////////////////////////////////

// dll_load.cpp : 定義控制檯應用程序的入口點。

//

 

#include "stdafx.h"

#include<stdio.h>

#include<windows.h>

#include<winuser.h>

#include<tchar.h>

#include<stdlib.h>

 

 

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<stdlib.h>

#include<stdio.h>

 

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<stdlib.h>

#include<stdio.h>

 

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後,結果以下:

 

 

四、  類的調用

使用visual studio2005工具,建立一個新項目,選擇Win32——Win32控制檯應用程序(此處需選擇名稱及位置,假設該處名稱爲dll_deal)—— 應用程序類型:控制檯應用程序+附加選項:預編譯頭,完成以上步驟便可建立一個dll_deal項目。

 

建立dll_deal項目完畢後,修改項目的字符集屬性,步驟以下:

項目——dll_deal屬性(最後一行就是)——配置屬性——常規——字符集,設置 爲「未設置」。項目默認建立的字符集爲「使用UNICODE字符集」。(若是字符集設置爲UNICODE字符集的話,調試程序時沒法自動實現「char *」轉換爲「LPCWSTR」,需使用_T()或其它方法解決)

 

而後,在該dll_deal項目中,繼續添加dll1和dll2項目,添加步驟以下:

文件——添加——新建項目——Win32——Win32控制檯應用程序(此處填寫名稱dll1,位置默認)——應用程序類型:DLL+附加選項:空項目。

完成以上步驟便可在當前dll_deal項目中增長dll1項目。dll2項目也可參照dll1項目的添加便可。

在dll_deal、dll1和dll2項目中增長下圖.h和.cpp源程序文件(其中dll_deal中的stdafx.h和stdafx.cpp爲項目建立時默認生成,無需增長)。

 

   各源程序文件代碼以下:

      dll_deal.h/dll1.h/dll2.h中定義相同的含有純虛函數virtual void display() const =0的基類。

      dll1.cpp中定義繼承類test1,並實現虛函數virtual void display()const的定義,並實現一個建立類函數和一個銷燬類指針函數。

      dll2.cpp中定義繼承類test2,並實現虛函數virtual void display()const的定義,並實現一個建立類函數和一個銷燬類指針函數。

   dll_deal.cpp中實現調用不一樣動態庫的display()方法。

////////////////////////////////dll_deal.h//////////////////////////////////////////////////////

#ifndef DLL_DEAL_H

#define DLL_DEAL_H

 

#include<iostream>

using namespace std;

 

class test_base {

 

public:

   test_base(){}

 

    virtual~test_base() {}

 

    voidcall_base() {

       cout << "call base"<< endl;

    }

 

    virtual voiddisplay() 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 <string>

#include<iostream>

#include<windows.h>

#include<winuser.h>

#include "dll_deal.h"

 

int main()

{

 

    HINSTANCE hDll; //DLL句柄

    string dll_name;

 

    cout << "Please choose thedll_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 notexist!"  <<endl;

    }

    return 0;

 

}

 

////////////////////////////////dll1.h//////////////////////////////////////////////////////

#ifndef DLL1_H

#define DLL1_H

 

#include<iostream>

using namespace std;

 

class test_base {

 

public:

   test_base(){}

 

    virtual~test_base() {}

 

    voidcall_base() {

       cout << "call base"<< endl;

    }

 

    virtual voiddisplay() 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 <cmath>

 

class test1 : public test_base {

public:

    virtual voiddisplay() const {

       cout << "Running in test1.so Now"<< endl;

    }

};

 

 

// the class factories

extern "C" __declspec(dllexport) test_base* create() {

    return newtest1;

}

 

extern "C" __declspec(dllexport) void destroy(test_base* p) {

    deletep;

}

 

////////////////////////////////dll2.h//////////////////////////////////////////////////////

#ifndef DLL2_H

#define DLL2_H

 

#include<iostream>

using namespace std;

 

class test_base {

 

public:

   test_base(){}

 

    virtual~test_base() {}

 

    voidcall_base() {

       cout << "call base"<< endl;

    }

 

    virtual voiddisplay() 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 <cmath>

 

class test2 : public test_base {

public:

    virtual voiddisplay() const {

       cout << "Running in test2.so Now"<< endl;

    }

};

 

 

// the class factories

extern "C" __declspec(dllexport) test_base* create() {

    return newtest2;

}

 

extern "C" __declspec(dllexport) void destroy(test_base* p) {

    deletep;

}

 

      各源程序中代碼填充完成以後,在dll1項目中完成dll1.dll的生成;在dll2項目中完成dll2.dll的生成;在dll_deal項目中進行Debug,結果以下:

 

輸入dll1.dll或者dll2.dll後,結果以下:

 

 

輸入其它無效dll後,結果以下:

相關文章
相關標籤/搜索