自动释放的GDI类

本文介绍了一组用于自动管理和释放Windows GDI资源的C++类,包括自动获取和释放窗口DC、创建兼容DC及位图等。这些类通过在析构函数中释放资源来简化编程流程,提高代码安全性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、目的

win32控制台或后台程序有时需要用到一些GDI对象,为避免手动释放的麻烦或的遗漏,封装一些类,在析构函数里面释放资源。

二、源码

#pragma once
/*************************************************  
Copyright:  mlizhi.com  
Author:     Juncheng Lin 
Date:       2017-01-01  
Description:自动释放GDI资源类的集合  
**************************************************/    

#include <Windows.h>

/**
	功能:	自动获取和释放窗口DC类
	类名:	CAutoWndDC
*/
class CAutoWndDC
{
public:
	/**
		功能:	构造函数
		参数:
				@hWnd	要获取画图上下文的窗口句柄
	*/
	CAutoWndDC(HWND hWnd)
		: m_hWnd(hWnd)
	{
		m_hDC = GetDC(hWnd);
	}

	//获取画图句柄
	HDC GetHandle() const
	{
		return m_hDC;
	}

	~CAutoWndDC()
	{
		if(m_hDC)
		{
			ReleaseDC(m_hWnd, m_hDC);
		}
	}
protected:
	HDC m_hDC;
	HWND m_hWnd;
};

/**
	功能:	自动创建兼容DC和释放的类
	类名:	CAutoCompatibleDC
*/
class CAutoCompatibleDC
{
public:
	/**
		功能:	构造函数
		参数:
				@hDC	源画图上下文
	*/
	CAutoCompatibleDC(HDC hDC)
		: m_hDC(CreateCompatibleDC(hDC))
	{
	}

	//获取画图句柄
	HDC GetHandle() const
	{
		return m_hDC;
	}

	~CAutoCompatibleDC()
	{
		if (m_hDC != NULL)
		{
			DeleteDC(m_hDC);
			m_hDC = NULL;
		}
	}
protected:
	HDC m_hDC;
};

/**
	功能:	自动创建兼容位图和释放的类
	类名:	CAutoCompatibleBitmap
*/
class CAutoCompatibleBitmap
{
public:
	/**
		功能:	构造函数
		参数:
				@hDC	源画图上下文
				@nWidth	位图的宽
				@nHeight	位图的高
	*/
	CAutoCompatibleBitmap(HDC hDC, int nWidth, int nHeight)
		: m_hBitmap(CreateCompatibleBitmap(hDC, nWidth, nHeight))
	{

	}

	//获取位图句柄
	HBITMAP GetHandle() const
	{
		return m_hBitmap;
	}

	~CAutoCompatibleBitmap()
	{
		if(m_hBitmap)
		{
			DeleteObject(m_hBitmap);
			m_hBitmap = NULL;
		}
	}
protected:
	HBITMAP m_hBitmap;
};

/**
	功能:	自动选择GDI对象的类
	类名:	CAutoDCSelBitmap
*/
class CAutoDCSelBitmap
{
public:
	/**
		功能:	构造函数
		参数:
				@hDC	源画图上下文
				@hBitmap	被选择的GDI对象
	*/
	CAutoDCSelBitmap(HDC hDC, HGDIOBJ hBitmap)
		: m_hOld(SelectObject(hDC, hBitmap)), m_hDC(hDC)
	{
	}
	~CAutoDCSelBitmap()
	{
		SelectObject(m_hDC, m_hOld);
	}
protected:
	HDC				m_hDC;
	HGDIOBJ			m_hOld;
};



