利用C++实现自定义插件
利用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相關信息。
在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 loadbreak;case DLL_PROCESS_DETACH:// detach from processbreak;case DLL_THREAD_ATTACH:// attach to threadbreak;case DLL_THREAD_DETACH:// detach from threadbreak;}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文件,返回空vectorreturn 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失敗,則釋放所有已加載dllfor (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;}}// 釋放所有dllfor (HINSTANCE hInst : modules)FreeLibrary(hInst);return 0; }運行
將所有插件編譯為dll文件并放入當前工程目錄下的plugins目錄中,啟動應用程序,插件自動被加載到程序中,得到結果如下圖所示:?
其他
事實上,Boost1.61.0 Beta中提供了一個新庫:DLL,設計這個庫的目的就是為了方便利用C++實現插件系統。
參考文獻
總結
以上是生活随笔為你收集整理的利用C++实现自定义插件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java-Socket实现文件的断点续传
- 下一篇: Windows 平台下的C++代理类(供