以下列代码为例

            {
// 创建内存DC
CDC mMemDc;
mMemDc.CreateCompatibleDC( &dc ); // 创建兼容位图
CBitmap bmpMemBmp;
bmpMemBmp.CreateCompatibleBitmap( &mMemDc, , );
CBitmap* pOldBmp = mMemDc.SelectObject( &bmpMemBmp ); // 在内存DC上绘图
BOOL bRet = mMemDc.Ellipse( , , , ); // 从内存DC上拷贝至显示DC
dc.BitBlt( , , , , &mMemDc, , , SRCCOPY ); // 恢复
// When you finish with the CBitmap object created with the
// CreateCompatibleBitmap function, first select the bitmap out of the device context, then delete the CBitmap object.
//mMemDc.SelectObject( pOldBmp );
}

以上代码最后被注释的部分,按照MSDN上的说法,要将之前的GDI对象SelectObjec回去,防止CBitmap bmpMemBmp中的GDI对象删除失败。

CBitmap析构函数调用基类的~CGdiObject~CGdiObject中会调用 ::DeleteObject函数来删除GDI对象,关于API函数::DeleteObject(HGDIOBJ hObject) MSDN上有如下说法,if the specified handle is not valid or is currently selected into a DC, the return value is zero.意思是传入的对象句柄无效或者已经被选进DC中的,会返回0,即删除失败。按照这个说法,上文代码最后那部分代码不应该被注释,否则会导致GDI对象删除失败,从而造成资源泄露。

但是,根据实验结果看,并没有造成资源的泄露,使用Process Explorer软件查看GDI句柄数,并没有一直上涨

将GDI对象选入DC, 在没有SelectObject出来之前调用DeleteObject返回的也是成功的,何解?

我想这些都是属于系统底层的机制,为了安全起见,在没有把握的情况下,还是按照官方给出的文档来做,

SelectObject

The SelectObject function selects an object into the specified device context (DC). The new object replaces the previous object of the same type.

HGDIOBJ SelectObject(
HDC
hdc, // handle to DC
HGDIOBJ hgdiobj // handle to object
);

Remarks

This function returns the previously selected object of the specified type. An application should always replace a new object with the original, default object after it has finished drawing with the new object. 

注:MFC那边的SelectObject,即CDC::SelectObject, 也需要遵循这个规则,查看MFC得知,MFC对这个并没有额外的去处理,^-^

补充:

直接用WIN32缩写,也验证了上面的论点,GDI对象在DC中,DeleteObject能删除成功,

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
switch (message)
{
case WM_CREATE:
return (); case WM_PAINT:
{
hdc = BeginPaint (hwnd, &ps);
TCHAR szStr[] = TEXT("A Window!");
TextOut (hdc, , , szStr, _countof(szStr) ); HPEN hPen = CreatePen( PS_DASH, , RGB(, , ) );
HPEN hOldPen = (HPEN)SelectObject( hdc, (HGDIOBJ)hPen ); Ellipse( hdc, , , , ); // 以下也能删除成功,这个hPen还在dc中
BOOL bRet = DeleteObject( (HGDIOBJ)hPen ); EndPaint (hwnd, &ps);
return ();
}
case WM_DESTROY:
PostQuitMessage ();
return ();
}
return DefWindowProc (hwnd, message, wParam, lParam);
}

结论:为了安全起见,遵循MSDN文档上的约定

