Memory DC + BitBlt to Screen...

I have an object Cbla and it has two methods: PaintToMemDC and PaintFromMemDCToScreen:

CDC memDC is protected member of Cbla.

void Cbla::PaintToMemDC(CPaintDC *pdc){
      
      memDC.CreateCompatibleDC(pdc);

      CBitmap bitmap;
      bitmap.CreateCompatibleBitmap(pdc, 100, 100);
      CBitmap *oldBitmap = (CBitmap*)memDC.SelectObject(&bitmap);

      memDC.MoveTo(0,0); memDC.LineTo(100,100);

      //memDC.SelectObject(oldBitmap);
}

And:


void Cbla::PaintFromMemDCToScreen(CPaintDC *pdc)
{
      pdc->BitBlt(rand()%150, rand()%150, 100, 100, &memDC, 0, 0, MERGECOPY);
}

I hope that names tells enough.

Obvioulsy since "memDC.SelectObject(oldBitmap);" is commented it will gain a memory leak. But this is the only way I can get that everything on the screen works right: memDCs are sucesfully created in PaintToMemDC method and loaded to the screen in PaintFromMemDCToScreen.
As soon as I uncomment there is no more memory leak but PaintFromMemDCToScreen draws black or random color objects. So something goes totally wrong.

How can I fix this? I just want create some glyphs in PaintToMemDC, save them in memory (CDC or whatever possible) and draw them laterer using PaintFromMemDCToScreen.

Thanks,
-Andrej
iks1Asked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

AlexFMCommented:
Keep bitmap and oldObtmap as class members. In class constructor create memory DC, bitmap and select bitmap in it. In destructor restore old bitmap.
BTW, it is better to change PaintFromMemDCToScreen to:

void Cbla::PaintFromMemDCToScreen(CDC *pdc)

0
DanRollinsCommented:
A agree with AlexFM.  The CBitmap on which you have done your drawing will be destroyed upon exiting the PaintToMemDC() function.  That is equivallent to drawing on a sketchpad, then throwing the top page into the trash... and then wondering why you can no longer see your lovely sketch :-)

If the CBitmap persists... normally by making it part of your class object -- then your drawing will continue to exist when you want to transfer it to the screen.  Once you have blitted to the screen, you can use
    m_bitmap.DeleteObject()
to avoid the memory leak.
0
iks1Author Commented:
Hi guys,

Thank you for the answers...

So I changed my methods to:
  void Cbla::PaintFromMemDCToScreen(CPaintDC *pdc)
  void Cbla::PaintToMemDC(CPaintDC *pdc)
and I agree that this is a good idea.

I also moved:
  CDC m_memDC
  CBitmap m_bitmap
  CBitmap *m_pOldMemDCBitmap
under private class declarations and my both methods now look like this:

void Cbla::PaintToMemDC(CDC *pdc){

     m_memDC.CreateCompatibleDC(pdc);

     m_bitmap.CreateCompatibleBitmap(pdc, 100, 100);
     m_oldBitmap = (CBitmap*)m_memDC.SelectObject(&m_bitmap);

     m_memDC.MoveTo(0,0); m_memDC.LineTo(100,100);

     //memDC.SelectObject(oldBitmap);
}

And:

void Cbla::PaintFromMemDCToScreen(CDC *pdc)
{
     pdc->BitBlt(rand()%150, rand()%150, 100, 100, &m_memDC, 0, 0, SRCCOPY);
}

This code works just like it worked before (in my first post) and is still leading to a memory leak although I also do m_bitmap.DeleteObject() in my own Cbla::Destroy() method and in Cbla::~Cbla() destructor.
In this case I would really like to destroy everything in my Destroy() method... So what can I do?

And I can't move memDC.CreateCompatibleDC(pdc) to constructor since I am making an array of this objects like this:

void CTestDraw1Dlg::OnPaint(){
  CPaintDC dc(this); // Device context for painting
...
  Cbla *bla;
  bla=new Cbla[500];
...
  for (i=0; i<500;i++){
    bla[i].PaintToMemDC(&dc);
  }
...
}

Thanks again,
-Andrej
0
Cloud Class® Course: Microsoft Exchange Server

The MCTS: Microsoft Exchange Server 2010 certification validates your skills in supporting the maintenance and administration of the Exchange servers in an enterprise environment. Learn everything you need to know with this course.

