戏说 Windows GDI (3)

来源:互联网 发布:农村淘宝 编辑:程序博客网 时间:2024/05/17 08:32

继续讨论之前的问题,包括GDI对象的删除问题,窗口注册与销毁问题,最后用标尺的小例子说明问题

1.GDI对象的删除

由CGDIObject派生类创建的画笔、画刷和其他对象都要占用内存资源,因此在使用完毕之后我们必须要删除他们。当然,如果我们在栈上创建CPen、CBrush、CFont或其他CGDIObject,那么在CGDIObject超出范围时,相关的GDI对象就会自动地删除。如果用new在堆上创建了一个CGDIObject,那么我们就必须要手动的进行销毁,以便调用它的析构函数。可以多了解一下,与CGDIObject相关联的GDI对象可以通过调用CGDIObject::DeleteObject被显示地消除。

在之前的16位Windows中,GDI对象经常会引发资源泄漏的问题,这时候因为某些程序不能删除他们创建的GDI对象,因此Program Manager报告的可用系统资源的数量会随着应用程序的不断打开与终止渐渐减少。但是,在32位程序中,这个问题有所改善,Windows会跟踪所分配的资源,并在程序结束的时候释放他们。如果大量的画笔画刷没有被释放,他们就会占据Windows GDI的系统内存空间。很快,创建的画笔画刷的调用就会失败,应用程序的OnPaint处理程序也会莫名其妙的停止工作。

删除由用户创建的GDI对象是重要的,而不能删除已经选入设备描述表的GDI对象也同样不容忽视。试图用已删除的对象画图的程序代码是错误的。

下面的示例程序允许删除选入设备的画刷,我们可以分析一下其中的原因:

void CMainWindow::OnPain(){CpaintDC dc(this);CBrush brush(RGB(255,0,0));dc.SelectObject(&brush);dc.Ellipse(0,0,200,100);}
事情是这样的,CPaintDC对象和CBrush对象都是在栈上创建的。由于CBrush是第二个被创建的,所以特的析构函数会首先被调用,相关的GDI画刷在dc超出范围之前就被删除了。但是要强调一点,如果代码的健壮性依赖于栈变量的特定顺序来创建,那么你的程序就是相当糟糕的。就代码的维护性而言,这种程序简直太可怕了!

最好的解决办法就是:在CPaintDC对象超出范围之前将CBrush从设备描述表中提出。示例如下:


CPen pen ( PS_SOLID,1,RGB(255,0,0));CPen* pOldPen = dc.SelectObject( &pen);CBrush brush(RGB(0,0,255));CBrush* pOldBrush = dc.SelectObject( &brush);....dc.SelectObject(pOldPen);dc.SelectObject(pOldBrush);

2.窗口是如何消除的

我们研究的MFC程序很多都是在InitInstance中用下面的语句创建了一个窗口:

m_pMainWnd = new CMainWindow;
应该注意到,这个对象也是用new创建的,所以InitInstance结束之后,对象应该还存在内存之中。实际上,知道delete语句执行后,对象才会被清除。然而,在源程序中,我们似乎没有发现这条语句。这是因为MFC类库可以自己删除对象。更确切地说就是对象自己删除了自己。这个技巧的关键在于:窗口在消除之前接收到的最后一条消息是WM_NCDESTROY。在源代码的CWnd::OnNcDestroy中,我们会发现,程序里面调用了一个虚函数PostNcDestroy.CFrameWnd覆盖PostNcDestroy并执行了语句:

delete this;
因此在框架窗口被清除时,与窗口有关的对象也自动被删除了。框架窗口在用户关闭应用程序时被清除。

这里必须值得注意的是:CWnd自己实现的PostNcDestroy并没有删除相关的窗口对象,因此,如果我们程序中的窗口是直接从CWnd中派生而来的,我们必须对虚函数PostNcDestroy进行覆盖,也就是添加delete this语句。

3.标尺应用程序

效果图:


源代码:

CRuler.h

class CMyApp : public CWinApp{public:    virtual BOOL InitInstance ();};class CMainWindow : public CFrameWnd{public:    CMainWindow ();protected:    afx_msg void OnPaint ();    DECLARE_MESSAGE_MAP ()};

CRuler.c

#include <afxwin.h>#include "Ruler.h"CMyApp myApp;/////////////////////////////////////////////////////////////////////////// CMyApp member functionsBOOL CMyApp::InitInstance (){    m_pMainWnd = new CMainWindow;    m_pMainWnd->ShowWindow (m_nCmdShow);    m_pMainWnd->UpdateWindow ();    return TRUE;}/////////////////////////////////////////////////////////////////////////// CMainWindow message map and member functionsBEGIN_MESSAGE_MAP (CMainWindow, CFrameWnd)    ON_WM_PAINT ()END_MESSAGE_MAP ()CMainWindow::CMainWindow (){    Create (NULL, _T ("Ruler"));}void CMainWindow::OnPaint (){    CPaintDC dc (this);        //    // Initialize the device context.    //    dc.SetMapMode (MM_LOENGLISH);    dc.SetTextAlign (TA_CENTER | TA_BOTTOM);    dc.SetBkMode (TRANSPARENT);    //    // Draw the body of the ruler.    //    CBrush brush (RGB (255, 255, 0));    CBrush* pOldBrush = dc.SelectObject (&brush);    dc.Rectangle (100, -100, 1300, -200);    dc.SelectObject (pOldBrush);    //    // Draw the tick marks and labels.    //    for (int i=125; i<1300; i+=25) {        dc.MoveTo (i, -192);        dc.LineTo (i, -200);    }    for (int i=150; i<1300; i+=50) {        dc.MoveTo (i, -184);        dc.LineTo (i, -200);    }    for (int i=200; i<1300; i+=100) {        dc.MoveTo (i, -175);        dc.LineTo (i, -200);        CString string;        string.Format (_T ("%d"), (i / 100) - 1);        dc.TextOut (i, -175, string);    }}

0 0
原创粉丝点击