插件设计

插件设计

我们只需要在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
 };

 

posted @ 2020-09-02 16:19  特权E5  阅读(181)  评论(0编辑  收藏  举报