iks1Author Commented:
Sorry (copy/paste problem):

So I changed my methods to:
  void Cbla::PaintFromMemDCToScreen(CDC *pdc)
  void Cbla::PaintToMemDC(CDC *pdc)

Thanks,
-Andrej
0
AlexFMCommented:
>> And I can't move memDC.CreateCompatibleDC(pdc) to constructor.

Why?
0
iks1Author Commented:
AlexFM,

Sorry I am pretty much new to C so this could be done differently but I dont know how...

I am creating an array of my Cbla objects like this:
  Cbla *bla;
  bla=new Cbla[500];
...
  for (i=0; i<500;i++){
    bla[i].PaintToMemDC(&dc);
  }

and as I change my default contructor from Cbla::Cbla() to Cbla::Cbla(CDC *pdc) the compiler complains:
>> error C2512: 'Cbla' : no appropriate default constructor available...

So shuld I leave this constructor alone and create another one with (CDC *pdc) and call it later? Isnt it pretty much the same then if I call PaintToMemDC later?

Or is there a chance to call this new constructor in my case?
Maybe I got all this array of objects initialization wrong...

Thanks again,
-Andrej

0
iks1Author Commented:
Okay,

Cbla bla[10]={&dc, &dc, &dc, &dc, &dc, &dc, &dc, &dc, &dc, &dc};

might be also okay but how do I do this for like 500 times? :)

Thanks,
-Andrej
0
AlexFMCommented:
OK, leave constructor empty and change PaintToMemDC so that it initializes data first time when called:

void Cbla::PaintToMemDC(CDC *pdc)
{
    if ( ! m_memDC.m_hDC )
    {
         m_memDC.CreateCompatibleDC(pdc);
         m_bitmap.CreateCompatibleBitmap(pdc, 100, 100);
         m_oldBitmap = (CBitmap*)m_memDC.SelectObject(&m_bitmap);
     }

     m_memDC.MoveTo(0,0); m_memDC.LineTo(100,100);
}

Restore old bitmap in class destructor.
0
AlexFMCommented:
BTW, &dc, &dc, &dc, &dc, &dc, &dc, &dc, &dc, &dc 500 times is good idea, I like it :)
0
iks1Author Commented:
AlexFM,

I did what you suggested... but it is still leaking :(

If I don't do:
  m_memDC.SelectObject(m_oldBitmap)
in the same method that I did:
  m_bitmap.CreateCompatibleBitmap(pdc, xx, yy);
  m_oldBitmap = (CBitmap*)m_memDC.SelectObject(&bitmap);
then I got a fancy huge memory leak that sometimes don't release all the allocated memory even then when I close the application...

But if I do it (m_memDC.SelectObject(m_oldBitmap)) - my glyphs are painted randomly :)

Is there any other way to quickly draw something on screen DC from memory? Maybe from some int array or something? Because theese bitmaps are driving me crazy...

Thanks,
-Andrej
0
AlexFMCommented:
Post your code again: class and client code.
0
iks1Author Commented:
Hi,

Here it goes:

--- bla.h -----------------------
#if !defined(AFX_BLA_H__E7A016EB_4474_4D53_BD4B_F9225B307E48__INCLUDED_)
#define AFX_BLA_H__E7A016EB_4474_4D53_BD4B_F9225B307E48__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// bla.h : header file
//

/////////////////////////////////////////////////////////////////////////////
// Cbla window

class Cbla : public CWnd
{
// Construction
public:
      Cbla();
      void PaintFromMemDCToScreen(CDC *pdc);
      
      void Destroy();

      void PaintToMemDC(CDC *pdc);


// Operations
public:

// Overrides
      // ClassWizard generated virtual function overrides
      //{{AFX_VIRTUAL(Cbla)
      //}}AFX_VIRTUAL

// Implementation
public:
      virtual ~Cbla();

      CDC m_memDC;
      CBitmap m_bitmap;
      CBitmap *m_pOldMemDCBitmap;


      // Generated message map functions
protected:
      //{{AFX_MSG(Cbla)
            // NOTE - the ClassWizard will add and remove member functions here.
      //}}AFX_MSG
      DECLARE_MESSAGE_MAP()

};

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_BLA_H__E7A016EB_4474_4D53_BD4B_F9225B307E48__INCLUDED_)
--------------- end of bla.h ----

