第一篇技术文…
内存泄漏, 句柄泄漏.. windows 编程中最头痛的事情. windows 提供了查看内存检测内存泄漏的API, 却没有提供检测句柄的, 至少我不知道. 第三方的工具如BoundsChecker 倒是异常强大.实在不行并且看得懂nc 堆栈信息的话, 可以用用看M$自家的 AppVerifier .
今天碰到的问题很诡异. 在用工具检查完GDI 句柄泄漏之后, 跑了一上午某个绘图程序, 内存居然涨到了100+M, ft 啊. 于是代码一段段注掉, 找到底哪里漏了… 最后定位到这样的一段代码:
1 2 3 4 5 |
HDC hMemDC = ::CreateCompatibleDC(...); HBITMAP hBitmap = ::CreateCompatibleBitmap(...); ::DeleteObject(::SelectObject(hMemDC, hBitmap)); // draw something on hMemDC... ::DeleteObject(hBitmap); |
问题在于, 最后的DeleteObject() 调用不正确. 应改为如下:
1 2 3 4 5 6 |
HDC hMemDC = ::CreateCompatibleDC(...); HBITMAP hBitmap = ::CreateCompatibleBitmap(...); HBITMAP hOldBitmap = (HBITMAP)::SelectObject(hMemDC, hBitmap)); // draw something on hMemDC... ::SelectObject(hMemDC, hOldBitmap); ::DeleteObject(hBitmap); |
就是说, 一个HBITMAP 跟一个HDC 还有关联的时候, 不要调用DeleteObject(), 而应该先把原始的HBITMAP 句柄SelectObject() 还原回去. 有以下几条notes:
- Bitmaps must be disconnected from a DC before being freed.
- Menus must be freed before an application exits if the menu is not connected to any window.
- A DC must be disconnected from bitmaps before being freed.
- If a bitmap is specified for the Caret, it must be freed.
- Calls to DeleteObject do not harm stock objects.