目录

一、动态库加载实现

二、代码工程设计

三、编译及测试

四、附件


在很多项目中,我们多少会用到第三方动态库,这些动态库一般都是相对固定,使用也很简单,在工程中包含其头文件,并将动态库在编译时动态链接进去就能调用头文件接口实现调用。

但也有不少这种情况,就是我们需要自己创建一个个功能模块的动态库,然后给我们集成软件来调用。由于项目是不断迭代及增量开发,因此对于这些动态库的使用就会有按需加载、按时加载、按配置加载等设计,并需要满足不断增加功能支持。

一、动态库加载实现

在win下,其LoadLibrary、LoadLibrary、FreeLibrary等API实现库加载、卸载的能力,而GetProcAddress等API提供从加载模块中检索指定的动态链接库(DLL)中的输出库函数地址,依据传入模块句柄、函数名称相关参数就能实现动态链接库接口调用。

注:动态库本身必须使用关键字__declspec(dllexport),暴露dll中的变量或方法,编译dll文件的时候,在dll头文件声明的变量名称前添加dllexport。表明把 dll中的相关代码(类,函数,全局变量)暴露出来为以后其他应用程序使用;对于调用库文件的应用程序,__declspec(dllexport)是不必要,与_declspec(dllexport)相呼应的_declspec(dllimport),意思是当其他工程要使用dll 内部代码(类,函数,全局变量)时,就在dll头文件中声明的变量名称前添加dllimport关键字,虽然不是必须,但加入了代码会更明确,编译器也可以生成更好的代码;简要来说就是,动态库的cpp文件中加关键字__declspec(dllexport),动态库的h文件中加关键字_declspec(dllimport)。

在linux下,类似的,其提供了dlopen、dlclose等API实现库加载、卸载的能力,同等地,dlsym等API函数提供了从动态加载链接库中获取函数地址的能力,依据传入模块句柄、函数名称相关参数能实现动态链接库接口调用。

一般来说,动态库提供的接口服务有函数服务接口和类服务接口,对于前者,直接获取函数地址就可以实现调用,而对于类服务接口,则需要先获取类实例地址,在取得实例地址后,就相当于通常类使用那样,然后通过实例句柄实现其内部的函数调用。

二、代码工程设计

本文将将win、linux加载、卸载动态库,并从动态库链接模块中获取类实例或函数地址等封装成统一的API接口,并集成在dllLoad.h/dllLoad.cpp中实现。构建一个注册类RegisterM,内置一个map容器,用来装载加载的动态库模块,并统一提供模块索引、及从模块中实现类实例获取、删除、函数地址获取等功能。

在动态库实现方面,提供一个虚拟元类MetaObject,然后在库的cpp文件中建立子类继承该类,实现其具体功能,并在cpp文件中直接提供函数API,这些API函数不在头文件中声明,需要extern关键字修饰。

dll_testbin#程序输出目录及库文件输出父目录Debug#win debug 库文件输出目录,编译时自动创建linux#Linux库文件输出目录,编译时自动创建Release#win Release库文件输出目录,编译时自动创建build_linuxlib#Linux库文件编译过程文件存储目录,编译时自动创建build_winlib#win库文件编译过程文件存储目录,编译时自动创建libCMakeLists.txt#win库文件cmake工程metaObject.h#win库文件,调用库文件的应用程序需要该头文件testlib.cpp #库文件功能实现srcdllload.h#动态库加载、卸载、获取实例或函数地址的API集合dllload.cppregister.h#动态库注册、管理、注销及库的类实例或函数调用实现register.cpptest.cpp #应用程序,库文件使用测试代码CMakeLists.txt

metaObject.h

#ifndef METAOBJECT_H#define METAOBJECT_Hclass 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 #include #ifdef WIN32#ifdef __cplusplus#define EXPORT_DLL extern "C" __declspec(dllexport)#else#define EXPORT_DLL __declspec(dllexport)#endif#else#define EXPORT_DLL extern "C"#endifclass 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 factoriesEXPORT_DLL MetaObject* create() {return new MetaObject_child();}EXPORT_DLL void destroy(MetaObject* p) {delete p;}//the funs factoriesEXPORT_DLL void testfunc01(int a){std::cout << "a="<