关于SelectObject之后是否要恢复之前的GDI对象的更多相关文章

  1. GDI编程

    图形设备接口(GDI)是一个可执行程序,它接受Windows应用程序的绘图请求(表现为GDI函数调用),并将它们传给相应的设备驱动程序,完成特定于硬件的输出,象打印机输出和屏幕输出.GDI负责Wind ...

  2. GDI编程小结

    图形设备接口(GDI)是一个可运行程序,它接受Windows应用程序的画图请求(表现为GDI函数调用),并将它们传给对应的设备驱动程序,完毕特定于硬件的输出,象打印机输出和屏幕输出.GDI负责Wind ...

  3. VC++学习之GDI概述

    VC++学习之GDI概述 图形设备接口(GDI)是一个可执行程序,它接受Windows应用程序的绘图请求(表现为GDI函数调用),并将它们传给相应的设备驱动程序,完成特定于硬件的输出,象打印机输出和屏 ...

  4. MFC/Windows API 使用过的函数(持续更新)

    /*******************使用默认画笔对象**************************** // //绘制矩形 pDC->MoveTo(50, 50); //返回值是一个指 ...

  5. 浅谈Windows API编程

    WinSDK是编程中的传统难点,个人写的WinAPI程序也不少了,其实之所以难就难在每个调用的API都包含着Windows这个操作系统的潜规则或者是windows内部的运行机制…… WinSDK是编程 ...

  6. ORACLE恢复数据

    ORACLE恢复删除表或表记录 一:表的恢复      对误删的表,只要没有使用PURGE永久删除选项,那么从flash back区恢复回来希望是挺大的.一般步骤有: 1.从flash back里查询 ...

  7. Oracle DB 执行表空间时间点恢复

    • 列出在执行表空间时间点恢复(TSPITR) 时会发生的操作 • 阐释TSPITR 使用的术语的定义 • 确定适合将TSPITR 用作解决方案的情况 • 确定时间点恢复的正确目标时间 • 确定不能使 ...

  8. iOS开发 - 数据归档与恢复 NSKeyedArchiver

    归档与恢复归档 归档,英文Archiver['ɑrkɪvə],这里指的是将OC的对象存储为一个文件或者网络上的一个数据块. 恢复归档.英文UnArchiver,指的是将一个来自文件或网络的归档数据块恢 ...

  9. 【RMAN】TSPITR--RMAN表空间基于时间点的自动恢复

    [RMAN]TSPITR--RMAN表空间基于时间点的自动恢复 一.1  BLOG文档结构图 一.2  前言部分 一.2.1  导读 各位技术爱好者,看完本文后,你可以掌握如下的技能,也可以学到一些其 ...

随机推荐

  1. 【bootstrap】Bootstrap Notify的使用步骤

    Bootstrap Notify说明文档:http://bootstrap-notify.remabledesigns.com/ Bootstrap Notify的GitHub地址:https://g ...

  2. weblogic日志管理

    服务器日志  每个 WebLogic Server 实例将来自子系统和应用程序的所有消息写入位于本地主机上的服务器日志文件.默认情况下,服务器日志文件位于服务器实例根目录下的 logs 目录中: 例如 ...

  3. [反汇编练习] 160个CrackMe之031

    [反汇编练习] 160个CrackMe之031. 本系列文章的目的是从一个没有任何经验的新手的角度(其实就是我自己),一步步尝试将160个CrackMe全部破解,如果可以,通过任何方式写出一个类似于注 ...

  4. 您也使用托管C++吗? 【转】

    http://blog.csdn.net/Muf/article/details/656920 转向.NET后,手头上往往仍有旧的模块要重用.也许这些模块是Delphi写的,也许是C/C++写的,或者 ...

  5. linux遍历目录源代码

    <pre code_snippet_id="1622396" snippet_file_name="blog_20160324_1_744516" nam ...

  6. 【转载】ASP.NET应用程序与页面生命周期

    在本文中,我们将了解不同的事件,ASP.NET 应用程序的生命周期以浏览器向 Web 服务器(对于 ASP.NET 应用程序,通常为 IIS)发送请求为起点,直至将请求结果返回至浏览器结束.在这个过程 ...

  7. 【每日Scrum】第五天(4.26) TD学生助手Sprint2站立会议

    站立会议 组员 昨天 今天 困难 签到 刘铸辉 (组长) 今天增加了几个页面的子菜单,然后设计了几个要用的界面 今天和楠哥做了课程事件和日历表操作的例子,并尝试做时间表和日历表的数据库设计 安卓的数据 ...

  8. 如何与强势的人相处zz

    要和强势的人相处良好,须知道强势的人有两个很显著的特点:一.以自我观点为中心.二.怕别人否定自己.强势的主要作用也有两个:一.支配别人.二.掩盖自卑. 首先,要区分一下强势的人和特立独行的人,这两类人 ...

  9. Lombok简介

    Lombok简介 和其他语言相比,Java经常因为不必要的冗长被批评.Lombok提供了一系列注解用以在后台生成模板代码,将其从你的类中删除,从而有助于保持你的代码整洁.较少的模板意味着更简洁的代码, ...

  10. 获取当前外网IP地址

    <script src="http://pv.sohu.com/cityjson?ie=utf-8"></script><script>cons ...