作者:朱金灿
来源:http://blog.csdn.net/clever101
有关VC编程中DLL封装对话框的资料网上多如牛毛,现在我想探究一下如何在DLL中封装框架窗口、文档和视图,略有所得,与诸君共享。我找到了两种DLL封装框架视图的方式。实际上用DLL封装框架视图涉及到两点:一是如何封装;二是确保窗口销毁之后的不出现内存泄露。(下面所有代码的开发环境为:VS C++ 2005+sp1, Win XP + sp2)
方式一 动态创建窗口
请不要误会,我不是指用C语言开发SDK的方式动态创建窗口的方式,当然这样是完全可以的,问题是这种太过于方式太过于复杂。微软已经封装好了MFC,我们没必要不领微软的情。我采用的是一种API和MFC结合的方式。我们把调用DLL模块的程序叫宿主程序,把封装窗体的DLL叫客户程序。
首先我们建一个使用共享MFC的规则DLL工程Custom1。我的基本想法是这样的:定义三个类:
类名 |
描述 |
备注 |
CCustomManage1 |
对外接口类,该类负责动态窗口的创建和销毁。 |
|
CCustomFrameWnd1
|
派生自CFrameWnd,为动态创建的框架窗口类。 |
|
CCustomView1
|
派生自CView,为动态创建的视图类。
|
|
大致的设计是这样的:在CCustomManage1类定义一个CCustomFrameWnd1类的指针,在CCustomFrameWnd1类定义一个CCustomView1类的指针。
主要创建代码如下:
- /*******************************************************************
- * 函数名称:CreateFrameWnd
- * 功 能:动态创建框架视图
- * 参 数:
- * 返回值:TRUE标识创建成功,FALSE表示创建失败
- *******************************************************************/
- extern CCustom1App theApp;
- BOOL CCustomManage1::CreateFrameWnd()
- {
- // 确保资源句柄有效
- AfxSetResourceHandle(theApp.m_hInstance);
- m_pFrmWnd = new CCustomFrameWnd1();
- CString wndClass = _T(""); // 用于保存窗口类名称
- try
- {
- // 注册窗口类
- wndClass = ::AfxRegisterWndClass(CS_DBLCLKS,0,::GetSysColorBrush(COLOR_BTNFACE), 0);
- }
- catch (CResourceException* pEx)
- {
- TCHAR szCause[255];
- CString strFormatted;
- pEx->GetErrorMessage(szCause, 255);
- strFormatted = _T("窗口创建失败原因: ");
- strFormatted += szCause;
- AfxMessageBox(strFormatted);
- pEx->Delete();
- return FALSE;
- }
- // 设定窗口的大小
- RECT rc;
- rc.top = 0;
- rc.left = 0;
- rc.bottom = 600;
- rc.right = 800;
- CString csWindowName = _T("使用动态创建的方式封装框架试图");
- // 正式创建窗口
- if( !m_pFrmWnd->Create(wndClass,csWindowName,WS_OVERLAPPEDWINDOW,rc,NULL,NULL))
- {
- TRACE0("创建窗口失败!\n");
- return FALSE;
- }
- m_pFrmWnd->ShowWindow(SW_NORMAL);
- // 创建视图
- m_pFrmWnd->CreateCustomView();
- AfxSetResourceHandle(AfxGetApp()->m_hInstance);
- return TRUE;
- }
- /*******************************************************************
- * 函数名称:CreateCustomView
- * 功 能:创建视图
- * 参 数:
- * 返回值:
- *******************************************************************/
- void CCustomFrameWnd1::CreateCustomView()
- {
- CCreateContext context;
- context.m_pCurrentDoc = NULL;
- context.m_pCurrentDoc = NULL;
- context.m_pCurrentFrame = this;
- context.m_pLastView = NULL;
- context.m_pNewDocTemplate = NULL;
- context.m_pNewViewClass = RUNTIME_CLASS(CCustomView1);
- m_pView = static_cast<CCustomView1*>(this->CreateView(&context));
- }
为了防止内存泄露,我们需要考虑防止如何销毁窗口。为了更好地说明这个问题,我先建一个调用该DLL的单文档工程Ower来说明这个问题。现在Ower工程的框架类CMainFrame类定义一个CCustomManage1类的私有变量:
- private:
- CCustomManage1 m_CustomManage1;
然后新建一个菜单项,在菜单项的命令响应函数里弹出新建窗口,具体代码如下:
- void CMainFrame::OnTest1()
- {
- // TODO: 在此添加命令处理程序代码
- m_CustomManage1.CreateFrameWnd();
- }
这时我们需要考虑用户是怎么关闭新建窗口,用户就是要么是单击调用程序的关闭按钮把两个窗口都关闭;要么单击DLL弹出的新建窗口的关闭按钮。那么在实现CCustomManage1类的DestroyFrameWnd函数里需要考虑这一点,防止用户先关闭新建窗口,再关闭调用程序时出错,就是要确保关闭时窗口句柄是有效的。
- /*******************************************************************
- * 函数名称:DestroyFrameWnd
- * 功 能:销毁动态创建的窗口
- * 参 数:
- * 返回值:
- *******************************************************************/
- void CCustomManage1::DestroyFrameWnd()
- {
- HWND hWnd = NULL;
- if (NULL!=m_pFrmWnd)
- {
- // 确保窗口的句柄有效,才进行销毁窗口操作
- hWnd = m_pFrmWnd->GetSafeHwnd();
- if(::IsWindow(hWnd))
- {
- m_pFrmWnd->DestroyWindow();
- }
- }
- }
- // 在析构函数里调用该函数
- CCustomManage1::~CCustomManage1(void)
- {
- DestroyFrameWnd();
- }
效果图如下:
当然你还可以测试在DLL的新建窗口的是否可以加载工具栏,响应Windows的标准消息。我测试过是可以的。
评论