--- bla.cpp ---------------------
// bla.cpp : implementation file
//

#include "stdafx.h"
#include "testdraw1.h"
#include "bla.h"

#include <Wingdi.h>
#include <Windows.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// Cbla
Cbla::Cbla(){

}

void Cbla::PaintFromMemDCToScreen(CDC *pdc)
{
      //--------------------------------------------------
      pdc->BitBlt(rand()%150, rand()%150, 100, 100, &m_memDC, 0, 0, SRCCOPY);
      //--------------------------------------------------
}

Cbla::~Cbla()
{
      m_memDC.SelectObject(m_pOldMemDCBitmap);
}

void Cbla::PaintToMemDC(CDC *pdc){

      CRect rcClient(0,0,100,100);

    if (!m_memDC.m_hDC)
    {
            m_memDC.CreateCompatibleDC(pdc);
            m_bitmap.CreateCompatibleBitmap(pdc, rcClient.Width(), rcClient.Height());
            m_pOldMemDCBitmap = (CBitmap*)m_memDC.SelectObject(&m_bitmap);
     }

     

      //--------------------------------------------------
      m_memDC.Rectangle(CRect(0,0,100,100));
      m_memDC.MoveTo(16,16); m_memDC.LineTo(99,99);
      m_memDC.MoveTo(99,1); m_memDC.LineTo(1,99);
      CString to; to.Format(_T("%d"),rand()%100);
      m_memDC.SetBkMode(TRANSPARENT);
      m_memDC.DrawText(to, CRect (3,2,98,98), DT_NOCLIP);
      //--------------------------------------------------

}


void Cbla::Destroy(){
      m_memDC.SelectObject(m_pOldMemDCBitmap);
}

BEGIN_MESSAGE_MAP(Cbla, CWnd)
      //{{AFX_MSG_MAP(Cbla)
            // NOTE - the ClassWizard will add and remove mapping macros here.
      //}}AFX_MSG_MAP
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// Cbla message handlers
--------------- end of bla.cpp --

Client app - Test dialog:
--- TDlg2.h ---------------------
#if !defined(AFX_TDLG2_H__73B8CE73_EE51_439A_B1F0_8E0060E84CAB__INCLUDED_)
#define AFX_TDLG2_H__73B8CE73_EE51_439A_B1F0_8E0060E84CAB__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// TDlg2.h : header file
//

/////////////////////////////////////////////////////////////////////////////
// TDlg2 dialog

class TDlg2 : public CDialog
{
// Construction
public:
      TDlg2(CWnd* pParent = NULL);   // standard constructor

// Dialog Data
      //{{AFX_DATA(TDlg2)
      enum { IDD = IDD_DIALOG1 };
            // NOTE: the ClassWizard will add data members here
      //}}AFX_DATA


// Overrides
      // ClassWizard generated virtual function overrides
      //{{AFX_VIRTUAL(TDlg2)
      public:
      virtual void OnFinalRelease();
      protected:
      virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
      //}}AFX_VIRTUAL

// Implementation
protected:

      // Generated message map functions
      //{{AFX_MSG(TDlg2)
      afx_msg void OnPaint();
      //}}AFX_MSG
      DECLARE_MESSAGE_MAP()
      // Generated OLE dispatch map functions
      //{{AFX_DISPATCH(TDlg2)
            // NOTE - the ClassWizard will add and remove member functions here.
      //}}AFX_DISPATCH
      DECLARE_DISPATCH_MAP()
      DECLARE_INTERFACE_MAP()
};

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_TDLG2_H__73B8CE73_EE51_439A_B1F0_8E0060E84CAB__INCLUDED_)
--------------- end of TDlg2.h --

--------------- end of TDlg2.cpp-
// TDlg2.cpp : implementation file
//

#include "stdafx.h"
#include "testdraw1.h"
#include "TDlg2.h"
#include "bla.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// TDlg2 dialog


TDlg2::TDlg2(CWnd* pParent /*=NULL*/)
      : CDialog(TDlg2::IDD, pParent)
{
      EnableAutomation();

      //{{AFX_DATA_INIT(TDlg2)
            // NOTE: the ClassWizard will add member initialization here
      //}}AFX_DATA_INIT
}


