DLL封装框架视图经验总结<一>

 

作者:朱金灿

来源:http://www.cnblogs.com/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的标准消息。我测试过是可以的。

 

 

posted @ 2009-11-07 14:31  是金子就会灿烂  阅读(1103)  评论(0编辑  收藏  举报