IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> c/c++中如何实现动态库动态加载(含完整工程示例代码) -> 正文阅读

[C++知识库]c/c++中如何实现动态库动态加载(含完整工程示例代码)

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

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

? 一、动态库加载实现

??????? 在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_test
    bin            #程序输出目录及库文件输出父目录
        Debug        #win debug 库文件输出目录,编译时自动创建
        linux        #Linux库文件输出目录,编译时自动创建
        Release      #win Release库文件输出目录,编译时自动创建
    build_linux
        lib          #Linux库文件编译过程文件存储目录,编译时自动创建
    build_win
        lib          #win库文件编译过程文件存储目录,编译时自动创建
    lib
        CMakeLists.txt    #win库文件cmake工程
        metaObject.h    #win库文件,调用库文件的应用程序需要该头文件
        testlib.cpp     #库文件功能实现
    src
        dllload.h    #动态库加载、卸载、获取实例或函数地址的API集合
        dllload.cpp
        register.h    #动态库注册、管理、注销及库的类实例或函数调用实现
        register.cpp
        test.cpp     #应用程序,库文件使用测试代码
    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,用于生成库文件,作为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 <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 };//函数类型
	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<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 最低版本号要求
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})
#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")

# 指定生成目标
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_win
cmake -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.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

运行效果:

[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]$ 

四、附件

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

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

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-09-24 20:38:17  更:2022-09-24 20:38:58 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 16:19:40-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码