<think>好的,用户现在问的是关于MFC中使用GDI+时的资源释放问题。之前用户问过关于MFC中CBrush的释放,现在可能是在开发图形应用程序时遇到了资源管理的问题,特别是升级到GDI+后,发现原有的资源管理方式有所不同,需要了解正确的释放方法。 首先,我需要回忆GDI+和传统GDI在资源管理上的区别。传统GDI对象如CBrush需要手动删除,或者利用MFC的自动析构,但GDI+使用的是不同的对象模型,比如Graphics、Pen、Brush等,这些对象需要显式释放,因为GDI+不依赖于MFC的自动管理机制。 用户可能已经遇到资源泄漏的问题,尤其是在长时间运行的应用程序中,GDI对象未正确释放会导致内存泄漏或程序崩溃。需要解释GDI+的基本释放原则,比如每个GDI+对象必须调用Delete或Dispose方法,或者通过析构函数自动处理,但要注意作用域。 接下来,我需要整理关键点。首先,GDI+对象的创建和释放方式,例如使用new创建的必须手动删除。其次,在MFC中如何结合使用GDI+,例如在OnPaint函数中初始化GDI+资源,使用后及时释放。还要提到GDI+的智能指针如std::unique_ptr来管理资源,以及常见的错误,如未调用GdiplusShutdown导致资源未完全释放。 另外,用户可能对GDI+的初始化和终止流程不太清楚,需要详细说明GdiplusStartup和GdiplusShutdown的正确使用,尤其是在应用程序启动和关闭时如何管理这些调用。同时,需要提醒在MFC的CView或CDialog派生中如何集成这些初始化步骤,比如在OnInitialUpdate或构造函数中启动,在析构函数中关闭。 还要考虑用户可能遇到的多线程问题,虽然GDI+本身对多线程的支持有限,但需要指出在MFC中通常在主线程操作GDI+对象,避免跨线程问题。此外,资源未恢复导致的上下文状态混乱也是常见错误,例如在设备上下文中选入Pen或Brush后没有恢复原对象,虽然GDI+在这方面可能没有传统GDI严格,但良好的习惯仍然重要。 最后,总结最佳实践,比如使用RAII封装、确保每个GDI+对象都有明确的释放点、利用调试工具检测泄漏等。用户可能需要具体的代码示例来理解如何正确释放不同型的GDI+对象,如Pen、Brush、Image等,并强调GdiplusShutdown的必要性,避免整个应用程序GDI+资源泄漏。 需要检查是否有遗漏的关键点,比如GDI+对象与MFC对象混合使用时的注意事项,或者某些特定对象如Graphics的特殊释放要求。确保回答结构清晰,分点说明,让用户能够逐步理解和应用正确的资源管理方法。</think>在 MFC 中使用 GDI+ 时,资源释放需要遵循与标准 GDI 不同的规则。以下是关键要点和操作规范: --- ### 一、GDI+ 资源释放基本原则 1. **显式释放原则** GDI+ 对象(如 `Pen`、`Brush`、`Image` 等)**必须显式释放**,不会自动随对象作用域销毁而释放 ```cpp Gdiplus::Pen* pPen = new Gdiplus::Pen(Color(255, 0, 0), 2); // 创建 // ... 使用操作 delete pPen; // 必须手动删除 ``` 2. **对象所有权明确** - 堆对象(`new` 创建)必须手动 `delete` - 栈对象通过析构函数自动释放(但需注意上下文关系) --- ### 二、MFC 集成关键流程 #### 1. GDI+ 初始化/终止 ```cpp // 在应用中声明全局变量 ULONG_PTR m_gdiplusToken; // 初始化(如 CWinApp::InitInstance) Gdiplus::GdiplusStartupInput gdiplusStartupInput; Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL); // 终止(如 CWinApp::ExitInstance) Gdiplus::GdiplusShutdown(m_gdiplusToken); ``` #### 2. 典型绘图场景 ```cpp void CMyView::OnDraw(CDC* pDC) { Gdiplus::Graphics graphics(pDC->GetSafeHdc()); // 创建 Graphics 对象 // 创建资源 Gdiplus::SolidBrush brush(Color(255, 0, 0)); Gdiplus::Pen pen(Color(255, 0, 0), 2); // 绘制操作 graphics.FillRectangle(&brush, 10, 10, 100, 50); graphics.DrawRectangle(&pen, 10, 10, 100, 50); // 无需手动释放 stack 对象(brush/pen 析构时自动处理) // Graphics 对象随栈帧结束自动销毁 } ``` --- ### 三、高级资源管理技巧 #### 1. 智能指针封装 ```cpp struct GDIPDeleter { template <typename T> void operator()(T* ptr) const { delete ptr; } }; // 使用示例 std::unique_ptr<Gdiplus::Bitmap, GDIPDeleter> spBitmap(Gdiplus::Bitmap::FromFile(L"test.png")); ``` #### 2. 复杂资源释放模式 ```cpp void DrawComplexShape(CDC* pDC) { Gdiplus::Graphics graphics(pDC->GetSafeHdc()); // 多级资源创建 Gdiplus::Image* pBgImage = Gdiplus::Image::FromFile(L"background.jpg"); Gdiplus::TextureBrush tBrush(pBgImage); // 必须按依赖顺序释放 delete pBgImage; // 先释放被依赖的 Image // tBrush 自动释放(栈对象) } ``` --- ### 四、常见错误排查 #### 1. 资源泄漏检测 ```cpp // 调试时监控 GDI+ 对象数量 #ifdef _DEBUG void CheckGDIPLeaks() { DWORD count = 0; Gdiplus::DllExports::GdipGetAllocatedObjects(nullptr, &count); TRACE("GDI+ Objects: %d\n", count); } #endif ``` #### 2. 典型错误案例 ```cpp // 错误示例:跨作用域泄漏 void LeakDemo() { Gdiplus::Bitmap* pBmp = Gdiplus::Bitmap::FromFile(L"temp.jpg"); // 忘记 delete pBmp → 内存泄漏! } // 正确做法 void SafeDemo() { std::unique_ptr<Gdiplus::Bitmap> spBmp( Gdiplus::Bitmap::FromFile(L"temp.jpg")); // 自动释放 } ``` --- ### 五、最佳实践建议 1. **统一释放模式** - 每个 `new` 对应一个 `delete` - 栈对象优先于堆对象 2. **异常安全处理** ```cpp void SafeDraw() { try { Gdiplus::Bitmap bmp(L"data.bin"); // 可能抛出异常 // 绘图操作... } catch (Gdiplus::Status s) { TRACE("GDI+ Error: %d\n", s); } } ``` 3. **多线程规范** - GDI+ **非线程安全**,建议: - 主线程专用于绘图 - 工作线程通过消息传递绘图指令 --- ### 总结对比表 | 特性 | GDI (CBrush等) | GDI+ | |--------------------|----------------------------|---------------------------| | 释放方式 | 手动 DeleteObject | 手动 delete | | 作用域自动释放 | MFC 对象自动销毁时释放 | 仅当对象是栈变量时自动释放 | | 上下文依赖 | 需要恢复 DC 原对象 | 无上下文状态依赖 | | 推荐管理模式 | RAII + MFC 封装 | 智能指针 + 异常保护 | 遵循这些规范可有效避免以下问题: - 界面闪烁(资源未及时释放) - 内存泄漏(GDI+ 对象未删除) - 绘图异常(多线程竞争资源)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值