C/C++實(shí)現(xiàn)動(dòng)態(tài)庫(kù)動(dòng)態(tài)加載
在很多項(xiàng)目中,我們多少會(huì)用到第三方動(dòng)態(tài)庫(kù),這些動(dòng)態(tài)庫(kù)一般都是相對(duì)固定,使用也很簡(jiǎn)單,在工程中包含其頭文件,并將動(dòng)態(tài)庫(kù)在編譯時(shí)動(dòng)態(tài)鏈接進(jìn)去就能調(diào)用頭文件接口實(shí)現(xiàn)調(diào)用。
但也有不少這種情況,就是我們需要自己創(chuàng)建一個(gè)個(gè)功能模塊的動(dòng)態(tài)庫(kù),然后給我們集成軟件來(lái)調(diào)用。由于項(xiàng)目是不斷迭代及增量開發(fā),因此對(duì)于這些動(dòng)態(tài)庫(kù)的使用就會(huì)有按需加載、按時(shí)加載、按配置加載等設(shè)計(jì),并需要滿足不斷增加功能支持。
一、動(dòng)態(tài)庫(kù)加載實(shí)現(xiàn)
在win下,其LoadLibrary、LoadLibrary、FreeLibrary等API實(shí)現(xiàn)庫(kù)加載、卸載的能力,而GetProcAddress等API提供從加載模塊中檢索指定的動(dòng)態(tài)鏈接庫(kù)(DLL)中的輸出庫(kù)函數(shù)地址,依據(jù)傳入模塊句柄、函數(shù)名稱相關(guān)參數(shù)就能實(shí)現(xiàn)動(dòng)態(tài)鏈接庫(kù)接口調(diào)用。
注:動(dòng)態(tài)庫(kù)本身必須使用關(guān)鍵字__declspec(dllexport),暴露dll中的變量或方法,編譯dll文件的時(shí)候,在dll頭文件聲明的變量名稱前添加dllexport。表明把 dll中的相關(guān)代碼(類,函數(shù),全局變量)暴露出來(lái)為以后其他應(yīng)用程序使用;對(duì)于調(diào)用庫(kù)文件的應(yīng)用程序,__declspec(dllexport)是不必要,與_declspec(dllexport)相呼應(yīng)的_declspec(dllimport),意思是當(dāng)其他工程要使用dll 內(nèi)部代碼(類,函數(shù),全局變量)時(shí),就在dll頭文件中聲明的變量名稱前添加dllimport關(guān)鍵字,雖然不是必須,但加入了代碼會(huì)更明確,編譯器也可以生成更好的代碼;簡(jiǎn)要來(lái)說(shuō)就是,動(dòng)態(tài)庫(kù)的cpp文件中加關(guān)鍵字__declspec(dllexport),動(dòng)態(tài)庫(kù)的h文件中加關(guān)鍵字_declspec(dllimport)。
在linux下,類似的,其提供了dlopen、dlclose等API實(shí)現(xiàn)庫(kù)加載、卸載的能力,同等地,dlsym等API函數(shù)提供了從動(dòng)態(tài)加載鏈接庫(kù)中獲取函數(shù)地址的能力,依據(jù)傳入模塊句柄、函數(shù)名稱相關(guān)參數(shù)能實(shí)現(xiàn)動(dòng)態(tài)鏈接庫(kù)接口調(diào)用。
一般來(lái)說(shuō),動(dòng)態(tài)庫(kù)提供的接口服務(wù)有函數(shù)服務(wù)接口和類服務(wù)接口,對(duì)于前者,直接獲取函數(shù)地址就可以實(shí)現(xiàn)調(diào)用,而對(duì)于類服務(wù)接口,則需要先獲取類實(shí)例地址,在取得實(shí)例地址后,就相當(dāng)于通常類使用那樣,然后通過(guò)實(shí)例句柄實(shí)現(xiàn)其內(nèi)部的函數(shù)調(diào)用。
二、代碼工程設(shè)計(jì)
本文將將win、linux加載、卸載動(dòng)態(tài)庫(kù),并從動(dòng)態(tài)庫(kù)鏈接模塊中獲取類實(shí)例或函數(shù)地址等封裝成統(tǒng)一的API接口,并集成在dllLoad.h/dllLoad.cpp中實(shí)現(xiàn)。構(gòu)建一個(gè)注冊(cè)類RegisterM,內(nèi)置一個(gè)map容器,用來(lái)裝載加載的動(dòng)態(tài)庫(kù)模塊,并統(tǒng)一提供模塊索引、及從模塊中實(shí)現(xiàn)類實(shí)例獲取、刪除、函數(shù)地址獲取等功能。
在動(dòng)態(tài)庫(kù)實(shí)現(xiàn)方面,提供一個(gè)虛擬元類MetaObject,然后在庫(kù)的cpp文件中建立子類繼承該類,實(shí)現(xiàn)其具體功能,并在cpp文件中直接提供函數(shù)API,這些API函數(shù)不在頭文件中聲明,需要extern關(guān)鍵字修飾。
dll_test bin #程序輸出目錄及庫(kù)文件輸出父目錄 Debug #win debug 庫(kù)文件輸出目錄,編譯時(shí)自動(dòng)創(chuàng)建 linux #Linux庫(kù)文件輸出目錄,編譯時(shí)自動(dòng)創(chuàng)建 Release #win Release庫(kù)文件輸出目錄,編譯時(shí)自動(dòng)創(chuàng)建 build_linux lib #Linux庫(kù)文件編譯過(guò)程文件存儲(chǔ)目錄,編譯時(shí)自動(dòng)創(chuàng)建 build_win lib #win庫(kù)文件編譯過(guò)程文件存儲(chǔ)目錄,編譯時(shí)自動(dòng)創(chuàng)建 lib CMakeLists.txt #win庫(kù)文件cmake工程 metaObject.h #win庫(kù)文件,調(diào)用庫(kù)文件的應(yīng)用程序需要該頭文件 testlib.cpp #庫(kù)文件功能實(shí)現(xiàn) src dllload.h #動(dòng)態(tài)庫(kù)加載、卸載、獲取實(shí)例或函數(shù)地址的API集合 dllload.cpp register.h #動(dòng)態(tài)庫(kù)注冊(cè)、管理、注銷及庫(kù)的類實(shí)例或函數(shù)調(diào)用實(shí)現(xiàn) register.cpp test.cpp #應(yīng)用程序,庫(kù)文件使用測(cè)試代碼 CMakeLists.txt
metaObject.h
#ifndef METAOBJECT_H #define METAOBJECT_H class MetaObject { public: MetaObject(){}; virtual ~MetaObject(){}; virtual int add(int a, int b) const = 0; virtual void setVal(int _val) =0; virtual int getVal() const = 0; }; // typedef MetaObject* create_t(); typedef void destroy_t(MetaObject*); //typedef _declspec(dllimport) void destroy_t(MetaObject*); //window #endif
testlib.cpp
#include "metaObject.h" #include <iostream> #include <string> #ifdef WIN32 #ifdef __cplusplus #define EXPORT_DLL extern "C" __declspec(dllexport) #else #define EXPORT_DLL __declspec(dllexport) #endif #else #define EXPORT_DLL extern "C" #endif class MetaObject_child : public MetaObject { public: virtual int add(int a, int b) const { return a+b; }; virtual void setVal(int _val){ val = _val; }; virtual int getVal() const{ return val; }; private: int val; }; // the class factories EXPORT_DLL MetaObject* create() { return new MetaObject_child(); } EXPORT_DLL void destroy(MetaObject* p) { delete p; } //the funs factories EXPORT_DLL void testfunc01(int a) { std::cout << "a="<<a<<std::endl; } EXPORT_DLL int testfunc02(int b) { return b*b; }
CMakeLists.txt,用于生成庫(kù)文件,作為subdir,被應(yīng)用程序CMakeLists.txt調(diào)用
#lib項(xiàng)目信息 project (testlib) # SET(source_h ${PROJECT_SOURCE_DIR}/metaObject.h ) SET(source_cpp ${PROJECT_SOURCE_DIR}/testlib.cpp ) #頭文件目錄 include_directories(${PROJECT_SOURCE_DIR}) # if (${WIN_OS}) #將庫(kù)文件輸出到Debug或Release目錄下,文件目錄編譯時(shí)自動(dòng)創(chuàng)建 set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/../bin) if (CMAKE_BUILD_TYPE STREQUAL "Debug") add_library(testlibd SHARED ${source_h} ${source_cpp}) else(CMAKE_BUILD_TYPE) add_library(testlib SHARED ${source_h} ${source_cpp}) endif (CMAKE_BUILD_TYPE) else(${WIN_OS}) set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/../bin/linux) # 指定生成目標(biāo) add_library(testlib SHARED ${source_h} ${source_cpp}) endif(${WIN_OS})
dlload.h
#ifndef DLLOAD_H #define DLLOAD_H #if defined(WIN32) #include <windows.h> typedef HMODULE MODULE_HANDLE; #endif #if defined(__linux__) typedef void * MODULE_HANDLE; #endif MODULE_HANDLE gdl_Open(const char *plname); void gdl_Close(MODULE_HANDLE h); void *gdl_GetProc(MODULE_HANDLE h, const char *pfname); char* gdl_GetLastError(); #endif
dlload.cpp
#include "dlload.h" #if defined(WIN32) #include <windows.h> #endif #if defined(__linux__) #include <dlfcn.h> #endif MODULE_HANDLE gdl_Open(const char *plname) { #if defined(WIN32) return LoadLibraryA (plname); #endif #if defined(__linux__) return dlopen( plname, RTLD_NOW|RTLD_GLOBAL); #endif }; void gdl_Close(MODULE_HANDLE h) { if(h) { #if defined(WIN32) FreeLibrary(h); #endif #if defined(__linux__) dlclose (h); #endif } }; void *gdl_GetProc(MODULE_HANDLE h, const char *pfname) { if(h) { #if defined(WIN32) return (void *)GetProcAddress(h, pfname); #endif #if defined(__linux__) return dlsym(h,pfname); #endif } return 0; }; char* gdl_GetLastError() { #if defined(WIN32) return (char*)::GetLastError(); #endif #if defined(__linux__) return dlerror(); #endif }
register.h
#ifndef REGISTER_H #define REGISTER_H #include <map> #include "dlload.h" #include "metaObject.h" class RegisterM { public: enum MethodType { Method, Constructor };//函數(shù)類型 enum Access { Private, Protected, Public };//訪問(wèn)方式 enum CallType {Asynchronous,Synchronous};//函數(shù)調(diào)用方式 enum SetType {SetVal,getVal};//屬性值設(shè)置 public: RegisterM(){}; ~RegisterM(){}; //注冊(cè)類庫(kù) int registerObject(const char* objectName, const char* conf); //注銷類庫(kù) bool unregisterObject(const char* objectName); //創(chuàng)建實(shí)例類 create_t* getInstance(const char* objectName); //析構(gòu)實(shí)例類 destroy_t* rmInstance(const char* objectName); //函數(shù)調(diào)用 void* getFunc(const char* objectName,char* funcName); private: MODULE_HANDLE index ( const char * Name ); private: std::map<char *, MODULE_HANDLE> libmap; }; #endif
register.cpp
#include "register.h" #include <stdio.h> #include <iostream> int RegisterM::registerObject(const char* objectName, const char* conf) { MODULE_HANDLE load_handle= gdl_Open(conf); if (!load_handle) { std::cerr << "Cannot load library: " << conf << " Error:" << gdl_GetLastError() << '\n'; return -1; } libmap[const_cast<char*>(objectName)]=load_handle; return 1; } bool RegisterM::unregisterObject(const char* objectName) { std::map<char *, MODULE_HANDLE>::iterator it=libmap.find(const_cast<char*>(objectName)); if (it!=libmap.end()) { gdl_Close(it->second); libmap.erase(it); return true; } return false; } create_t* RegisterM::getInstance(const char* objectName) { MODULE_HANDLE _handle = index(objectName); if (NULL!=_handle) { create_t* create_instance = (create_t*) gdl_GetProc(_handle, "create"); const char* dlsym_error = gdl_GetLastError(); if (dlsym_error) { std::cerr << "Cannot load symbol create error: " << dlsym_error << '\n'; return NULL; }else{ return create_instance; } } return NULL; } destroy_t* RegisterM::rmInstance(const char* objectName) { MODULE_HANDLE _handle = index(objectName); if (NULL!=_handle) { destroy_t* destroy_instance = (destroy_t*) gdl_GetProc(_handle, "destroy"); const char* dlsym_error = gdl_GetLastError(); if (dlsym_error) { std::cerr << "Cannot load symbol create: " << dlsym_error << '\n'; return NULL; }else{ return destroy_instance; } } return NULL; } void* RegisterM::getFunc(const char* objectName,char* funcName) { MODULE_HANDLE _handle = index(objectName); if (NULL!=_handle) { void* ret = gdl_GetProc(_handle,funcName); const char* dlsym_error = gdl_GetLastError(); if (dlsym_error) { std::cerr << "Cannot load symbol create: " << dlsym_error << '\n'; return NULL; }else{ return ret; } } return NULL; } MODULE_HANDLE RegisterM::index ( const char * Name ) { std::map<char *, MODULE_HANDLE>::iterator it=libmap.find(const_cast<char*>(Name)); if (it!=libmap.end()) { return it->second; }else{ std::cerr << "Cannot find library: " << Name << '\n'; } return NULL; }
test.cpp
#include "register.h" #include <stdint.h> #include <iostream> #include <string> int main(int argc, char **argv) { RegisterM *rm = new RegisterM(); char libname[128] = "test"; #ifdef WIN32 #ifdef _DEBUG char libpath[128] = ".\\Debug\\testlibd.dll"; #else char libpath[128] = ".\\Release\\testlib.dll"; #endif #endif #ifdef __linux__ char libpath[128] = "./linux/libtestlib.so"; #endif int ret = rm->registerObject(libname,libpath); if (ret>0) { std::cout<<"registerObject success"<<std::endl; create_t* create_instance = rm->getInstance(libname); if (NULL!=create_instance) { std::cout<<"getInstance success"<<std::endl; MetaObject* _instance = create_instance(); int _sum = _instance->add(7,8); std::cout<<"sum="<<_sum<<std::endl; _instance->setVal(15); std::cout<<"_instance->val="<<_instance->getVal()<<std::endl; void (*pa)(int a); *(void**)(&pa) = rm->getFunc((char*)libname,(char*)"testfunc01"); pa(6); int (*fa)(int a); *(void**)(&fa) = rm->getFunc((char*)libname,(char*)"testfunc02"); std::cout<<"fa(5)="<<fa(5)<<std::endl; destroy_t* destroy_instance = rm->rmInstance(libname); if (NULL!=destroy_instance) { std::cout<<"rmInstance success"<<std::endl; destroy_instance(_instance); } }else{ std::cout<<"getInstance failed"<<std::endl; } bool re = rm->unregisterObject(libname); if (re) { std::cout<<"unregisterObject success"<<std::endl; } } return 0; };
CMakeLists.txt
# CMake 最低版本號(hào)要求 cmake_minimum_required (VERSION 2.8) # 項(xiàng)目信息 project (dll_test) # if(WIN32) message(STATUS "windows compiling...") add_definitions(-D_PLATFORM_IS_WINDOWS_) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd") set(WIN_OS true) else(WIN32) message(STATUS "linux compiling...") add_definitions( -D_PLATFORM_IS_LINUX_) add_definitions("-Wno-invalid-source-encoding") # add_definitions("-O2") set(UNIX_OS true) set(_DEBUG true) endif(WIN32) # set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) # 指定源文件的目錄,并將名稱保存到變量 SET(source_h_lib ${PROJECT_SOURCE_DIR}/lib/metaObject.h ) SET(source_h_src ${PROJECT_SOURCE_DIR}/src/dlload.h ${PROJECT_SOURCE_DIR}/src/register.h ) SET(source_cpp_src ${PROJECT_SOURCE_DIR}/src/dlload.cpp ${PROJECT_SOURCE_DIR}/src/register.cpp ${PROJECT_SOURCE_DIR}/src/test.cpp ) #頭文件目錄 include_directories( ${PROJECT_SOURCE_DIR}/lib ${PROJECT_SOURCE_DIR}/src ) add_subdirectory(${PROJECT_SOURCE_DIR}/lib ./lib) if (${UNIX_OS}) add_definitions( "-W" "-fPIC" "-Wall" # "-Wall -g" "-Werror" "-Wshadow" "-Wformat" "-Wpointer-arith" "-D_REENTRANT" "-D_USE_FAST_MACRO" "-Wno-long-long" "-Wuninitialized" "-D_POSIX_PTHREAD_SEMANTICS" "-DACL_PREPARE_COMPILE" "-Wno-unused-parameter" "-fexceptions" ) set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0") link_directories( "${EXECUTABLE_OUTPUT_PATH}"/linux ) # 指定生成目標(biāo) add_executable(dll_test ${source_h_lib} ${source_h_src} ${source_cpp_src}) #link target_link_libraries(dll_test testlib -ldl) endif(${UNIX_OS}) if (${WIN_OS}) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4819") add_definitions( "-D_CRT_SECURE_NO_WARNINGS" "-D_WINSOCK_DEPRECATED_NO_WARNINGS" "-DNO_WARN_MBCS_MFC_DEPRECATION" "-DWIN32_LEAN_AND_MEAN" ) #link_directories() if (CMAKE_BUILD_TYPE STREQUAL "Debug") # 指定生成目標(biāo) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PROJECT_SOURCE_DIR}/bin) # 指定生成目標(biāo) add_executable(dll_testd ${source_h_lib} ${source_h_src} ${source_cpp_src}) link_directories( "${EXECUTABLE_OUTPUT_PATH}"/Debug ) else(CMAKE_BUILD_TYPE) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/bin) # 指定生成目標(biāo) add_executable(dll_test ${source_h_lib} ${source_h_src} ${source_cpp_src}) link_directories( "${EXECUTABLE_OUTPUT_PATH}"/Release ) endif (CMAKE_BUILD_TYPE) endif(${WIN_OS})
三、編譯及測(cè)試
win編譯時(shí)采用cmake+vs編譯,具體編譯版本可以依據(jù)自身電腦安裝版本決定
cd dll_test && mkdir build_win && cd build_win
cmake -G "Visual Studio 10 2010 Win64" -DCMAKE_BUILD_TYPE=Release ..
msbuild dll_test.sln /p:Configuration="Release" /p:Platform="x64"
運(yùn)行效果:
D:\workForMy\workspace\dll_test\bin>dll_testd.exe
registerObject success
getInstance success
sum=15
_instance->val=15
a=6
fa(5)=25
rmInstance success
unregisterObject success
D:\workForMy\workspace\dll_test\bin>
Linux下
cd dll_test
mkdir build_linux
cd build_linux
cmake ..
make
運(yùn)行效果:
[py@pyfree bin]$ ./dll_test
registerObject success
getInstance success
sum=15
_instance->val=15
a=6
fa(5)=25
rmInstance success
unregisterObject success
[py@pyfree bin]$
以上就是C/C++實(shí)現(xiàn)動(dòng)態(tài)庫(kù)動(dòng)態(tài)加載的詳細(xì)內(nèi)容,更多關(guān)于C++動(dòng)態(tài)庫(kù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
VC中使用ADO開發(fā)數(shù)據(jù)庫(kù)應(yīng)用程序簡(jiǎn)明教程
這篇文章主要介紹了VC中使用ADO開發(fā)數(shù)據(jù)庫(kù)應(yīng)用程序的方法,結(jié)合實(shí)例形式詳細(xì)講述了ADO的原理及VC使用ADO開發(fā)數(shù)據(jù)庫(kù)應(yīng)用程序的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2016-06-06C++中函數(shù)使用的基本知識(shí)學(xué)習(xí)教程
這篇文章主要介紹了C++中函數(shù)使用的基本知識(shí)學(xué)習(xí)教程,涵蓋了函數(shù)的聲明和參數(shù)以及指針等各個(gè)方面的知識(shí),非常全面,需要的朋友可以參考下2016-01-01Qt 使用QDialog實(shí)現(xiàn)界面遮罩的示例(蒙版)
界面遮罩在很多時(shí)候都可以用到,例如彈窗,本文主要介紹了Qt 使用QDialog實(shí)現(xiàn)界面遮罩的示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04詳解C++中遞增運(yùn)算符重載的實(shí)現(xiàn)
本文主要詳解運(yùn)算符重載里的遞增運(yùn)算符重載;遞增和遞減原理是一樣的,這里就只分享遞增的重載;提到遞增遞減,我們都知道又前置和后置兩種方法, 那今天就詳解一下前置遞增和后置遞增的細(xì)節(jié),拿捏遞增運(yùn)算符重載2022-06-06C++實(shí)現(xiàn)將輸入復(fù)制到輸出的方法
這篇文章主要介紹了C++實(shí)現(xiàn)將輸入復(fù)制到輸出的方法,實(shí)例分析了C++字符串轉(zhuǎn)換及輸入輸出操作的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07C語(yǔ)言實(shí)現(xiàn)變色進(jìn)度條
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)一個(gè)變色的進(jìn)度條,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01C++ 關(guān)于 CMFCPropertyGridCtrl 的使用方法
這篇文章主要介紹了C++ 關(guān)于 CMFCPropertyGridCtrl 的使用方法的相關(guān)資料,需要的朋友可以參考下2015-06-06詳解C++編程中標(biāo)記語(yǔ)句與復(fù)合語(yǔ)句的寫法
這篇文章主要介紹了C++編程中標(biāo)記語(yǔ)句與復(fù)合語(yǔ)句的寫法,是C++入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2016-01-01