相较于其他方式,使用GetDC方法是否有性能提升?三类代码使用优势咨询
关于GetDC性能及三类代码实现的分析
一、GetDC方法的性能对比
咱们先聊聊GetDC的性能问题。首先得明确:这里的GetDC分两种场景,性能差异不小:
- 窗口专属的
GetDC(比如m_StatusBar.GetDC()):系统会为每个窗口维护DC缓存,调用GetDC和ReleaseDC的开销极低,重复获取同一个窗口的DC时,基本是直接从缓存取,不需要额外分配大量GDI资源。对比手动创建DC(比如CreateCompatibleDC),这种方式的性能优势很明显——手动创建DC需要全新分配GDI上下文,初始化成本高得多。 - 桌面DC(比如
CClientDC(nullptr)):这是全局共享的DC,系统需要处理更多的竞争同步,而且桌面尺寸通常远大于单个窗口,DC的初始化和操作开销都会比窗口DC高不少。
所以如果是针对某个窗口的GDI操作,直接调用该窗口的GetDC,性能肯定比获取桌面DC或手动创建DC要好。
二、三类代码的优势与问题分析
咱们逐个拆解这三段代码:
代码3:直接使用状态栏的DC
CDC *pDC = m_StatusBar.GetDC(); cxNewWidth = pDC->GetTextExtent(strCalendar).cx + kStatusBarIconWidth;
这是最优的实现方案,优势非常突出:
- 最简洁:不需要手动处理字体,状态栏的DC已经默认加载了自身的字体、映射模式等GDI配置,和实际显示环境完全一致,计算出的文本宽度最准确。
- 性能最佳:直接获取窗口专属DC,避免了桌面DC的高开销,也省去了字体选择/恢复的额外步骤。
⚠️ 注意:这段代码有个隐患——GetDC获取的DC必须手动调用ReleaseDC(pDC)释放,否则会造成GDI资源泄漏,建议改成用CClientDC自动管理:
CClientDC dc(&m_StatusBar); cxNewWidth = dc.GetTextExtent(strCalendar).cx + kStatusBarIconWidth;
代码2:用桌面DC + MFC GetFont()
CClientDC dc(nullptr); CFont *pFont = m_StatusBar.GetFont(); if (pFont != nullptr) dc.SelectObject(pFont); cxNewWidth = dc.GetTextExtent(strCalendar).cx + kStatusBarIconWidth;
这段代码比代码1更简洁,因为CFont::GetFont()是MFC的封装,内部其实也是发送WM_GETFONT消息,但用MFC接口更符合框架习惯,可读性更好。但它的问题也很明显:
- 计算结果可能不准确:桌面DC的映射模式、DPI设置可能和状态栏的DC不一致,导致文本宽度计算出现偏差。
- GDI资源风险:选入新字体后没有恢复原来的字体,会修改桌面DC的全局状态,影响其他使用桌面DC的程序或自身后续操作。
- 性能不如代码3:桌面DC的开销比窗口DC高,还要多一步字体选择操作。
代码1:用桌面DC + WM_GETFONT消息
CClientDC dc(nullptr); HFONT hFont = (HFONT)m_StatusBar.SendMessage(WM_GETFONT); HGDIOBJ hOldFont = nullptr; if (hFont != nullptr) hOldFont = dc.SelectObject(hFont); cxNewWidth = dc.GetTextExtent(strCalendar).cx + kStatusBarIconWidth;
这段代码是原生Win32风格的实现,几乎没有优势——它和代码2的底层逻辑完全一致,但代码更繁琐,需要手动处理HFONT和HGDIOBJ的类型转换,可读性差。而且同样存在代码2的所有问题:桌面DC的准确性、性能问题,以及没有恢复旧字体的GDI资源泄漏风险(代码里定义了hOldFont但没调用dc.SelectObject(hOldFont)恢复,这是明显的错误)。
总结
- 如果是针对某个窗口的文本尺寸计算,优先用该窗口自己的DC(代码3的思路,注意正确管理DC资源),性能和准确性都是最优的。
- 代码1和代码2的设计思路不太合理,除非有特殊需求(比如在非窗口环境模拟计算),否则不建议使用,而且要注意修复GDI资源泄漏的问题。
内容的提问来源于stack exchange,提问作者Andrew Truckle
相关产品推荐
相关产品推荐

