详细介绍CDC::FromHandle()
·
书籍:《Visual C++ 2017从入门到精通》的4.2 按钮控件
环境:visual studio 2022
内容:[例 4.2]制作图片按钮
说明:以上内容大部分来自腾讯元宝。
在 MFC 中,CDC::FromHandle() 是连接 Windows 原生设备上下文(HDC)与 MFC 设备上下文类(CDC)的核心接口。以下从功能、实现机制、使用场景及注意事项等方面进行详细分析:
一、功能概述
CDC::FromHandle(HDC hDC) 的作用是将 Windows 原生设备上下文句柄(HDC)封装为 MFC 的 CDC 类对象,提供对设备上下文的面向对象操作接口。
核心价值:
- 封装性:隐藏底层 GDI 函数调用细节,通过 MFC 成员函数(如
DrawText、Rectangle)简化绘图操作。 - 资源管理:自动维护 HDC 与 CDC 的映射关系,避免直接操作句柄带来的风险。
- 兼容性:支持从不同来源(如窗口、内存 DC)获取 HDC 并转换为 CDC。
二、实现原理
1. 源码分析(基于搜索结果)
CDC* CDC::FromHandle(HDC hDC)
{
CHandleMap* pMap = afxMapHDC(TRUE); // 获取全局句柄映射表
CDC* pDC = (CDC*)pMap->FromHandle(hDC);
ASSERT(pDC == NULL || pDC->m_hDC == hDC);
return pDC;
}
- 关键步骤:
- 通过全局
CHandleMap管理 HDC 与 CDC 的映射关系。 - 优先从永久映射表(
m_permanentMap)查找,若不存在则创建临时 CDC 对象并存入临时映射表(m_temporaryMap)。 - 返回的 CDC 对象与 HDC 绑定,但需手动释放资源。
- 通过全局
2. 映射管理机制
| 映射类型 | 生命周期 | 存储位置 | 典型场景 |
|---|---|---|---|
| 永久映射 | 与窗口/控件绑定 | m_permanentMap |
对话框或控件的 CDC 对象 |
| 临时映射 | 仅在当前作用域有效 | m_temporaryMap |
动态创建的 CDC(如内存 DC) |
三、使用场景
1. 窗口绘制(基于搜索结果)
void CMyWnd::OnPaint()
{
CPaintDC dc(this); // 自动调用 BeginPaint
CDC* pDC = CDC::FromHandle(dc.m_hDC); // 转换为 CDC 对象
pDC->SetTextColor(RGB(255,0,0));
pDC->DrawText(_T("Hello MFC"), &dc.GetClientRect(), DT_CENTER);
}
- 优势:通过
CDC的封装,避免直接调用TextOut等 GDI 函数。
2. 内存绘图
HDC hMemDC = CreateCompatibleDC(hdc);
CDC* pMemDC = CDC::FromHandle(hMemDC); // 封装内存 DC
CBitmap bitmap;
bitmap.CreateCompatibleBitmap(hdc, 100, 100);
pMemDC->SelectObject(&bitmap);
pMemDC->FillSolidRect(0,0,100,100, RGB(0,255,0)); // 绘制到内存 DC
- 用途:实现双缓冲绘图,避免屏幕闪烁。
3. 控件自定义绘制
void CMyButton::PreSubclassWindow()
{
ModifyStyle(0, BS_OWNERDRAW);
CButton::PreSubclassWindow();
}
void CMyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
pDC->SetBkMode(TRANSPARENT);
pDC->DrawText(_T("Custom Button"), &lpDrawItemStruct->rcItem, DT_CENTER);
}
- 实现自绘按钮:通过
DrawItem处理WM_DRAWITEM消息。
四、注意事项
1. 资源释放
- HDC 必须手动释放:
HDC hDC = ::GetDC(hWnd); CDC* pDC = CDC::FromHandle(hDC); // ... 绘图操作 ... ::ReleaseDC(hWnd, hDC); // 必须显式释放 - 避免重复释放:同一 HDC 被多个 CDC 对象共享时,需确保仅释放一次。
2. 线程安全
- 跨线程限制:HDC 仅能在创建它的线程中使用。若需跨线程操作,需使用
::DuplicateDC复制 HDC。
3. 错误处理
HDC hDC = ::GetDC(hWnd);
if (hDC == NULL) {
TRACE(_T("Failed to get DC! Error: %d\n"), GetLastError());
return;
}
CDC* pDC = CDC::FromHandle(hDC);
ASSERT(pDC != nullptr); // 检查映射是否成功
4. 与智能指针结合
// 使用 CHandle 管理 HDC 生命周期
CHandle hDC = ::GetDC(hWnd);
CDC* pDC = CDC::FromHandle(hDC);
// 当 hDC 超出作用域时自动调用 ReleaseDC
五、与同类函数的对比
| 函数 | 用途 | 返回值类型 | 生命周期管理 |
|---|---|---|---|
CDC::FromHandle |
HDC → CDC 封装 | CDC* |
需手动释放 HDC |
CWnd::GetDC |
获取窗口客户区 DC | CDC* |
自动调用 ReleaseDC |
CClientDC 构造函数 |
封装客户区 DC(自动管理资源) | CClientDC |
析构时自动 ReleaseDC |
::CreateCompatibleDC |
创建内存 DC | HDC |
需手动调用 DeleteDC |
六、调试技巧
-
验证映射关系:
CDC* pDC = CDC::FromHandle(hDC); ASSERT(pDC->m_hDC == hDC); // 检查是否成功绑定 -
跟踪 DC 操作:
pDC->SetMapMode(MM_LOMETRIC); // 设置映射模式为 0.1mm 单位 TRACE(_T("Current Map Mode: %d\n"), pDC->GetMapMode()); -
内存泄漏检测:
- 使用 Visual Studio 的内存分析工具,监控
HDC和CDC对象的创建/销毁。
- 使用 Visual Studio 的内存分析工具,监控
七、扩展应用
1. 多显示器支持
HDC hMonitorDC = ::CreateDC(_T("\\\\.\\DISPLAY2"), NULL, NULL, NULL);
CDC* pMonitorDC = CDC::FromHandle(hMonitorDC);
pMonitorDC->FillSolidRect(0,0,800,600, RGB(0,0,255)); // 在第二屏幕绘制
::DeleteDC(hMonitorDC);
2. 打印预览
CPrintDialog printDlg(FALSE);
if (printDlg.DoModal() == IDOK) {
HDC hPrinterDC = printDlg.GetPrinterDC();
CDC printerDC(hPrinterDC);
DOCINFO di = { sizeof(DOCINFO), _T("Print Job") };
printerDC.StartDoc(&di);
printerDC.StartPage();
// 打印内容...
printerDC.EndPage();
printerDC.EndDoc();
}
总结
CDC::FromHandle() 是 MFC 中连接底层 GDI 与高层图形操作的关键桥梁,其核心价值在于封装 HDC 的复杂性并提供安全的资源管理。开发者需注意:
- 严格配对资源操作(如
GetDC/ReleaseDC) - 避免跨线程直接传递 HDC
- 优先使用 MFC 封装类(如
CClientDC)以简化代码
通过合理运用此函数,可显著提升图形代码的可维护性和健壮性。
更多推荐


所有评论(0)