1. 程式人生 > >linux 呼叫動態庫so檔案

linux 呼叫動態庫so檔案

   關於動態呼叫動態庫方法說明
一、        動態庫概述
1、  動態庫的概念
日常程式設計中,常有一些函式不需要進行編譯或者可以在多個檔案中使用(如資料庫輸入/輸出操作或螢幕控制等標準任務函式)。可以事先對這些函式進行編譯,然後將它們放置在一些特殊的目的碼檔案中,這些目的碼檔案就稱為庫。庫檔案中的函式可以通過連線程式與應用程式進行連結,這樣就不必在每次開發程式時都對這些通用的函式進行編譯了。

       動態庫是一種在已經編譯完畢的程式開始啟動執行時,才被載入來呼叫其中函式的庫。其載入方式與靜態庫截然不同。

2、  動態庫的命名
Linux下,動態庫通常以.so(share object)結尾。(通常/lib和/usr/lib等目錄下存在大量系統提供的以.so結尾的動態庫檔案)

Windows下,動態庫常以.dll結尾。(通常C:\windows\System32等目錄下存在大量系統提供的以.dll結尾的動態庫檔案)

3、  動態庫與靜態庫之間的區別
靜態庫是指編譯連線時,把庫檔案的程式碼全部加入到可執行檔案中,所以生成的檔案較大,但執行時,就不再需要庫檔案了。即,程式與靜態庫編譯連結後,即使刪除靜態庫檔案,程式也可正常執行。

動態庫正好相反,在編譯連結時,沒有把庫檔案的程式碼加入到可執行檔案中,所以生成的檔案較小,但執行時,仍需要載入庫檔案。即,程式只在執行啟動時才載入動態庫,如果刪除動態庫檔案,程式將會因為無法讀取動態庫而產生異常。

二、        Linux下動態呼叫動態庫
備註:以下linux例項說明都是在RedHat 5.1系統+ gcc 版本 4.1.2 20080704 (Red Hat 4.1.2-46)上實現。

1、  .so動態庫的生成
可使用gcc或者g++編譯器生成動態庫檔案(此處以g++編譯器為例)

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

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

2、  .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之後,解構函式會被呼叫。

