最好的GDI入门教程是《Window程序设计》的第五章,如果你没有任何GDI基础,最好精读这一章,因为本文并不会介绍GDI的方方面面,事实上这也是不可能完成的任务。我只将以前学习GDI时遇到的几个难点拿出来讲讲。
GDI对象的用法
GDI对象就是画笔,画刷,字体这类资源,以我的经验,GDI对象的管理是一件麻烦的事,如果操作不当,很容易引起GDI泄漏。
Delphi用TPen,TBrush,TFont三个类来表示画笔画刷和字体,用Canvas表示设备描述表。以TPen为例,一个TPen并不表示一个GDI对象,真正的GDI对象被保存在“池”里面,TPen只是根据自己的属性到“池”里面寻找对应的GDI对象,如果找不到将会创建一个新的,而这些GDI对象都有一个引用计数,代表它被多少个TPen对象引用,只要引用计数为0,这个GDI马上被DeleteObject并从“池”中移走。
各位可以想象Delphi对WinAPI封装到什么程度,这就是为什么你即使不会Windows编程也可以很快上手Delphi。这样封装的好处是非常明显的,你不必理会什么时候需要DeleteObject,你只要向Pen设置你喜欢的颜色,宽度,风格,然后调用Canvas的函数来绘制就行了。但它的坏处也非常明显,你设的样式越多,表明“池”中的GDI对象也会越多,Delphi程序的GDI普遍偏高就缘于此,更可怕的是我们对这种偏高的GDI数量有些束手无策。
MFC则完全不一样,它仅仅利用栈对象的自动消毁来简化GDI对象的管理,其余的差不多就是一层简单的包装。所以你必须了解GDI的用法,有下面三条规则,这是从Windows程序设计引过来的:
1. 最后必须删除自己创建的所有GDI对象。
2. 当GDI对象正在一个有效的DC中使用时,不要删除它。
3. 不要删除现有对象(StockObject)。
我们用简单的例子来说明这三条规则,请看下例:
HPEN hPen = ::CreatePen(PS_SOLID, 2, RGB(0xFF, 00, 00));
::SelectObject(hDC, hPen);
::Rectangle(hDC, 10, 10, 100, 100);
参照上面三条规则,明显违反了第一条规则,创建一个画笔之后,最后没有调用DeleteObject消毁hPen,这会发生什么事情呢,GDI对象不断的泄漏,在XP系统下GDI达到10000时程序就死掉了。
把代码修改如下: