书籍:《Visual C++ 2017从入门到精通》
环境:visual studio 2022
说明:以上内容大部分来自腾讯元宝。
一、函数功能与原型
GetObject() 是 MFC 封装的 GDI 函数,用于获取图形对象(GDI Object)的详细信息,如位图尺寸、字体属性、画笔颜色等。其核心作用是为开发者提供对 GDI 对象属性的动态查询能力。
函数原型
int GetObject( HGDIOBJ hgdiobj, // 图形对象句柄(如 HBITMAP、HPEN 等) int cbBuffer, // 缓冲区大小(字节) LPVOID lpvObject // 接收信息的缓冲区指针 );
返回值
- 成功:
- 若
lpvObject为NULL,返回所需缓冲区大小(字节)。 - 若
lpvObject有效,返回实际写入缓冲区的字节数。
- 若
- 失败:返回
0(可通过GetLastError()获取错误码)。
二、参数详解
1. hgdiobj(图形对象句柄)
- 支持类型:
- 位图(
HBITMAP) - 画笔(
HPEN) - 画刷(
HBRUSH) - 字体(
HFONT) - 调色板(
HPALETTE) - 设备无关位图(
HBITMAP通过CreateDIBSection创建)
- 位图(
- 获取方式:
CBitmap bitmap; bitmap.LoadBitmap(IDB_BITMAP1); HBITMAP hBmp = (HBITMAP)bitmap.Detach(); // 从 MFC 对象获取句柄
2. cbBuffer(缓冲区大小)
- 计算方式:根据对象类型确定缓冲区结构大小。例如:
- 位图:
sizeof(BITMAP) - 字体:
sizeof(LOGFONT) - 画笔:
sizeof(LOGPEN)
- 位图:
- 示例:
BITMAP bm; int nSize = sizeof(BITMAP); // 位图信息结构大小 GetObject(hBmp, nSize, &bm);
3. lpvObject(缓冲区指针)
- 指向结构体:根据对象类型传入对应结构体指针,如:
- 位图:
BITMAP* - 字体:
LOGFONT* - 画笔:
LOGPEN*
- 位图:
三、核心应用场景
1. 获取位图属性
// 示例:获取位图宽度和高度 HBITMAP hBmp = (HBITMAP)::LoadImage(NULL, _T("image.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); BITMAP bm = {0}; GetObject(hBmp, sizeof(BITMAP), &bm); int width = bm.bmWidth; // 位图宽度 int height = bm.bmHeight; // 位图高度
2. 查询字体信息
// 示例:获取当前选中字体属性 CFont* pFont = pDC->GetCurrentFont(); LOGFONT lf = {0}; pFont->GetObject(sizeof(LOGFONT), &lf); CString fontName = lf.lfFaceName; // 字体名称
3. 解析画笔属性
/ 示例:获取画笔颜色和样式 CPen* pPen = new CPen(PS_SOLID, 2, RGB(255, 0, 0)); LOGPEN lp = {0}; pPen->GetObject(sizeof(LOGPEN), &lp); COLORREF color = RGB(lp.lopnColor[2], lp.lopnColor[1], lp.lopnColor[0]); // BGR 顺序
四、关键技术与注意事项
1. 缓冲区溢出风险
- 必须预分配足够内存:缓冲区大小需严格匹配对象类型。
// 错误示例:缓冲区过小导致崩溃 BITMAP bm; GetObject(hBmp, 100, &bm); // sizeof(BITMAP)=40,100 足够,但需按实际计算 // 正确示例 int nRequiredSize = sizeof(BITMAP); if (GetObject(hBmp, nRequiredSize, &bm) == 0) { // 处理错误 }
2. 对象类型验证
- 需确认句柄类型:避免对非目标类型对象调用(如用
LOGFONT解析画笔)。 - 使用
GetObjectType()辅助判断:UINT type = GetObjectType(hGdiObj); if (type == OBJ_BITMAP) { // 处理位图 }
3. MFC 封装与原生 API 对比
| 方法 | MFC 封装(如 CBitmap::GetObject) | Win32 原生 API |
|---|---|---|
| 代码简洁性 | 高(自动处理句柄转换) | 低(需手动管理句柄) |
| 错误处理 | 依赖返回值和 GetLastError() | 同左 |
| 灵活性 | 仅支持 MFC 支持的 GDI 对象 | 支持所有 GDI 对象 |
五、实际项目中的应用案例
案例 1:动态调整控件图标大小
// 根据位图尺寸调整按钮图标 CBitmap bitmap; bitmap.LoadBitmap(IDB_ICON); BITMAP bm = {0}; GetObject(bitmap, sizeof(BITMAP), &bm); CSize iconSize(bm.bmWidth, bm.bmHeight); m_btn.SetIconSize(iconSize); // 假设按钮支持图标缩放
案例 2:颜色替换(如将图片中的白色替换为透明)
HBITMAP hBmp = (HBITMAP)::LoadImage(NULL, _T("input.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); BITMAP bm = {0}; GetObject(hBmp, sizeof(BITMAP), &bm); CDC memDC; memDC.CreateCompatibleDC(NULL); CBitmap* pOldBmp = memDC.SelectObject(&bitmap); for (int y = 0; y < bm.bmHeight; y++) { for (int x = 0; x < bm.bmWidth; x++) { COLORREF clr = memDC.GetPixel(x, y); if (clr == RGB(255, 255, 255)) { // 白色像素 memDC.SetPixel(x, y, RGB(0, 0, 0, 0)); // 设为透明 } } } memDC.SelectObject(pOldBmp);
六、性能优化建议
- 缓存对象信息:频繁访问的图形对象(如字体、画笔)应缓存其属性,避免重复调用
GetObject()。 - 批量操作:对位图等大数据对象,优先使用
GetDIBits()一次性获取像素数据。 - 多线程安全:确保图形对象在单线程内操作,避免跨线程访问冲突。
七、常见问题与解决方案
问题 1:返回值始终为 0
- 原因:
- 缓冲区大小不足。
- 句柄无效或类型不匹配。
- 解决:
int requiredSize = GetObject(hBmp, 0, NULL); // 先获取所需大小 if (requiredSize > 0) { // 分配足够缓冲区后重试 }
问题 2:颜色值异常(如 BGR 顺序)
- 原因:GDI 中颜色存储为 BGR 格式,与常见的 RGB 顺序不同。
- 解决:手动转换颜色通道:
COLORREF clr = RGB(lp.lopnColor[2], lp.lopnColor[1], lp.lopnColor[0]);
八、总结
GetObject() 是 MFC 中操作图形对象的核心工具,其核心价值在于:
- 动态属性查询:支持位图、字体、画笔等对象的实时信息获取。
- 跨对象兼容性:通过统一接口适配多种 GDI 对象类型。
最佳实践:
- 始终检查返回值和缓冲区大小,避免内存越界。
- 结合
GetObjectType()验证对象类型,增强代码健壮性。
648

被折叠的 条评论
为什么被折叠?



