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