戏说 Windows GDI (3)
继续讨论之前的问题,包括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);} }
戏说 Windows GDI (3)相关推荐
- 戏说 Windows GDI (2)
1. GDI画笔和CPen类 Windows用当前选入设备描述表的画笔绘制直线和曲线,并给用Rectangle.Ellipse以及其他图像生成函数画出的图形镶嵌边框.在系统默认的情况下,画笔是指一个像 ...
- 戏说 Windows GDI (1)
0.题记: 作为QT开发的支持者和践行者,明知道MFC已经过时,但是还不得不用:老板要求,项目组师兄弟代码兼容Balabala~也许这就是中国式项目研发. 好啦,聊点正经事...... 1.设备描述表 ...
- MFC和Win32之三___CGdiObject类和windows Gdi对象
小结: 前面讲到的windows窗口对象,在windows下用句柄来代表之,并且用了一个数据结构WNDCLASS(窗口类)来描述之.同理,windows的Gdi对象也有一些句柄来代表之(比如hPen等 ...
- Windows GDI和GDI+编程实例剖析(1)
Windows GDI和GDI+编程实例剖析(1) 作者:宋宝华 e-mail:21cnbao@21cn.com 1.基本概念 GDI在Windows中定义为Graphics Device Inte ...
- 原创]Windows Gdi入门初级应用(VC SDK)
原创]Windows Gdi入门初级应用(VC SDK) 好久没发贴了,今天手痒痒,发一个. GDI的绘图函数基本上都是有状态的,所有的函数都要求一个HDC类型的句柄. 这个HDC的获得有几个途径Be ...
- GetBitmapBits和GetDIBits的区别(Windows GDI)
Windows GDI中有两个用来得到位图图像数据的API,分别是GetBitmapBits和GetDIBits; 按照MSDN的解释,前者是用来得到设备独立位图的BITS, 后者是得到兼容位图的BI ...
- windows GDI+ 绘图的简单封装
由于用mfc经常需要在界面上进行一些绘制输出,所以用windows GDI比较多,但是用windowsGDI 绘图比较麻烦,刚开始学的时候还经常弄不清一堆DC, Object, HANDLE到底是干啥 ...
- Windows GDI
Windows GDI 在单任务环境如 MS-DOS 中,运行中的应用程序随时可自由地做它想做的事情,无论是在屏幕上画一条线,重新编写适配器的调色板,还是转换到另一种图像模式.而在窗口化多任务环境如 ...
- Windows GDI 窗口与 Direct3D 屏幕截图
前言 Windows 上,屏幕截图一般是调用 win32 api 完成的,如果 C# 想实现截图功能,就需要封装相关 api.在 Windows 上,主要图形接口有 GDI 和 DirectX.GDI ...
最新文章
- 容器管理大战:Kubernetes vs.Docker Swarm与Amazon ECS
- 黄聪:php精度计算问题
- Firefox浏览器的安装
- gprof使用介绍 (gcc -pg)
- 天池实验室Python能力测验
- !Important:CSS中!important的作用用于Ie6.0 与Ie7.0、firefox
- apache spark_Apache Spark中的自定义日志
- 登录微信用android设备,Android 之微信登录
- 计算机入门 姚班,清华“姚班”:学霸中的尖子生,大佬毕业后都去哪了?
- bzoj1037[ZJOI2008]生日聚会Party dp
- POJ1993+矩阵相乘
- Oracle ora-15070,查询字段过多触发了Oracle的BUG?【ORA-01465: 无效的十六进制数字】...
- DIV+CSS布局 - 弹性盒子布局
- python ocr 识别 开源_OCR识别-python版(一)
- 一步一步教你如何搭建自己的视频聚合站
- iphone模拟器目录
- java与模式.pdf_Java与模式(清晰书签版)PDF文档
- Python+Selenium+Appium环境搭建
- 使用word文件制作ftl模板文件总结(靠谱)
- Nginx的下载与安装及配置