Device Contexts and memory leaks.

This is a discussion on Device Contexts and memory leaks. within the Windows Programming forums, part of the Platform Specific Boards category; In my previous post I was asking about a snake game I was working on. I got it to draw ...

  1. #1
    Bored Programmer
    Join Date
    Jul 2009
    Location
    Tomball, TX
    Posts
    407

    Exclamation Device Contexts and memory leaks.

    In my previous post I was asking about a snake game I was working on. I got it to draw and behave properly, but I ended up with issues where the program locked up after the snake has around 20+ segments. This would be a very lame game if the snake did not get bigger so I went down the rabbit hole of device contexts and memory leaks. (I suspected I created a leak and that was why it locked when I had more segments.). I have spent probably a day and a half reading about the four device context types and how they were used. I read probably ten different tutorials and spent most my time on the MSDN documentation pages.

    I have had issues understanding pointers in the pas so it is making this difficult as everything is done with handles which seem to be for the most part an abstraction pointer to an object that the application can't directly affect. So I have a couple questions about the device contexts. Basicly I want to draw all application drawing to a Memory DC and then after everything is done I would bltbit to SRCCOPY to the display context as I think this would eliminate all the screen flickering. I am having some issues understanding proper use of the selectobject() getobject() and deleteobject() functions to prevent any memory leaks.

    Do you have to delete an object after EVERY selectobject() function?

    If a memory DC can only hold one bitmap would I have to use 2 memory DCs and one display DC? One DC for slecting BITMAPs into. Another to draw everything onto and a final one being the display one? (If that would even work I am guessing I would have to set one of the memory DC to hold a BITMAP the size of the window)

    Sorry for the lengthy post but I wanted to communicate what I have read and where I am still sketchy. I don't want to post code as I want to understand the concept rather than just have someone give me the right code. If you can answer with a link that explains in lamen terms that would be cool too. Thnx in advance for your time.

  2. #2
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,856
    Consider the HDC a struct.

    The HDC holds 'pointers' to each of the GDI objects it can contain; font, pen, brush, bmp etc.

    The HDC is created / get already full of 'default' GDI objects (ie a created compatible DC is created containing a 1x1 monochrome bmp)

    The HDC can only hold one of each type, so when you select a GDI object into the DC you push out the current GDI object of the same type (ie SelectObject a pen and the current pen is pushed out). This means you need to 'catch' the GDI object when using SelectObject(), as you can not get the GDI object back out of the DC (and it may not delete while in the DC).

    The HDC may not delete / release with non default GDI objects selected into it.

    Non default GDI objects may not delete while selected into a HDC (ie BITMAPs may not delete while selected into a DC).

    .NET version IDEs are less strict and tend to 'hide' GDI leaks.

    Check in Task Manger to see if your app is leaking GDI objects (add 'GDI Objects' to the 'Processes" TAB of Task Manager).

    Use GetLastError()


    >>Do you have to delete an object after EVERY selectobject() function?

    Sort of...

    You are responsible to return the DC back to it's default state and delete any GDI objects you create.

    'Delete' anything you 'Create' (ie CreateSolidBrush() requires DeleteObject() ) and 'Release' anything you 'Get' (ie GetDC() requires ReleaseDC() )


    Stock objects are sort of an exception but can be ignored, as DeleteObject() on a stock GDI object does nothing.

    Follow this pattern and you should not go too far wrong;

    //create new GDI object
    HBRUSH GreenBrush = CreateSolidBrush(RGB(0, 255, 0));
    //select into DC, catching return
    HBRUSH *DefaultBrush = (HBRUSH*)SelectObject(MemDC, GreenBrush);

    //draw

    //clean up
    //put DC back to default by selecting in the original / default GDI objects
    SelectObject(MemDC, DefaultBrush);
    //delete created GDI objects
    DeleteObject(GreenBrush);

    //If a memory DC can only hold one bitmap would I have to use 2 memory DCs and one display DC?

    One DC to hold the current screen, often called MemDC.
    This has scope for the entire apps life.
    Create these in WM_CREATE / WM_INITDIALOG
    It needs a CompatibleBitmap (which is resized with the app or created at full screen size).
    Return to default and delete these in WM_CLOSE.

    In WM_PAINT you BitBlt to the DC in the PAINTSTRUCT, using the RECT in the PAINTSTRUCT, and from your MemDC.

    You may need to create other DCs for images.
    It depends on your app if these have the same scope as the MemDC (ie app lifetime) or are created and destroyed as required (ie only have scope in the actual drawing function)

    Lot of info for a start, post any follow up questions/clarifications you have.

    Search here for code snippets, I have been answering these questions here for a decade now. So there is plenty of code to be found.

    ie Drawing with mouse

    NOTE: It has been years since I coded a UI in WIN32, so bit rusty. Any code is written from memory so it will be full of bugs...
    Last edited by novacain; 11-06-2012 at 11:59 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

  3. #3
    Bored Programmer
    Join Date
    Jul 2009
    Location
    Tomball, TX
    Posts
    407
    This means you need to 'catch' the GDI object when using SelectObject(), as you can not get the GDI object back out of the DC (and it may not delete while in the DC).
    I am a little fuzzy on what that means. If
    Code:
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hwnd, &ps);
    HDC hdcMem = CreateCompatibleDC(hdc);
    HBITMAP A = LoadBitmap(GetModuleHandle(NULL), "Somepicture.bmp");
    HBITMAP B = LoadBitmap(GetModuleHandle(NULL), "Anotherpicture.bmp");
    SelectObject(hdcMem, A);
    BitBlt(hdc, 0, 0, 32, 32, hdcMem, 0, 0, SRCCOPY);
    SelectObject(hdcMem, b);  //this is where you say i would have to "catch it"?  Would that be with a susequent delete object call?
    BitBlt(hdc, 32, 0, 32, 32, hdcMem, 0, 0, SRCCOPY);
    DeleteDC(hdcMem);
    Sorry if the code above is flawed i am trying to recall all functions from memory and only been using winapi for a few days. The documentation on MSDN's library website says you also have to delete each HBITMAP that uses a LoadBitmap() function. Would you delete it at then end of the program IE the when the WM_DESTROY is called?

    Obviously in a real program i would not call a LoadBitmap() function everytime i drew. I would make it part of the game class construction. So if i deleted these HBITMAPS before the WM_DESTROY i am guessing i would lose the Handle to the actual files.

    As for the compiler i am using (don't crucify me) DevC++. I am saving up for the full version of Visual C++, but at the moment this is what i am using.

    Check in Task Manger to see if your app is leaking GDI objects (add 'GDI Objects' to the 'Processes" TAB of Task Manager).
    The program initiates with 49 GDI objects. It goes between 49-51 during the entire life of the program it starts at 900k and crashes at 1200k which is really small usage compared to the rest of the applications runnuing. CPU usage spikes to 100% before the crash though so maybe i am overloading the graphics card?

    GetLastError() returns an int is there a way to output this to the debugger so I can check this value?

    I guess it would be easier for you to know what i am not understanding if i just post the code. Could only upload 5 files so the vectoredit.h is only declarations of the CPP functions and the resource file looks like.

    Code:
    #include <resource.h>
    
    IDB_SNAKEHEAD BITMAP "snakehead.bmp"
    IDB_SNAKEHEADD BITMAP "snakeheadd.bmp"
    IDB_SNAKEHEADU BITMAP "snakeheadu.bmp"
    IDB_SNAKEHEADL BITMAP "snakeheadl.bmp"
    IDB_SHMASK BITMAP "shmask.bmp"
    IDB_SHMASKD BITMAP "shmaskd.bmp"
    IDB_SHMASKU BITMAP "shmasku.bmp"
    IDB_SHMASKL BITMAP "shmaskl.bmp"
    IDB_MMASK BITMAP "mmask.bmp"
    IDB_MOUSE BITMAP "mouse.bmp"
    IDB_SBODY BITMAP "sbody.bmp"
    IDB_SBMASK BITMAP "sbmask.bmp"
    
    IDR_MYMENU MENU
    BEGIN
         POPUP  "&File"
         BEGIN
              MENUITEM "&New Game", ID_FILE_NEWGAME
              MENUITEM "&Change Difficulty", ID_FILE_CHANGEDIFFICULTY
              MENUITEM "&View Achievements", ID_FILE_VIEWACHIEVEMENTS
              MENUITEM "E&xit", ID_FILE_EXIT
         END
    END
    The snake.cpp has all the drawing functions which is where the glitch is coming from i am guessing.
    Attached Files Attached Files

  4. #4
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,856
    Code:
    //get a copy of the screen DC
    HDC hdc = GetDC(NULL); //NULL is the main screen IIRC, might want to check this
    //and create our memDC
    HDC hdcMem = CreateCompatibleDC(hdc); 
    HBITMAP A = LoadBitmap(GetModuleHandle(NULL), "Somepicture.bmp"); 
    HBITMAP B = LoadBitmap(GetModuleHandle(NULL), "Anotherpicture.bmp"); 
    //variable to hold the default 1x1 BITMAP already in the DC you created
    HBITMAP* origBMP = NULL;
    
    //'catch' the default BITMAP pushed out by you selecting a BITMAP into the DC
    origBMP = (HBITMAP*)SelectObject(hdcMem, A); 
    //draw
    BitBlt(hdc, 0, 0, 32, 32, hdcMem, 0, 0, SRCCOPY); 
    //when we select BITMAP 'B' into the DC, BITMAP 'A' is pushed out. 
    //as we already have A in a variable, we do not need to catch it
    SelectObject(hdcMem, b);  
    BitBlt(hdc, 32, 0, 32, 32, hdcMem, 0, 0, SRCCOPY); 
    //clean up
    //select the default BITMAP back into the DC
    //BITMAP 'B' will be pushed out, as we have this in a variable no need to catch it
    SelectObject(hdcMem, origBMP);
    
    //now memDC is contains the same GDI objects as when we creatd it we can delete it
    DeleteDC(hdcMem);
    
    //delete anything we created
    DeleteObject(A);
    DeleteObject(B);
    
    //release anything we 'GetDC'ed
    ReleaseDC(hdc);
    >>Obviously in a real program i would not call a LoadBitmap() function everytime i drew. I would make it part of the game class construction. So if i deleted these HBITMAPS before the WM_DESTROY i am guessing i would lose the Handle to the actual files.

    Yes. Make these have scope as long as your window is on screen. Create the GDIs when the window opens and when the window closes, select in the original GDIs and delete all GDIs you created.

    >>GetLastError() returns an int is there a way to output this to the debugger so I can check this value?

    int iErr = GetLastError()
    Zero is ERROR_SUCCESS (no error) so you can write conditional code (using #ifdef __DEBUG)
    Conditional Compilation (#if, #ifdef, #ifndef, #else,#elif, #endif, and defined)
    or put a conditional break point on the line (ie iErr != 0)
    or put a watch on iErr in the debugger
    or output to a MessageBox (possibly using FormatMessage() ).

    See this for a code snippet;
    Retrieving the Last-Error Code (Windows)

    What the codes mean
    System Error Codes (Windows)

    I will try to find the time to read your code and comment.
    Last edited by novacain; 11-08-2012 at 10:26 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

  5. #5
    Bored Programmer
    Join Date
    Jul 2009
    Location
    Tomball, TX
    Posts
    407
    Thanks for the info. I have been trying to find the Petzold book to clear everything up, but it seems the local bookshops don't have any good books on windows programming. I think I am going to shy away from using the winapi for large graphic applications as it seems that openGL felt a lot better equiped for this sort of application. If you don't have the time to wade through that project I understand. I just bought a book called professional c++ (though it doesn't have the greatest reviews) and it made me aware that I never spent enough time in the design phase of programming. So that problem will be scrapped rather than finished. Thanks for the information you provided on DCs it helped me clear some things up.

  6. #6
    Sweet
    Join Date
    Aug 2002
    Location
    Tucson, Arizona
    Posts
    1,807
    Just a quick suggestion on your vectoredit code. You can use a template to avoid that code duplication.

    Code:
    template <class T>
    void CopyAndReduce(vector<T> &x)
    {	
    	for(int loopcontrol = 0 ; loopcontrol < x.size()-1; loopcontrol++)
    	{
    		x[loopcontrol] = x[loopcontrol+1];
    	}
    	x.resize(loopcontrol);   
    }
    Woop?

  7. #7
    Bored Programmer
    Join Date
    Jul 2009
    Location
    Tomball, TX
    Posts
    407
    Nice. I am going to look into the template I think that will save me time on a few things and make my code more re-usable. I am all self taught so I know my styles arent the best but for some twisted reason I really enjoy coding lol.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 6
    Last Post: 10-15-2012, 05:16 PM
  2. memory leaks
    By TehOne in forum C Programming
    Replies: 4
    Last Post: 10-10-2008, 09:33 PM
  3. Memory usage and memory leaks
    By vsanandan in forum C Programming
    Replies: 1
    Last Post: 05-03-2008, 05:45 AM
  4. Memory leaks!!!
    By kaushik1986 in forum C++ Programming
    Replies: 2
    Last Post: 10-08-2007, 05:41 AM
  5. Drag and Drop using CImageList, Device contexts and BitBlt
    By MJWhiteman2 in forum Windows Programming
    Replies: 0
    Last Post: 03-03-2005, 06:22 AM

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