CMakeLists.txt,用于生成库文件,作为subdir,被应用程序CMakeLists.txt调用

#lib项目信息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})#将库文件输出到Debug或Release目录下,文件目录编译时自动创建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)# 指定生成目标add_library(testlib SHARED ${source_h} ${source_cpp})endif(${WIN_OS})

dlload.h

#ifndef DLLOAD_H#define DLLOAD_H#if defined(WIN32)#include typedef HMODULEMODULE_HANDLE;#endif#if defined(__linux__)typedef void *MODULE_HANDLE;#endifMODULE_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 #endif#if defined(__linux__)#include #endifMODULE_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 #include "dlload.h"#include "metaObject.h"class RegisterM{public:enum MethodType { Method, Constructor };//函数类型enum Access { Private, Protected, Public };//访问方式enum CallType {Asynchronous,Synchronous};//函数调用方式enum SetType {SetVal,getVal};//属性值设置public:RegisterM(){};~RegisterM(){};//注册类库int registerObject(const char* objectName, const char* conf);//注销类库bool unregisterObject(const char* objectName);//创建实例类create_t* getInstance(const char* objectName);//析构实例类destroy_t* rmInstance(const char* objectName);//函数调用void* getFunc(const char* objectName,char* funcName);private:MODULE_HANDLE index ( const char * Name );private:std::map libmap;};#endif

register.cpp

#include "register.h"#include #include 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(objectName)]=load_handle;return 1;}bool RegisterM::unregisterObject(const char* objectName){std::map::iterator it=libmap.find(const_cast(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::iterator it=libmap.find(const_cast(Name));if (it!=libmap.end()){return it->second;}else{std::cerr << "Cannot find library: " << Name << '\n';}return NULL;}

test.cpp

#include "register.h"#include #include #include int main(int argc, char **argv) {RegisterM *rm = new RegisterM();char libname[128] = "test";#ifdef WIN32#ifdef _DEBUGchar libpath[128] = ".\\Debug\\testlibd.dll";#elsechar libpath[128] = ".\\Release\\testlib.dll";#endif#endif#ifdef __linux__char libpath[128] = "./linux/libtestlib.so";#endifint ret = rm->registerObject(libname,libpath);if (ret>0){std::cout<<"registerObject success"<getInstance(libname);if (NULL!=create_instance){std::cout<<"getInstance success"<add(7,8);std::cout<<"sum="<<_sum<setVal(15);std::cout<val="<getVal()<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)<rmInstance(libname);if (NULL!=destroy_instance){std::cout<<"rmInstance success"<<std::endl;destroy_instance(_instance);}}else{std::cout<<"getInstance failed"<unregisterObject(libname);if (re){std::cout<<"unregisterObject success"<<std::endl;}}return 0;};

CMakeLists.txt

# CMake 最低版本号要求cmake_minimum_required (VERSION 2.8)# 项目信息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)# 指定生成目标add_executable(dll_test ${source_h_lib} ${source_h_src} ${source_cpp_src})#linktarget_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")# 指定生成目标set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PROJECT_SOURCE_DIR}/bin)# 指定生成目标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)# 指定生成目标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})

三、编译及测试

win编译时采用cmake+vs编译,具体编译版本可以依据自身电脑安装版本决定

cd dll_test && mkdir build_win && cd build_wincmake -G "Visual Studio 10 2010 Win64" -DCMAKE_BUILD_TYPE=Release ..msbuild dll_test.sln /p:Configuration="Release" /p:Platform="x64"

运行效果:

D:\workForMy\workspace\dll_test\bin>dll_testd.exeregisterObject successgetInstance successsum=15_instance->val=15a=6fa(5)=25rmInstance successunregisterObject successD:\workForMy\workspace\dll_test\bin>

Linux下

cd dll_testmkdir build_linuxcd build_linuxcmake ..make

运行效果:

[py@pyfree bin]$ ./dll_test registerObject successgetInstance successsum=15_instance->val=15a=6fa(5)=25rmInstance successunregisterObject success[py@pyfree bin]$ 

四、附件

上述已经提供完备的案例信息,自行稍微组织一下项目结构就好。类似其他案例,也提供完整代码包

https://download.csdn.net/download/py8105/86626081