3、  普通函式的呼叫
此處以原始碼例項說明。各原始碼檔案關係如下:

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_t pf=(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 [email protected] $?

        cp [email protected] /usr/lib

test_so2.so:$(OBJ2)

        $(CC) -o [email protected] $?

        cp [email protected] /usr/lib

test_dl:$(OBJ3)

        $(GCC)  -o [email protected] $? -ldl

.cpp.o:

        $(CC) -c $*.cpp

.c.o:

        $(CC) -c $*.c

clean:

        rm -f *.o

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

1、test_dl.cpp中,對於動態庫中的test函式呼叫是通過函式指標來完成的。

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

在每個C++程式(或庫、目標檔案)中,所有非靜態(non-static)函式在二進位制檔案中都是以“符號(symbol)”形式出現的。這些符號都是唯一的字串,從而把各個函式在程式、庫、目標檔案中區分開來。

在C中,符號名正是函式名:strcpy函式的符號名就是“strcpy”。這可能是因為兩個非靜態函式的名字一定各不相同的緣故。

而C++允許過載(不同的函式有相同的名字但不同的引數),並且有很多C所沒有的特性──比如類、成員函式、異常說明──幾乎不可能直接用函式名作符號名。為了解決這個問題,C++採用了所謂的name mangling。它把函式名和一些資訊(如引數數量和大小)雜糅在一起,改造成奇形怪狀,只有編譯器才懂的符號名。例如,被mangle後的foo可能看起來像[email protected]%6^,或者,符號名裡頭甚至不包括“foo”。

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

用 extern "C"宣告的函式將使用函式名作符號名,就像C函式一樣。因此,只有非成員函式才能被宣告為extern "C",並且不能被過載。儘管限制多多,extern "C"函式還是非常有用,因為它們可以象C函式一樣被dlopen動態載入。冠以extern "C"限定符後,並不意味著函式中無法使用C++程式碼了,相反,它仍然是一個完全的C++函式,可以使用任何C++特性和各種型別的引數。所以extern "C" 只是告訴編譯器編和連結的時候都用c的方式的函式名字,函式裡的內容可以為c的程式碼也可以為c++的。


執行makefile正常編譯後,可生成test_so1.so、test_so2.so動態庫以及test_dl執行程式。可執行test_dl,顯示結果如下:

[[email protected] so_src]# ./test_dl test_so1.so

USING TEST_SO1.SO NOW!

[[email protected] so_src]# ./test_dl test_so2.so

USING TEST_SO2.SO NOW!

[[email protected] so_src]# ./test_dl

Argument Error! You must enter like this:

./test_dl test_so1.so

備註:如果我們去掉test_so1.h和test_so2.h中的extern "C",重新編譯執行後將可能會出現什麼情況?有興趣的朋友可以試下:

[[email protected] so_src]# ./test_dl test_so1.so

/usr/lib/test_so1.so: undefined symbol: test

[[email protected] so_src]# ./test_dl test_so2.so

/usr/lib/test_so2.so: undefined symbol: test

4、  類的呼叫
載入類有點困難,因為我們需要類的一個例項,而不僅僅是一個函式指標。我們無法通過new來建立類的例項,因為類是在動態庫中定義的而不是在可執行程式中定義的,況且有時候我們連動態庫中具體的類的名字都不知道。

解決方案是:利用多型性!我們在可執行檔案中定義一個帶虛成員函式的介面基類,而在模組中定義派生實現類。通常來說,介面類是抽象的(如果一個類含有虛擬函式,那它就是抽象的)。因為動態載入類往往用於實現外掛,這意味著必須提供一個清晰定義的介面──我們將定義一個介面類和派生實現類。

接下來,在模組中,我們會定義兩個附加的類工廠函式(class factory functions)(或稱物件工廠函式)。其中一個函式建立一個類例項,並返回其指標;另一個函式則用以銷燬該指標。這兩個函式都以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() {}

    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 <iostream>

#include <dlfcn.h>

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 [email protected] $?

        cp [email protected] /usr/lib

a.out:$(OBJ2)

        $(GCC)  -o [email protected] $? -ldl

test_2.so:$(OBJ3)

        $(CC) -o [email protected] $?

        cp [email protected] /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,顯示結果如下:

[[email protected] c++_so_src]# ./a.out test_1.so

Running in test1.so Now

[[email protected] c++_so_src]# ./a.out test_2.so

Running in test2.so Now

[[email protected] c++_so_src]# ./a.out

Argument Error! You must enter like this:

./a.out test_1.so

三、        Windows下動態呼叫動態庫
備註:以下windows例項說明都是在Win7系統+visual studio 2005上實現。

1、  .dll動態庫的生成
使用visual studio 2005工具,建立一個新專案,選擇Win32——Win32控制檯應用程式(此處需選擇名稱及位置)——應用程式型別:DLL+附加選項:空專案,完成以上步驟即可建立一個dll專案。

在專案中的標頭檔案和原始檔、資原始檔中新增相應程式碼後,通過工具欄中Build(生成)即可生成相應dll檔案。dll檔案生成的位置通常在該專案位置中的debug目錄下。

2、  .dll動態庫的動態呼叫介面函式說明
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解除安裝動態庫。解除安裝成功返回true,否則返回false。

3、  普通函式的呼叫
使用visual studio 2005工具,建立一個新專案,選擇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後,結果如下:

4、  類的呼叫
使用visual studio 2005工具,建立一個新專案,選擇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() {}

    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 <string>

#include <iostream>

#include <windows.h>

#include <winuser.h>

#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 <iostream>

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 <cmath>

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 <iostream>

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 <cmath>

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

相關推薦

linux 呼叫動態so檔案

   關於動態呼叫動態庫方法說明 一、        動態庫概述 1、  動態庫的概念 日常程式設計中,常有一些函式不需要進行編譯或者可以在多個檔案中使用(如資料庫輸入/輸出操作或螢幕控制等標準任務函式)。可以事先對這些函式進行編譯,然後將它們放置在一些特殊的目的碼檔

linux與windows呼叫動態so dll檔案

 關於動態呼叫動態庫方法說明 一、        動態庫概述 1、  動態庫的概念 日常程式設計中,常有一些函式不需要進行編譯或者可以在多個檔案中使用(如資料庫輸入/輸出操作或螢幕控制等標準任務函式)。可以事先對這些函式進行編譯,然後將它們放置在一些特殊的目的碼檔案中

單目跟蹤位姿產品研發(二)----在linux下將c++工程打包成動態so檔案API

       單目跟蹤位姿專案由對方公司提供應用場景,我方研發核心演算法,通過c++實現功能,然後對方公司通過java\js來開發炫酷的介面,共同合作完成,最終對方公司負責銷售推廣,推向市場。因此,涉及到java介面呼叫c++核心程式碼的問題。 現記錄

linux關於執行編譯後命令找不到動態.so檔案的解決辦法。

方法一:(沒有root許可權)利用find找到報錯動態庫檔案目錄lib路徑,vim /home/xxx/.bashrc 新增一行: export LD_LIBRARY_PATH=動態庫/lib:$LD_LIBRARY_PATH 然後 source /home/xxx/.bashrc

linux下靜態.a和動態.so檔案的生成和使用

1.靜態庫是一些目標檔案(字尾名為.o)的集合體而已。 2.靜態庫的字尾名是.a,對應於windows作業系統的字尾名為.lib的靜態庫。 3.可以使用ar命令來建立一個靜態庫檔案。 來看一個例項,根據書中的程式碼簡化的,先看一看可以編譯成庫檔案的原始檔中的程式碼: /* test.c */ i

Linux下.h與動態.so檔案的路徑新增及gcc編譯的記錄

使用場景 當你在程式中加入一個非gcc預設搜尋路徑上的一個.h標頭檔案時,會報錯“No such file”,當你的程式需要動態連結一個.so庫時,在預設路徑裡找不到該庫,也會報錯。那麼,如何解決這兩種問題呢? gcc編譯使用“-I”選項 當頭檔案非標

Linux動態(.so)和靜態(.a) 的區別 Linux動態(.so)和靜態(.a) 的區別 動態(.so)連結靜態(.a)的情況總結

Linux下動態庫(.so)和靜態庫(.a) 的區別   靜態庫在程式編譯時會被連線到目的碼中,程式執行時將不再需要該靜態庫。編譯之後程式檔案大,但載入快,隔離性也好。 動態庫在程式編譯時並不會被連線到目的碼中,而是在程式執行是才被載入,因此在程式執行時還需要動態庫存在。多個

Android Studio打包APK後動態so檔案被改動

本人專案中遇到一個非常奇葩的問題,百思不得其解。 問題是這樣的: 由於專案是將C/C++層的程式碼與java程式碼分開管理的,所以C/C++程式碼沒有放進AS裡,編譯時將在linux下編譯好的動態庫直接放進AS工程的jniLibs下再打包生成APK。問題來了。 AS 打包A

GCC 編譯動態 so檔案時,靜態連結libmysqlclient.a 與動態連結引數一起使用問題。

直接放結論, 下面是編譯一個使用了mysql C API 的 mysql.so 檔案(我們的業務中,它是zbx加裁的modules so檔案) gcc -fPIC -shared -o mysql.so mysql.c \ -I/usr/include

linux下gcc編譯 .c檔案生成動態連結 .so檔案,並測試呼叫該連結

簡單介紹:linux中so檔案為共享庫,和windows下dll相似;so可以共多個程序呼叫,不同程序呼叫同一個so檔案,所使用so檔案不同;so原檔案不需要main函式;例項,1.通過mysqlTest.c中的函式mysql(),生成一個libmysql.so連結庫#inc

Linux下gcc編譯生成動態連結*.so檔案呼叫

動態庫*.so在linux下用c和c++程式設計時經常會碰到,最近在網站找了幾篇文章介紹動態庫的編譯和連結,總算搞懂了這個之前一直不太瞭解得東東,這裡做個筆記,也為其它正為動態庫連結庫而苦惱的兄弟們提供一點幫助。1、動態庫的編譯下面通過一個例子來介紹如何生成一個動態庫。這裡

c編譯,呼叫動態連線 (.so檔案)

c編譯,呼叫動態連線庫 (.so檔案) C編譯: 動態連線庫 (.so檔案) Linux動態連結庫.so檔案的命名及用途總結 Linux程式設計練習(二)—— Linux下.so動態庫的建立和呼叫 在“紙上談兵: 演算法與資料結構”中,我在每一篇都會有一個C程式,用於實現演算法和資料

JAVA呼叫動態連結so檔案

最近專案在用到openjdk1.8時,出現以下異常,綜其原因是JDK1.8中libjavajpeg.so估計缺少某些函式。 記錄一下,這裡有一篇關於動態連結庫的文章,比較不錯,轉載推薦一下:http://www.cnblogs.com/duanxz/p/3651347.html Exception in t

linux下檢視動態連結so檔案的依賴的相關組建

  我們很多c程式在windows下是以dll形式展現的,在linux則是以so 形式展現的。   windows一般不會因為編譯dll檔案的編譯器版本不同而出先dll檔案不能執行。   但是linux下,不同版本核心的linux下編譯的c程式,在其他版本的linux下就容易

linux下檢視動態連結so檔案的依賴的相關元件

  我們很多c程式在windows下是以dll形式展現的,在linux則是以so 形式展現的。   windows一般不會因為編譯dll檔案的編譯器版本不同而出先dll檔案不能執行。   但是linux下,不同版本核心的linux下編譯的c程式,在其他版本的linux下就容易出現無法執行的問題。主要可能是支

Linux動態連結 so檔案的建立與使用

1. 介紹         使用GNU的工具我們如何在Linux下建立自己的程式函式庫?一個“程式函式庫”簡單的說就是一個檔案包含了一些編譯好的程式碼和資料,這些編譯好的程式碼和資料可以在事後供其他的程式使用。程式函式庫可以使整個程

Linux動態.a與動態.so的生成與區別、以及.so檔案的封裝與使用

一、前言 如果有公司需要使用你們產品的一部分功能(通過程式碼呼叫這些功能),如果不想提供原始碼,那麼就可以通過封裝成庫檔案的形式提供給對方使用。本文主要介紹了生成動態庫與靜態庫檔案的過程、以及封裝和使用庫檔案的方法。 二、靜態庫.a與動態庫.so的生成與

linux下生成動態連結so檔案

怎樣在linux下生成動態連結庫即.so檔案? 一、 首先需要一個好的編譯工具,直接用gcc命令列編譯已經不再是一個明智之舉了,一個好的帶編譯工具的環境是很重要的,我選擇的是easyeclipse,它集成了CDT,可以很方便地編寫C和C++程式,它自動集成了gcc編譯器

linux動態so呼叫外部so,執行時出現undefined symbol

1、首先排查,C++呼叫了c的庫?是不是需要加上extern "c",尤其是類的動態庫,需要用到工廠模式,create一個物件出來,該工廠函式需要extern "c"宣告。 extern "C" CDbBase* create(); extern "C" void dest

Linux下如何檢視動態連結so檔案的依賴

最近專案用到了opencv,在測試環境編譯後生成了so檔案,在測試環境執行正常後準備在預發環境進行上線前的測試 但是System.loadLibrary(Core.NATIVE_LIBRARY_NAM