使用mfc扩展dll实现插件效果
2016-10-23 18:25
573 查看
概述
本文要解决的问题是,使用mfc设计具有对话框界面的程序,并且支持插件(数量不限),并且每个插件都可以有自己的界面,并且主程序和插件之间要能(通过接口)双向传递数据。开发环境
windows 10Visual Studio 2010
解决方案
主程序为任意mfc应用程序,插件为mfc扩展dll,动态链接。插件提供创建和销毁窗口的接口函数,创建接口创建窗口对象后将窗口指针或句柄返回给主程序,主程序使用完毕后调用销毁接口销毁窗口对象。主程序和插件之间使用消息或额外的接口函数传递数据。操作步骤
本章将创建一个例程,可动态加载插件。1、创建工程
主程序为任意mfc应用程序,插件为mfc扩展dll。笔者在制作这个例程时将主程序做成了基于对话框的程序,并且将主程序和插件放入了同一个解决方案里,但这些都不是必须的。
2、在插件中定义继承CWnd的类,实现插件的界面显示功能
先在资源中添加formview类型的资源,资源ID使用默认的IDD_FORMVIEW。然后资源上右键添加类,得到一个继承CDialogEx的类,类名我设的是TestDlg。
下面是vs自动生成的TestDlg.h
#pragma once #include "Resource.h" // TestDlg 对话框 class TestDlg : public CDialogEx { DECLARE_DYNAMIC(TestDlg) public: TestDlg(CWnd* pParent = NULL); // 标准构造函数 virtual ~TestDlg(); // 对话框数据 enum { IDD = IDD_FORMVIEW };//louObaichu:如果在这行报错提示IDD_FORMVIEW未定义,则需要#include "Resource.h" protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 DECLARE_MESSAGE_MAP() };
再然后在资源中添加控件,修改TestDlg的代码,实现插件的功能。例程只有一个静态控件,显示插件的名字。
上述步骤可以满足一般需求,但实际上资源的类型并不限于formview,甚至资源都不是必须的,如有特殊需要完全可以直接定义继承CWnd的类。
3、在插件中定义接口函数,并声明为dll导出函数
<pre name="code" class="cpp">// Plugin1.cpp : 定义 DLL 的初始化例程。 #include "stdafx.h" #include "TestDlg.h" #ifdef _DEBUG #define new DEBUG_NEW #endif extern HINSTANCE g_hDll;//DLL的模块句柄。可在DllMain中获得。 extern "C" __declspec(dllexport) CWnd* DLLAPI_Create(CWnd *pobjWnd) {//创建一个插件,以pobjWnd为父窗口,返回其指针。 HINSTANCE hRes=AfxGetResourceHandle(); TestDlg *pobjNew=new TestDlg(); AfxSetResourceHandle(g_hDll);//设置当前资源模块句柄。如果不设置且不同模块间资源ID有冲突,则pobjNew无法正确创建。 pobjNew->Create(TestDlg::IDD,pobjWnd); AfxSetResourceHandle(hRes); return pobjNew; } extern "C" __declspec(dllexport) int DLLAPI_Destroy(CWnd *pobjWnd) { TestDlg *pobjDes=(TestDlg *)pobjWnd; pobjDes->DestroyWindow(); delete pobjDes; return 0; }
例程的销毁接口将pobjWnd转换成了子类再操作,也可以直接操作pobjWnd,但必须确保所有被重写的操作在TestDlg的所有父类中都定义为虚函数。
如果有多个插件,所有插件的接口函数原型和名字必须相同。主程序加载插件时也必须指定同样的函数原型和名字。
4、主程序加/卸载插件
当主程序需要加载插件时,使用LoadLibrary和GetProcAddress函数获得接口函数的指针。需要显示插件界面时,通过创建接口创建插件,再使用CWnd的方法调整其位置和显隐。同理可通过销毁接口销毁插件,使用FreeLibrary卸载dll。例程通过控件输入插件的路径名,再加载插件并显示。下面是例程窗口类的源码:
// MainProDlg.h : 头文件 // #pragma once #include "afxeditbrowsectrl.h" #include "afxwin.h" // CMainProDlg 对话框 class CMainProDlg : public CDialogEx { // 构造 public: CMainProDlg(CWnd* pParent = NULL); // 标准构造函数 // 对话框数据 enum { IDD = IDD_MAINPRO_DIALOG }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 // 实现 protected: HICON m_hIcon; // 生成的消息映射函数 virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP() public: HMODULE m_hPlugin; CWnd* (*m_pfunCreate)(CWnd *pobjWnd); int (*m_pfunDestroy)(CWnd *pobjWnd); CWnd *m_pobjPlugin;//插件的界面 int LoadPlugin(CString strPlugin); int FreePlugin(); int ResetCtrls(int iWidth,int iHeight); CMFCEditBrowseCtrl m_EditPlugin; CButton m_ButtonLoad; afx_msg void OnBnClickedButton1(); afx_msg void OnSize(UINT nType, int cx, int cy); afx_msg void OnDestroy(); };
// MainProDlg.cpp : 实现文件 // #include "stdafx.h" #include "MainPro.h" #include "MainProDlg.h" #include "afxdialogex.h" #ifdef _DEBUG #define new DEBUG_NEW #endif #define HEIGHT 30 #define WIDTH 60 // 用于应用程序“关于”菜单项的 CAboutDlg 对话框 class CAboutDlg : public CDialogEx { public: CAboutDlg(); // 对话框数据 enum { IDD = IDD_ABOUTBOX }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 // 实现 protected: DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD) { } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx) END_MESSAGE_MAP() // CMainProDlg 对话框 CMainProDlg::CMainProDlg(CWnd* pParent /*=NULL*/) : CDialogEx(CMainProDlg::IDD, pParent) { m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); m_pobjPlugin=NULL; m_hPlugin=NULL; m_pfunCreate=NULL; m_pfunDestroy=NULL; } void CMainProDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Control(pDX, IDC_MFCEDITBROWSE1, m_EditPlugin); DDX_Control(pDX, IDC_BUTTON1, m_ButtonLoad); } BEGIN_MESSAGE_MAP(CMainProDlg, CDialogEx) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_BUTTON1, &CMainProDlg::OnBnClickedButton1) ON_WM_SIZE() ON_WM_DESTROY() END_MESSAGE_MAP() // CMainProDlg 消息处理程序 BOOL CMainProDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // 将“关于...”菜单项添加到系统菜单中。 // IDM_ABOUTBOX 必须在系统命令范围内。 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { BOOL bNameValid; CString strAboutMenu; bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX); ASSERT(bNameValid); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动 // 执行此操作 SetIcon(m_hIcon, TRUE); // 设置大图标 SetIcon(m_hIcon, FALSE); // 设置小图标 // TODO: 在此添加额外的初始化代码//[ CRect objRect; this->GetClientRect(objRect); this->ResetCtrls(objRect.Width(),objRect.Height()); m_EditPlugin.EnableFileBrowseButton(NULL,L"动态链接库 (*.dll)|*.dll|所有文件 (*.*)|*.*||"); //] return TRUE; // 除非将焦点设置到控件,否则返回 TRUE } void CMainProDlg::OnSysCommand(UINT nID, LPARAM lParam) { if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { CDialogEx::OnSysCommand(nID, lParam); } } // 如果向对话框添加最小化按钮,则需要下面的代码 // 来绘制该图标。对于使用文档/视图模型的 MFC 应用程序, // 这将由框架自动完成。 void CMainProDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // 用于绘制的设备上下文 SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0); // 使图标在工作区矩形中居中 int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // 绘制图标 dc.DrawIcon(x, y, m_hIcon); } else { CDialogEx::OnPaint(); } } //当用户拖动最小化窗口时系统调用此函数取得光标 //显示。 HCURSOR CMainProDlg::OnQueryDragIcon() { return static_cast<HCURSOR>(m_hIcon); } void CMainProDlg::OnBnClickedButton1() { // TODO: 在此添加控件通知处理程序代码 int i; CString strPlugin; m_EditPlugin.GetWindowText(strPlugin); FreePlugin(); i=this->LoadPlugin(strPlugin); if(i<0) this->MessageBox(L"error!"); return; } int CMainProDlg::LoadPlugin(CString strPlugin) { int iRet=-1,i; CRect objRect; this->GetClientRect(objRect); m_EditPlugin.GetWindowText(strPlugin); m_hPlugin=LoadLibrary(strPlugin); if(m_hPlugin==NULL) goto _Exit; m_pfunCreate=(CWnd* (*)(CWnd *pobjWnd))GetProcAddress(m_hPlugin,"DLLAPI_Create"); m_pfunDestroy=(int (*)(CWnd *pobjWnd))GetProcAddress(m_hPlugin,"DLLAPI_Destroy"); if(m_pfunCreate==NULL||m_pfunDestroy==NULL) goto _Exit; m_pobjPlugin=m_pfunCreate(this); this->ResetCtrls(objRect.Width(),objRect.Height()); m_pobjPlugin->ShowWindow(1); iRet=0; _Exit: if(iRet<0) FreePlugin(); return iRet; } int CMainProDlg::FreePlugin() { int iRet=0,i; if(m_hPlugin==NULL) goto _Exit; if(m_pobjPlugin) { m_pobjPlugin->DestroyWindow(); m_pfunDestroy(m_pobjPlugin); m_pobjPlugin=NULL; } FreeLibrary(m_hPlugin); m_hPlugin=NULL; m_pfunCreate=NULL; m_pfunDestroy=NULL; _Exit: return iRet; } void CMainProDlg::OnSize(UINT nType, int cx, int cy) { CDialogEx::OnSize(nType, cx, cy); // TODO: 在此处添加消息处理程序代码//[ ResetCtrls(cx,cy); //] } int CMainProDlg::ResetCtrls(int iWidth,int iHeight) { if(m_EditPlugin.GetSafeHwnd()) { if(iWidth>WIDTH&&iHeight>HEIGHT) { m_EditPlugin.MoveWindow(0,0,iWidth-WIDTH,HEIGHT); m_EditPlugin.ShowWindow(1); } else m_EditPlugin.ShowWindow(0); } if(m_ButtonLoad.GetSafeHwnd()) { if(iWidth>WIDTH&&iHeight>HEIGHT) { m_ButtonLoad.MoveWindow(iWidth-WIDTH,0,WIDTH,HEIGHT); m_ButtonLoad.ShowWindow(1); } else m_ButtonLoad.ShowWindow(0); } if(m_pobjPlugin) { if(iWidth>WIDTH&&iHeight>HEIGHT) { m_pobjPlugin->MoveWindow(0,HEIGHT,iWidth,iHeight); m_pobjPlugin->ShowWindow(1); } else m_pobjPlugin->ShowWindow(0); } return 0; } void CMainProDlg::OnDestroy() { CDialogEx::OnDestroy(); // TODO: 在此处添加消息处理程序代码//[ FreePlugin(); //] }
5、编译生成插件和主程序
运行结果如图所示:一个DLL包含多个插件
使用mfc扩展dll可以实现此效果,但必须设计更复杂的接口。最简单的做法是,增加一个load接口,主程序加载dll完毕后调用该接口函数获得所有插件的信息,同时创建接口增加一个参数,指明插件的标识符。因为所有插件都继承CWnd,所以不影响使用。---------------------------EOB-------------------------
相关文章推荐
- 实现MFC扩展DLL中导出类和对话框(这个好)
- 实现MFC扩展DLL中导出类和对话框
- 【转】一步一步实现MFC扩展DLL中导出类和对话框
- 仿MFC实现c++按名动态创建对象之续(瘦身以及解决其在dll中使用的问题)
- 一步一步实现MFC扩展DLL中导出类和对话框
- 实现MFC扩展DLL中导出类和对话框
- 使用Intent Filter实现插件和扩展
- 在asp.net中使用JQuery的SlideViewer插件实现图片的滚动效果
- 一步一步实现MFC扩展DLL中导出类和对话框
- 实现MFC扩展DLL中导出类和对话框
- 实现MFC扩展DLL中导出类和对话框 【转】
- 如何使用扩展MFC dll
- jquery框架中使用jqzoom插件实现图片放大镜效果
- 一步一步实现MFC扩展DLL中导出类和对话框
- 使用HTML5和jQuery插件Quicksand实现一个超酷的星际争霸2兵种分类展示效果
- 使用HTML5和jQuery插件Quicksand实现一个超酷的星际争霸2兵种分类展示效果
- 使用HTML5和jQuery插件Quicksand实现一个超酷的星际争霸2兵种分类展示效果
- 使用HTML5和jQuery插件Reel实现一个超酷的星际争霸2兵种动画360度预览效果
- 使用HTML5和jQuery插件Reel实现一个超酷的星际争霸2兵种动画360度预览效果
- 一步一步实现MFC扩展DLL中导出类和对话框