关于SelectObject之后是否要恢复之前的GDI对象

以下列代码为例

            {
                // 创建内存DC
                CDC mMemDc;
                mMemDc.CreateCompatibleDC( &dc );

                // 创建兼容位图
                CBitmap bmpMemBmp;
                bmpMemBmp.CreateCompatibleBitmap( &mMemDc, 100, 100 );
                CBitmap* pOldBmp = mMemDc.SelectObject( &bmpMemBmp );

                // 在内存DC上绘图
                BOOL bRet = mMemDc.Ellipse( 0, 0, 100, 100 );

                // 从内存DC上拷贝至显示DC
                dc.BitBlt( 0, 0, 100, 100, &mMemDc, 0, 0, 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 (0);
        
    case WM_PAINT:
        {
            hdc = BeginPaint (hwnd, &ps);
            TCHAR szStr[] = TEXT("A Window!");
            TextOut (hdc, 0, 0, szStr, _countof(szStr) );

            HPEN hPen = CreatePen( PS_DASH, 10, RGB(255, 0, 0) );
            HPEN hOldPen = (HPEN)SelectObject( hdc, (HGDIOBJ)hPen );
            
            Ellipse( hdc, 10, 100, 200, 150 );
            
            // 以下也能删除成功,这个hPen还在dc中
            BOOL bRet = DeleteObject( (HGDIOBJ)hPen );

            EndPaint (hwnd, &ps);
            return (0);
        }
    case WM_DESTROY:
        PostQuitMessage (0);
        return (0);
    }
    return DefWindowProc (hwnd, message, wParam, lParam);
}

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

原文地址:https://www.cnblogs.com/shanql/p/6576444.html