插件设计
插件设计
我们只需要在dll里面在继承这个虚基类 进行实现 便可实现com的设计
但是需要dll导出一个函数 这个函数的功能便是返回一个基类指针 子类对象的指针 这样在实
际使用中我们只需要获取一个对象指针 直接使用其功能程序便会自动查虚表进行调用子类实现的
功能 这样便实现了导出一个函数 可以使用各种功能的设计
装插件的时机一般在产生窗口后产生create消息来临的时候安装插件
c++版插件设计 之前c插件设计是在结构体中保存函数的地址。把结构体的指针传过来。 c++则是利用虚表的结构来实现的,通过返回类对象来查找对应的虚表函数。 通过继承纯虚基类实现函数的代码 class IShape { public: virtual const char* MY_CALL GetPuinName() = 0; virtual int MY_CALL OnSetBeginPoint(POINT pt) = 0; virtual int MY_CALL OnSetEndPoint(POINT pt) = 0; virtual int MY_CALL OnDraw(HDC hdc) = 0; virtual int Release() = 0; }; //返回类的对象来拿到虚表指针 IShape* MY_CALL MyGetClassObject(); //定义函数指针来方便调用 typedef IShape* (MY_CALL *MY_GET_CLASS_OBJECT)(); //拿到函数指针 MY_GET_CLASS_OBJECT pfnGetClassObj = (MY_GET_CLASS_OBJECT)GetProcAddress(hModule, "MyGetClassObject"); //拿到类对象 IShape *pObject = pfnGetClassObj(); //添加类对象 g_interfaces.push_back(pObject); //因为对象都是new出来的,所以要考虑到释放问题,只是放自己却不能影响到其他类,所以不能等析构,不然父类也会释放。释放时间WM_DESTROY消息: g_interfaces[i]->Release(); //自杀 virtual int Release() { delete this; return 0; }
原来的版本
在插件里导出函数
一般有 插件名和函数
应用程序中 :识别插件添加到菜单中
首先遍历插件目录,查找所以的插件,然后添加到菜单当中
//加载插件 WIN32_FIND_DATA FileData; HANDLE hSearch = FindFirstFile("./plugin/*.dll", &FileData); //如果有dll文件,专门为dll文件做一个主菜单 设置菜单名 InsertMenu(hMenu, 1, MF_BYPOSITION | MF_POPUP, (UINT_PTR)hMenu, "Shape(&S)"); //定义子菜单 HMENU hSubMenu = GetSubMenu(hMenu, 1); BOOL fFinished = TRUE; while (fFinished) { 相对路径加上文件名 std::string path("./plugin/"); path += FileData.cFileName; CadInterface interface; //加载插件 HMODULE hModule = LoadLibrary(path.c_str()); if (hModule != nullptr) { 这里需要拿所有的函数指针。遍历函数指针 for (int i = 0; i < sizeof(g_funName) / sizeof(g_funName[0]); i++) { 这里定义一个结构体,将函数名和指针定义一起,每次就一起拿。无需判断看下图特别说明 interface.interface[i].pfn = GetProcAddress(hModule, g_funName[i]); } 这里需要判断是不是插件,如果函数名为空就不添加菜单继续遍历下一个dll if (interface.interface[0].pfn != nullptr) { g_interfaces.push_back(interface); //添加子菜单,参数:句柄,类型(加入的方式),设置id,菜单名。菜单名用插件定义的 AppendMenu(hSubMenu, MF_STRING, WM_USER + index++, interface.interface[0].CR33CadGetPuinName()); } } 获取下一个文件信息 fFinished = FindNextFile(hSearch, &FileData); } 关闭HANDLE FindClose(hSearch);
因为大量的操作在应用中,使用起来很不方便,所以我们在插件中只提供一个数组指针保存着所有函数的地址,应用程序只用加载一个函数就可以了
vector<CadInterface*> g_interfaces; //所有插件接口 将插件用动态数组指针保存起来
插件头文件需要定义一个数组 来保存所有函数类型的定义,方便使用
//接口不变原则 struct CadInterface { const char* (__stdcall *GetPuinName)(); int (__stdcall *OnSetBeginPoint)(POINT pt); int(__stdcall *OnSetEndPoint)(POINT pt); int (__stdcall *OnDraw)(HDC hdc); void(__stdcall *New)(); };
定义宏为导出函数重命名 #define CAD_INTERFACE extern "C" __declspec(dllexport) CadInterface g_interfaces 应用中 interface = (CadInterface*)GetProcAddress(hModule, "g_interfaces"); 在dll中 实现函数指针的值就行了 一般在函数后面都会加入默认参数以防以后需要改变。或者加入函数指针以防添加 多参(void *p) 定义结构体指针 struct CadInterface { const char* (__stdcall *GetPuinName)(); int (__stdcall *OnSetBeginPoint)(POINT pt); int(__stdcall *OnSetEndPoint)(POINT pt); int (__stdcall *OnDraw)(HDC hdc); void(__stdcall *New)(); }; 定义结构体指针对象。并在dll中实现 CadInterface g_interfaces= { GetPuinName , OnSetBeginPoint, OnSetEndPoint, OnDraw, New };
学如逆水行舟,不进则退。
博客园技术交流群 群 号:1073255314
(本群没人,刚刚建立 -_-!!! )