利用C++实现插件系统
来源:互联网 发布:分期贷款的软件 编辑:程序博客网 时间:2024/05/16 05:28
利用C++实现插件系统
插件机制能够方便地扩展已有应用程序的功能。用C++实现插件机制的基本思路是:应用程序提供接口,由用户或第三方实现这些接口,并编译出相应的动态链接库(即插件);将所有插件放到某个特定目录,应用程序运行时会自动搜索该目录,并动态加载目录中的插件。
应用程序提供接口
为了实现功能扩展,应用程序必须向插件提供接口。在base.h中定义一个抽象类Base作为接口:
#ifndef BASE_H_#define BASE_H_class Base {public: virtual ~Base() = default; virtual void print(void) = 0; virtual double calc(double val) = 0;};#endif
实现插件
插件应该包含并实现应用程序提供的接口。在test1.h中定义Test1,让Test1继承并实现Base中提供的所有接口:
#ifndef TEST1_H_#define TEST1_H_#include <iostream>#include <cmath>#include "main.h"class Test1 : public Base {public: void print(void) { std::cout << "Hello Everybody! Test1!" << std::endl; } double calc(double val) { return sqrt(abs(val / 5 * 1.61)); }};#endif
为了让应用程序动态加载插件,需要将插件编译为dll文件。在main.h中,插件声明两个导出函数:
- getObj:用于新建一个Test1对象并返回该对象的指针;
- getName:用于打印Test1相关信息。
#ifndef __MAIN_HPP_INCLUDED__#define __MAIN_HPP_INCLUDED__#define BUILD_DLL#include <memory>#include <string>#include "base.h"#ifdef BUILD_DLL #define DLLAPI __declspec(dllexport)#else #define DLLAPI #endif // BUILD_DLL// DLL导出函数#ifdef __cplusplusextern "C" {#endif DLLAPI Base *getObj(void); DLLAPI const char* getName(void);#ifdef __cplusplus}#endif#endif // __MAIN_HPP_INCLUDED__
在main.cpp中,定义getObj和getName以及DLL入口DLLMain函数:
#include <iostream>#include <cmath>#include <windows.h>#include "main.h"#include "test1.h"extern "C" Base* getObj(void) { return new Test1;}extern "C" const char* getName(void) { return "Test1:Maths";}extern "C" DLLAPI BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved){ switch (fdwReason) { case DLL_PROCESS_ATTACH: // attach to process // return FALSE to fail DLL load break; case DLL_PROCESS_DETACH: // detach from process break; case DLL_THREAD_ATTACH: // attach to thread break; case DLL_THREAD_DETACH: // detach from thread break; } return TRUE; // succesful}
至此,一个插件就实现了。可以按照此方式实现多个dll插件。
实现应用程序
现在来写一个简单的应用程序,功能是加载plugins目录中的所有dll插件,打印出dll相关信息,并调用在插件中实现的函数。
首先,在my_exception.h中实现一个自己的异常类,用于捕获wstring类型的异常消息:
#ifndef MY_EXCEPTION_H_#define MY_EXCEPTION_H_#include <string>#include <stdexcept>class MyException : public std::runtime_error {public: MyException(const std::wstring &msg) : runtime_error("Error"), message_(msg) { } ~MyException() throw() {} std::wstring message() { return message_;}private: std::wstring message_;};#endif
然后,在main.cpp中实现应用程序的相关功能:
#include <iostream>#include <vector>#include <memory>#include <stdexcept>#include <exception>#include <windows.h>#include "my_exception.h"#include "base.h"// 功能:加载plugins目录中的所有dll插件// 参数:modules用于保存所有dll文件句柄,所有句柄最后会在main函数中被FreeLibrary()函数释放// 返回:std::vector<Base*> getPlugins(std::vector<HINSTANCE>& modules) { std::vector<Base*> ret; modules.clear(); // 在plugins目录中查找dll文件并将文件信息保存在fileData中 WIN32_FIND_DATA fileData; HANDLE fileHandle = FindFirstFile(L"plugins/*.dll", &fileData); if (fileHandle == (void*)ERROR_INVALID_HANDLE || fileHandle == (void*)ERROR_FILE_NOT_FOUND) { // 没有找到任何dll文件,返回空vector return std::vector<Base*>(); } // 循环加载plugins目录中的所有dll文件 do { typedef Base* (__cdecl *ObjProc)(void); typedef const char* (__cdecl *NameProc)(void); // 将dll加载到当前进程的地址空间中 HINSTANCE mod = LoadLibrary((L"./plugins/" + std::wstring(fileData.cFileName)).c_str()); if (!mod) { // 加载dll失败,则释放所有已加载dll for (HINSTANCE hInst : modules) FreeLibrary(hInst); throw MyException(L"Library " + std::wstring(fileData.cFileName) + L" wasn't loaded successfully!"); } // 从dll句柄中获取getObj和getName的函数地址 ObjProc objFunc = (ObjProc) GetProcAddress(mod, "getObj"); NameProc nameFunc = (NameProc) GetProcAddress(mod, "getName"); if (!objFunc || !nameFunc) throw std::runtime_error("Invalid Plugin DLL: both 'getObj' and 'getName' must be defined."); ret.push_back(objFunc()); // 保存objFunc(即getObj)生成的对象指针 modules.push_back(mod); // 保存dll句柄 std::clog << nameFunc() << " loaded!\n"; } while (FindNextFile(fileHandle, &fileData)); std::clog << std::endl; // 关闭文件句柄 FindClose(fileHandle); return ret;}int main() { std::vector<HINSTANCE> modules; { std::vector<Base*> objs; // 加载插件 try { objs = getPlugins(modules); } catch (const std::exception& e) { for (auto& x : objs) { delete x; } std::cerr << "Exception caught: " << e.what() << std::endl; return 1; } // 调用插件中对Base接口的实现 for (auto& x : objs) { x->print(); std::cout << "\t" << x->calc(10) << std::endl; } for (auto& x : objs) { delete x; } } // 释放所有dll for (HINSTANCE hInst : modules) FreeLibrary(hInst); return 0;}
运行
将所有插件编译为dll文件并放入当前工程目录下的plugins目录中,启动应用程序,插件自动被加载到程序中,得到结果如下图所示:
其他
事实上,Boost1.61.0 Beta中提供了一个新库:DLL,设计这个库的目的就是为了方便利用C++实现插件系统。关于DLL的使用方法,我将在另一篇文章中介绍。
参考文献
- Making a Plugin System, http://www.cplusplus.com/articles/48TbqMoL/
0 0
- 利用C++实现插件系统
- 利用C++实现插件系统
- 利用Linux系统函数实现线程池(C++)
- 利用反射API设计PHP插件系统
- 利用ReentrantReadWriteLock实现缓存系统
- 利用JNI实现JAVA插件开发
- 利用PHP实现插件式构架
- 利用Unity来实现插件开发
- 利用Unity来实现插件开发
- 利用FullPage.js实现全屏滚动插件
- 利用jsp+uploadify插件实现附件下载
- 利用fullpage.js插件实现全屏滚动
- 利用AccessibilityService实现“微信红包”插件
- 利用Java反射实现插件框架
- C语言 计时器的实现(利用系统55ms中断计时)
- 我是这样学习Linux下C语言编程的-利用RPC快速实现分布式系统
- Linux下C语言编程 利用RPC(Remote Procedure Call)快速实现分布式系统
- 分布式系统中利用zookeeper集群实现微服务主备切换代码片段(C语言版)
- 第一章:使用XMLHttpRequest对象
- 剑指offer(十一)之数值的整数次方
- 忘掉你的一万个小时吧
- AOP日志,记录调用类、方法、方法参数名称、方法参数值(包括对象和基本类型)
- 请你帮帮小王 (长沙理工大学第十一届程序设计竞赛)
- 利用C++实现插件系统
- JAVA常见问题解决办法汇总
- LeetCode-73. Set Matrix Zeroes
- php打包文件夹代码实现
- WEEK3-5
- 大话设计模式-状态模式
- iOS 8 自适应 Cell
- 代码大全读后感之一
- 动态规划—矩阵连乘问题