c++mfchbitmapcbitmap

LoadBitmap fails after multiple calls


In this function, after about 90 calls (it's called in a loop and the idea is to load in a separate image each time, but I have kept it to one image for simplicity).The global variables now changed to local ones.

   void CDLP_Printer_ControlDlg::DisplayBMPfromSVG(CString& strDsiplayFile)
{
    HBITMAP hbmp_temp = (HBITMAP)::LoadImage(0, strDsiplayFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
    if (!hbmp_temp)
    {
        //hbmp_temp = ::LoadBitmap(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_BITMAP1));
        ActionList.AddString(L"Bitmap Load Failure: GetBMPromSVG");
        ActionList.UpdateWindow();
        if (!hbmp_temp)
            return;
    }

    CBitmap bmp_temp;
    bmp_temp.Attach(hbmp_temp);
    mProjectorWindow.m_picControl.ModifyStyle(0xF, SS_BITMAP, SWP_NOSIZE);
    mProjectorWindow.m_picControl.SetBitmap(bmp_temp);

    return;
}

I hope someone can come up with an Idea whats wrong. GetLastError returns "8" which means nothing to me.


Solution

  • Detach will destroy the previous handle.

    Note that if you call Detach after calling SetBitmap, the picture control's bitmap is destroyed. The result is that the picture control is painted once, but it won't be repainted. For example picture control goes blank if dialog is resized.

    EDIT

    To destroy the old bitmap, call Detach followed by DestroyObject. Example

    HGDIOBJ hbitmap_detach = m_bitmap.Detach();
    if (hbitmap_detach)
        DeleteObject(hbitmap_detach); 
    m_bitmap.Attach(hbitmap);
    

    If it's a temporary CBitmap then DeleteObject is not necessary, because DeleteObject is called automatically when CBitmap goes out of scope.

    Note that if you destroy the bitmap after calling SetBitmap, the picture control's bitmap is destroyed. The result is that the picture control is painted once, but it won't be repainted. For example picture control goes blank if dialog is resized.

    It's the same problem if you declare a temporary CBitmap on stack and attach the bitmap handle. That bitmap handle will be destroyed and picture control can't repaint itself.

    In addition, Windows XP sometimes makes duplicate bitmap which needs to be destroyed as well. SetBitmap returns a handle to previous bitmap. In Vista+ the returned bitmap is the same one which was save in m_bitmap, we already destroy that with Detach. But in XP we need to destroy this copy if it is a different handle.

    void CMyDialog::foo()
    {
        HBITMAP save = m_bitmap;
        HBITMAP hbitmap = (HBITMAP)::LoadImage(0, filename,
            IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
        if (hbitmap)
        {
            HGDIOBJ hbitmap_detach = m_bitmap.Detach();
            //Edit ****************************************
            //Delete old handle, otherwise program crashes after 10,000 calls
            if (hbitmap_detach)
                DeleteObject(hbitmap_detach); 
            //*********************************************
            m_bitmap.Attach(hbitmap);
    
            HBITMAP oldbmp = m_picControl.SetBitmap(m_bitmap);
    
            //for Windows XP special case where there might be 2 copies:
            if (oldbmp && (oldbmp != save))
                DeleteObject(oldbmp);
        }
    }
    

    Also, SetBitmap takes HBITMAP parameter and returns HBITMAP, so you can avoid using CBitmap altogether. The following example works in Vista+

    void foo()
    {
        HBITMAP temp = (HBITMAP)::LoadImage(0,filename,IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
        if (temp)
        {
            HBITMAP oldbmp = m_picControl.SetBitmap(temp);
            if (oldbmp)
                DeleteObject(oldbmp);
            DeleteObject(temp);
        }
    }