void TDlg2::OnFinalRelease()
{
      // When the last reference for an automation object is released
      // OnFinalRelease is called.  The base class will automatically
      // deletes the object.  Add additional cleanup required for your
      // object before calling the base class.

      CDialog::OnFinalRelease();
}

void TDlg2::DoDataExchange(CDataExchange* pDX)
{
      CDialog::DoDataExchange(pDX);
      //{{AFX_DATA_MAP(TDlg2)
            // NOTE: the ClassWizard will add DDX and DDV calls here
      //}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(TDlg2, CDialog)
      //{{AFX_MSG_MAP(TDlg2)
      ON_WM_PAINT()
      //}}AFX_MSG_MAP
END_MESSAGE_MAP()

BEGIN_DISPATCH_MAP(TDlg2, CDialog)
      //{{AFX_DISPATCH_MAP(TDlg2)
            // NOTE - the ClassWizard will add and remove mapping macros here.
      //}}AFX_DISPATCH_MAP
END_DISPATCH_MAP()

// Note: we add support for IID_ITDlg2 to support typesafe binding
//  from VBA.  This IID must match the GUID that is attached to the
//  dispinterface in the .ODL file.

// {041D574B-9525-4FF1-9A66-DB406E3A656B}
static const IID IID_ITDlg2 =
{ 0x41d574b, 0x9525, 0x4ff1, { 0x9a, 0x66, 0xdb, 0x40, 0x6e, 0x3a, 0x65, 0x6b } };

BEGIN_INTERFACE_MAP(TDlg2, CDialog)
      INTERFACE_PART(TDlg2, IID_ITDlg2, Dispatch)
END_INTERFACE_MAP()

/////////////////////////////////////////////////////////////////////////////
// TDlg2 message handlers

void TDlg2::OnPaint()
{
      CPaintDC dc(this); // device context for painting
      
      // TODO: Add your message handler code here

      
      Cbla bla[300];
      int i=0;

      dc.DrawText(_T("PaintToMemDC...           "),
            CRect (2,2,98,98), DT_NOCLIP);
      Sleep(1000);

      for (i=0; i<300;i++){
            bla[i].PaintToMemDC(&dc);
      }

      dc.DrawText(_T("PaintFromMemDCToScreen... "),
            CRect (2,2,98,98), DT_NOCLIP);
      Sleep(1000);

      for (i=0; i<300;i++){
            bla[i].PaintFromMemDCToScreen(&dc);
      }

      dc.DrawText(_T("Destroy...                "),
            CRect (2,2,98,98), DT_NOCLIP);
      Sleep(1000);

      /*for (i=0; i<300;i++){
            bla[i].Destroy();

      }*/

      // Do not call CDialog::OnPaint() for painting messages
}

--- TDlg2.cpp -------------------

Thank you again,
-Andrej
0
iks1Author Commented:
Oooops, last comments:

--------------- end of TDlg2.cpp-
and
--- TDlg2.cpp -------------------

are mistaken... Sorry :)

-Andrej
0
AlexFMCommented:
I don't see any place for memory leak here. BTW, why Cbla is derived from CWnd?
Try to catch memory leak using CrtSetBreakAlloc function.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
iks1Author Commented:
Okay thanks...

I am on eMbedded VC4 and can't find CrtSetBreakAlloc function... So I was always checking for a leak using Remote Performance Monitor (WCE420) -> CE Memory Statistics -> Memory Load...

So do you have any idea how to check memory usage here?

Cbla is not derived from CWnd any more and was because I copied it out from a different project just to show the leak...

Okay you helped me enough :)

Thanks for everything...
-Andrej
0
DanRollinsCommented:
You are still using CreateCompatibleBitmap without deleting the previous one.  That causes a memeory leak as you overwrite one HBITMAP with another.   Somewhere in your code, you need:

          if ( m_bitmap.m_hObject != 0 ) {
                    m_bitmap.DeleteObject();
                    m_bitmap.m_hObject= 0;
          }

This belongs in the code at a location very near to, and just before, the place where you call:

          m_bitmap.CreateCompatibleBitmap(pdc, rcClient.Width(), rcClient.Height());
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
System Programming

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.