关于gdi泄漏问题注意的地方,询问了AI:
在 VC++ 中避免 GDI 泄漏需要注意以下几个关键方面:
1. 基本原则:成对使用
// 获取和释放必须配对
HDC hdc = GetDC(hWnd);
// 使用 hdc...
ReleaseDC(hWnd, hdc);
// 创建和删除必须配对
HBITMAP hBmp = CreateBitmap(...);
// 使用 hBmp...
DeleteObject(hBmp);2. 常见 GDI 对象及其释放方法
// Pen, Brush, Font, Bitmap, Region, Palette 等
HPEN hPen = CreatePen(PS_SOLID, 1, RGB(255,0,0));
HBRUSH hBrush = CreateSolidBrush(RGB(0,255,0));
HFONT hFont = CreateFont(...);
HBITMAP hBitmap = CreateCompatibleBitmap(...);
HRGN hRgn = CreateRectRgn(...);
HPALETTE hPalette = CreatePalette(...);
// 使用后必须释放
DeleteObject(hPen);
DeleteObject(hBrush);
DeleteObject(hFont);
DeleteObject(hBitmap);
DeleteObject(hRgn);
DeleteObject(hPalette);3. 特殊的 DC 获取方式
// 对于 GetDC()/ReleaseDC()
HDC hdc = GetDC(hWnd); // 获取窗口 DC
// 使用...
ReleaseDC(hWnd, hdc); // 必须用 ReleaseDC
HDC hdc = GetWindowDC(hWnd); // 获取包含非客户区的 DC
// 使用...
ReleaseDC(hWnd, hdc);
// 对于 BeginPaint()/EndPaint() - 只在 WM_PAINT 中使用
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// 绘制操作...
EndPaint(hWnd, &ps);
}
break;
// 创建和删除兼容 DC
HDC hMemDC = CreateCompatibleDC(hdc);
// 使用...
DeleteDC(hMemDC); // 不是 DeleteObject!4. 内存 DC 的正确使用
HBITMAP hOldBmp, hNewBmp;
HDC hMemDC = CreateCompatibleDC(hdc);
hNewBmp = CreateCompatibleBitmap(hdc, width, height);
hOldBmp = (HBITMAP)SelectObject(hMemDC, hNewBmp);
// 使用内存 DC 绘图...
// 恢复旧对象
SelectObject(hMemDC, hOldBmp);
// 先恢复再删除
DeleteObject(hNewBmp);
DeleteDC(hMemDC);5. 使用 RAII 包装类(推荐)
class GDIDeviceContext {
HDC m_hDC;
HWND m_hWnd;
public:
GDIDeviceContext(HWND hWnd) : m_hWnd(hWnd) {
m_hDC = GetDC(hWnd);
}
~GDIDeviceContext() {
if(m_hDC) ReleaseDC(m_hWnd, m_hDC);
}
operator HDC() const { return m_hDC; }
};
class GDIObj {
HGDIOBJ m_hObj;
public:
GDIObj(HGDIOBJ hObj) : m_hObj(hObj) {}
~GDIObj() {
if(m_hObj) DeleteObject(m_hObj);
}
operator HGDIOBJ() const { return m_hObj; }
};
// 使用示例
{
GDIDeviceContext dc(hWnd); // 自动释放 DC
GDIObj pen(CreatePen(PS_SOLID, 1, RGB(255,0,0))); // 自动删除对象
SelectObject(dc, pen);
// 绘图操作...
} // 自动清理6. 检测 GDI 泄漏的工具和方法
- 任务管理器:查看 GDI 对象计数(需开启对应列)
- GDIView 工具(NirSoft):监控进程的 GDI 对象
- Process Explorer:查看进程的 GDI 句柄
- Debug 版本:启用 GDI 调试功能
7. 常见陷阱
// 错误:SelectObject 后直接删除
HGDIOBJ hOld = SelectObject(hdc, hNewPen);
DeleteObject(hNewPen); // 错误!应该先恢复旧对象
// 正确做法
HGDIOBJ hOld = SelectObject(hdc, hNewPen);
// ... 使用新 pen ...
SelectObject(hdc, hOld); // 恢复旧对象
DeleteObject(hNewPen); // 现在可以删除
// 错误:多次删除同一对象
DeleteObject(hObj);
DeleteObject(hObj); // 错误!可能导致崩溃
// 错误:用错删除函数
DeleteObject(hDC); // 错误!DC 应该用 DeleteDC
DeleteDC(hPen); // 错误!GDI 对象用 DeleteObject8. 最佳实践
- 使用智能指针:如
std::unique_ptr配合自定义删除器 - 封装资源管理:创建资源管理类
- 在构造函数中获取,在析构函数中释放
- 检查返回值:确保 GDI 函数调用成功
- 避免在栈上保存 GDI 对象句柄
通过遵循这些规则,可以有效避免 VC++ 中的 GDI 泄漏问题。
使用GDIView检测的GDI泄漏情况,DC数量一直增加而没有得到释放
