Odd resizing behavior in MFC

This is a discussion on Odd resizing behavior in MFC within the Windows Programming forums, part of the Platform Specific Boards category; I have a dialog with a generic CWnd embedded into it. I want to allow the user to change the ...

  1. #1
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,596

    Odd resizing behavior in MFC

    I have a dialog with a generic CWnd embedded into it.

    I want to allow the user to change the size of the dialog. So here is my method:

    This is the OnSize handler in the dialog window
    Code:
    void TilePickerDlg::OnSize(UINT nType, int cx, int cy) 
    {
    	
      m_wndTileRenderer.MoveWindow(0,0,cx-5,cy-5,true);
      m_wndTileRenderer.Resize(cx-5,cy-5);
      
      CDialog::OnSize(nType, cx, cy);
      
    }
    So you see I'm passing the command then to the CWnd and using MoveWindow to change the size of that window.
    My renderer then needs to re-calculate the size of the memory bitmap for the window since I'm using double buffering.

    Here is the Resize code:
    Code:
    void Resize(int cx,int cy)
      {
        MemoryDC.SelectObject(MemoryBitmap);
        MemoryBitmap.Detach();
            
        //Create bitmap
        MemoryBitmap.CreateCompatibleBitmap(GetDC(),cx,cy);
        
        //Select bitmap into DC
        MemoryDC.SelectObject(MemoryBitmap);
    
        CalculateMaxScroll();
      }
    It is inline which is why the class name is not in front of the function name. So it's a simple, de-select the existing bitmap - which is the MemoryBitmap, detach it, re-create it, and then re-select it into the DC. Then re-calc the scroll extents.

    It works great until the window gets past a certain size and then it acts as if the DC is corrupt. It still draws, but it slows everything down in the editor. All drawing begins to look like slideshows.

    Any ideas?

  2. #2
    Registered User
    Join Date
    Mar 2004
    Posts
    220
    Hmm...sounds like you're not freeing up memory somewhere...but I can't see exactly where o_O.
    OS: Windows XP Pro CE
    IDE: VS .NET 2002
    Preferred Language: C++.

  3. #3
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,859
    You seem to have a issue with which bmp to use.

    Should have two bmps;
    the original bmp contained in your created DC (MemoryDC)
    a bmp from a call to CreateCompatibleDC()

    >>MemoryDC.SelectObject(MemoryBitmap);

    Is MemoryBitmap the original bmp contained in the MemoryDC?

    If so then you should not be creating another compatible one using its handle. You should be using your created bitmap.

    If MemoryBitmap is one you have created, then you should not be SelectObject()'ing it in but should be SelectObject()'ing the original bmp in, the created/current one will be returned.
    EDIT:
    This is because a bmp may not delete if currently selected into a DC.
    You can then DeleteObject it, resize and selectobject it back in again..


    >>MemoryBitmap.Detach();

    This does not free the bitmaps memory (AFAIK) so you will lose the bitmaps memory.

    Should be a DeleteObject()
    Last edited by novacain; 01-08-2006 at 09:22 PM.
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  4. #4
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,596
    I fixed it.

    Code:
    void Resize(int cx,int cy)
    {
        
        int iDC=MemoryDC.SaveDC();
    
        MemoryBitmap.DeleteObject();
            
        //Re-create bitmap
        MemoryBitmap.CreateCompatibleBitmap(GetDC(),cx,cy);
    
        MemoryDC.RestoreDC(iDC);
        
        CalculateMaxScroll();
    }
    MemoryDC is just the device context. The MemoryBitmap is selected into the DC to act as the pixels to draw to. It is simply a blank bitmap that is the size of GetClientRect() and is compatible with the DC.

    The actual tile render window is embedded into the main dialog window. The dialog doesn't call the OnPaint() inside of it's OnPaint() because the framework and the API handle this when it sees that the tile render window is a child window of the dialog.

    I fixed all of the leaks. It seems that MFC leaks GDI object in calls like this:

    MemoryDC->FrameRect(&rect,&brush);

    This will report a leaked CBrush object.

    The correct way to do it is this:
    Code:
    CBrush *pBrush=new CBrush();
    pBrush->CreateSolidBrush(RGB(0,0,0));
    
    CBrush *pOldBrush=MemoryDC.SelectObject(&pBrush);
    
    //..draw
    
    MemoryDC.SelectObject(pOldBrush);
    
    pBrush->DeleteObject();
    delete pBrush;
    Shouldn't MFC do this for me since it requires a CBrush to be passed to FrameRect? Anyways anytime I use a brush, pen, etc, I use this and all of the leaks are gone. The app runs forever and doesn't report leaks in the debugger and no corruption occurs in the app whatsoever. I'm still not positive everything is ok, but so far it seems to be.

  5. #5
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,859
    Are you using a .NET version of MSVC? (.NET MSVC treats GDI objects as temp by default, MSVC v6 does not)

    >>Shouldn't MFC do this for me since it requires a CBrush to be passed to FrameRect?

    FrameRect() should not cause a leak. I have not noticed it leaking in my apps.

    I don't think that you are using SaveDC() / RestoreDC() correctly. It should save / restore the DC (inc the BMP) from the stack and so discard your changes. But if it works.......

    I would use the similar method/code for the BMP (in your Resize() )as you use for the brush (but don't delete until next resize or OnClose() ).

    MSDN:eleteObject()
    "Do not delete a drawing object (pen or brush) while it is still selected into a DC."
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  6. #6
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,596
    Well none of my objects are selected into the DC in resize.

    Inside of OnPaint() I do this:

    Code:
    CBitmap *pOldBitmap=MemoryDC.SelectObject(&MemoryBitmap);
    ...
    ...
    //This will return MemoryBitmap, but I don't want to do anything with it
    //so I don't need a pointer to it to delete it's memory
    //MemoryBitmap is destroyed when the window is closed, otherwise it's valid.
    MemoryDC.SelectObject(pOldBitmap);
    So when I return from OnPaint() the MemoryDC is exactly as it was upon entry to it which is exactly what Windows wants. Anytime you use a brush, pen, bitmap, etc, you must select it and get a pointer to the current one. Then when you are done you select the old one back into the DC and this preserves the integrity of the DC.

    After significant changes to the code, it all works perfect now. And FrameRect() does NOT do this for you according to what my debugging has shown. Even though FrameRect() requires a CBrush and it knows what DC it operates on, it does not correctly select it into the DC. So even using FrameRect() requires that you first get a pointer to the old brush by selecting your brush into the DC. Call FrameRect() and pass the desired rect and the your brush. When done, select the old bitmap into the DC which will return your brush. Delete your brush using DeleteObject() and then de-allocate it's memory if you created it on the heap and if you are not going to use it again. If you are storing the GDI object in a class then there is no reason to delete the object until the app or window is closed. By doing the second SelectObject() you are simply de-selecting your GDI object and returning it to it's state before you started fiddling with it.

    It works like a charm but it is a very stupid way to do graphics.


    I forgot that portion in my example but this is what my code does.

  7. #7
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,859
    How are you detecting the leak?


    I do not select the brush into the DC prior to calling FrameRect() and do not get a GDI leak.

    If you look at the code MFC uses to draw you will see it used that way as well.

    ie from VC98\MFC\SRC\PPGCOLOR.CPP
    Code:
    void _AfxDraw3DButtonFrame(CDC *pDC, CRect rcButton, BOOL fFocus)
    {
    	CPen *pPen, *pOldPen;
    	CBrush GrayBrush(RGB_BUTTON_LIGHT);
    	CBrush BlackBrush(RGB_BUTTON_BLACK);
    
    	pPen = new CPen(PS_SOLID, 1, RGB_BUTTON_BLACK);
    	pOldPen = pDC->SelectObject(pPen);
    
    	// Draw gray outside
    	pDC->FrameRect(&rcButton, &GrayBrush);
    	rcButton.InflateRect(-1, -1);
    
    	if (fFocus)
    	{
    		// Draw inside of border
    		pDC->FrameRect(&rcButton, &BlackBrush);
    		// Draw curved border on outside;
    		rcButton.InflateRect(1, 1);
    	}
    	else
    	{
    		// Prepare inside border
    		pDC->FrameRect(&rcButton, &GrayBrush);
    	}
    
    	pDC->MoveTo(rcButton.left+1, rcButton.top);
    	pDC->LineTo(rcButton.right-1, rcButton.top);
    	pDC->MoveTo(rcButton.left+1, rcButton.bottom-1);
    	pDC->LineTo(rcButton.right-1, rcButton.bottom-1);
    	pDC->MoveTo(rcButton.left, rcButton.top+1);
    	pDC->LineTo(rcButton.left, rcButton.bottom-1);
    	pDC->MoveTo(rcButton.right-1, rcButton.top+1);
    	pDC->LineTo(rcButton.right-1, rcButton.bottom-1);
    
    	pDC->SelectObject(pOldPen);
    	delete pPen;
    }
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. resizing OpenGL picture control in MFC window
    By stanlvw in forum Windows Programming
    Replies: 1
    Last Post: 03-19-2009, 04:21 PM
  2. WIndows programming?
    By hostensteffa in forum Windows Programming
    Replies: 7
    Last Post: 06-07-2002, 09:52 PM
  3. Release MFC Programs & Dynamic MFC DLL :: MFC
    By kuphryn in forum Windows Programming
    Replies: 2
    Last Post: 05-18-2002, 07:42 PM
  4. Beginning MFC (Prosise) Part III - Now What? :: C++
    By kuphryn in forum C++ Programming
    Replies: 5
    Last Post: 03-03-2002, 06:58 PM
  5. MFC is Challenging :: C++
    By kuphryn in forum C++ Programming
    Replies: 8
    Last Post: 02-05-2002, 01:33 AM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21