Solved

CPaintDC - Unhandled Exception

Posted on 2003-11-16
30
1,662 Views
Last Modified: 2013-11-20
Hello,
I have started writing a Grid control derived from CWnd. I am using VC++ 6 with SP5
The architecture is based on Chris Maunder's MFC Grid Control from CodeProject.com. I have used CTypedPtrArray to allocate memory for Grid cells. I have also used a Memory device context in Paint to stop flicker. The code works fine till I load 500 items in the grid. Beyond that I get a debug assertion in my Paint procedure in this line,
CBitmap *oldBitmap = memDC.SelectObject(&bm); I checked during debug and found out that memDC becomes NULL. I tried not using memDC, but the problem still continues..this time with the dc of CPaintDC itself. The Release version just blanks out the entire screen. I am not able to understand, please help.

Thanks,
Balaji

Here are the relevant portions in the code:

BOOL CGridCtrl::OnEraseBkgnd(CDC* pDC)
{
      return (TRUE);
}

void CGridCtrl::OnPaint()
{
      CPaintDC dc(this);

      CRect rect;
      GetClientRect(&rect);

      //CMemDC memDC(&dc);
      //OnDraw(&memDC);

      CDC memDC;
      memDC.CreateCompatibleDC(&dc);

      CBitmap bm;
      bm.CreateCompatibleBitmap(&dc,rect.Width(),rect.Height());
      CBitmap *oldBitmap = memDC.SelectObject(&bm);

      //Erase the background here

      CBrush brush;
      brush.CreateSolidBrush(GetSysColor(COLOR_WINDOW));
      memDC.FillRect(&rect,&brush);
      brush.DeleteObject();

                //Draw the Grid
      OnDraw(&memDC);
      
      dc.BitBlt(rect.left,rect.top,rect.Width(),rect.Height(),&memDC,0,0,SRCCOPY);

      memDC.SelectObject(oldBitmap);
      bm.DeleteObject();
      memDC.DeleteDC();
}
0
Comment
Question by:BalajiS
  • 10
  • 9
  • 9
  • +1
30 Comments
 
LVL 23

Expert Comment

by:Roshan Davis
ID: 9761773
Create a bitmap compatible with the screen that contains one more scanline than the original bitmap may make problems...

   hBMPCompatible = CreateCompatibleBitmap(hDCScreen, 801, 601);   <-- This makes problems,

Becoz u are creating that bitmap compatible with current resolution screen.

So if size exceeds the screen resolution, makes it to the resolution.

Rosh :)
0
 
LVL 23

Expert Comment

by:Roshan Davis
ID: 9761779
This is the general case, but u are taking the rect from ClientRect, this should not make problems....

checking.......
0
 

Author Comment

by:BalajiS
ID: 9761831
One more point to be noted: I added these lines now and observed the following,

CDC memDC;
memDC.CreateCompatibleDC(&dc); //memDC becomes null !....Unable to create memDC ?
HDC hDC = memDC.GetSafeHdc(); //Therefore hDC too is null !

This is not the case if I load the Grid with fewer items. One more thing is that the Grid's rect is unaltered, only scrollbars are added.

Thanks,

Balaji
0
 
LVL 23

Expert Comment

by:Roshan Davis
ID: 9761876
Just try this

Create memory dc once like

Create a pointer member of memory dc

if ( !m_pMemDC )
{
      m_pMemDC = new CDC;

      m_pMemDC->CreateCompatibleDC(&dc);

    bm.CreateCompatibleBitmap(&dc,nScreenWidth,nScreenHieght);
    m_oldBitmap = m_pMemDC->SelectObject(&bm);

}

CBrush brush;
brush.CreateSolidBrush(GetSysColor(COLOR_WINDOW));
m_pMemDC->FillRect(&rect,&brush);
brush.DeleteObject();

//Draw the Grid
OnDraw(m_pMemDC);

Rosh :)
0
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 9761910
Rosh

if ( !m_pMemDC )
{
     m_pMemDC = new CDC;

     m_pMemDC->CreateCompatibleDC(&dc);

    bm.CreateCompatibleBitmap(&dc,nScreenWidth,nScreenHieght);
    m_oldBitmap = m_pMemDC->SelectObject(&bm);

}

also requires a bitmap that exists even when the onPaint routine is finished

It should read

if ( !m_pMemDC )
{
     m_pMemDC = new CDC;

     m_pMemDC->CreateCompatibleDC(&dc);
}
bm.CreateCompatibleBitmap(&dc,nScreenWidth,nScreenHieght);
m_oldBitmap = m_pMemDC->SelectObject(&bm);

 and just to be real awkward what happens if the user changes the resolution/colour depth whilst the app is running.

0
 
LVL 23

Expert Comment

by:Roshan Davis
ID: 9761930
CreateCompatibleBitmap will not copy bitmaps from one DC to another, so it need only once.
Changing resolution makes problems in the above code, that need to handled...
Rosh :)
0
 

Author Comment

by:BalajiS
ID: 9761961
Thanks for the new idea...I just tried it now. Still the problem continues...during debug, CreateCompatibleDC fails once again...

if (!m_pMemDC)
{
      m_pMemDC = new CDC;
      m_pMemDC->CreateCompatibleDC(&dc);
      HDC hDC = m_pMemDC->GetSafeHdc();  //hDC is NULL here....!
}

Balaji
0
 
LVL 23

Expert Comment

by:Roshan Davis
ID: 9761977
m_pMemDC should be the class member, so it will be created once....So put the declaration of that memory pointer in the class header

Rosh :)
0
 

Author Comment

by:BalajiS
ID: 9762020
Yes, I did that, but still the problem continues..... In fact, just to check, I tried this too..
HDC hdcMem = ::CreateCompatibleDC(dc.GetSafeDC()); //hdcMem is NULL ?

Balaji
0
 
LVL 23

Expert Comment

by:Roshan Davis
ID: 9762064
When u are deleting that memory DC? That should be deleted in the destructor

Please mention the ClientRect value of that ASSERT iteration
Rosh :)
0
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 9762112
Yes, but if bm is a local variable it goes out of scope when OnPaint exits and at the next call you have something like

CBitmap bm;
CBitmap *oldBitmap = memDC.SelectObject(&bm);
paint to memDC...
0
 
LVL 23

Expert Comment

by:Roshan Davis
ID: 9762127
Thats true, Use API-::CreateCompatibleBitmap that gives HBITMAP
0
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 9762159
Post exam stress?
0
 
LVL 23

Expert Comment

by:Roshan Davis
ID: 9762165
?? I didn't get
Rosh :)
0
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 9762189
It's usually pre- not post-   (dim memories from the past).

ps.  Anything less than a first and I would be very surprised based on your knowledge at EE.
0
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

 
LVL 23

Expert Comment

by:Roshan Davis
ID: 9762223
>>>> Post exam stress?
>> It's usually pre- not post-   (dim memories from the past).
Still I didn't get :o(
0
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 9762240
Why :o(
I remember looking at massive folders full of paper and thinking I should know all of this.  (But I worry about trivial things).
0
 
LVL 23

Expert Comment

by:Roshan Davis
ID: 9762255
:0)
0
 

Author Comment

by:BalajiS
ID: 9762344

Yes, I am deleting m_pMemDC in my destructor only.

I have copied the code where it asserts while in the Debug mode:

if (!m_pMemDC)
{
     m_pMemDC = new CDC;
     m_pMemDC->CreateCompatibleDC(&dc); //Didn't get created here....
     -----------------------------------------------
}
bm->CreateCompatibleBitmap(&dc,rect.Width(),rect.Height());
CBitmap *oldBitmap = m_pMemDC->SelectObject(bm); //Asserts here becasue m_pMemDC's m_hDC is null ! Its just refusing to create a memory device context for me !

Also checked the ClientRect: It is a valid one...RECT(0,289,0,445)

MFC Source Code:

_AFXWIN_INLINE CBitmap* CDC::SelectObject(CBitmap* pBitmap)
{ ASSERT(m_hDC != NULL); return (CBitmap*) SelectGdiObject(m_hDC,
pBitmap-GetSafeHandle()); }

Strangely, the same code works well with fewer items in the Grid....just to remind you of that !
This is the first time I have encountered such a problem....very very strange but ITS TRUE !

Balaji



0
 
LVL 16

Expert Comment

by:_nn_
ID: 9762415
If the code works with fewer items in the grid, and suddenly fails when that number grows, then it must have something to do with GDI objects being allocated for each grid item. At some point, GDI runs out of resources and can't allocate a new DC. What is GetLastError() when the memory DC creation fails ?
0
 

Author Comment

by:BalajiS
ID: 9762437
That seems to be a valid reason why a dc cannot be created. BTW, GetLastError() returns 0, doesn't seem to convey anything, does it ?

Balaji
0
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 9762468
Just for interest what happens if you don't try drawing offscreen.
Comment out your code for the memdc etc. and draw it directly on the given dc.
Does that function (apart from flicker) correctly?
What happens if you add even more rows (re gdi object theory).
0
 

Author Comment

by:BalajiS
ID: 9762514
After commenting out the MemDC part, the problem still continues, only this time it fails to create a CPen GDI object. Memory allocation with CTypedPtrArray seems to affect GDI.
Please help me in this regard.

Balaji

Relevant portions in code:

typedef CTypedPtrArray<CObArray,CGridCellBase*> GRID_ROW;
//..............................................................................................
//In the Class header section:
CTypedPtrArray<CObArray,GRID_ROW*> m_RowData;

//Initialize function:

TRY
{
m_RowData.SetSize(m_nRows); //m_nRows = 1200 !!!

//Here's the problem...Too much.....? ASSERTS in OnPaint(), works fine till 500 items.
Strangely does not get into my CATCH statement here.

for (int nRow = 0; nRow < m_nRows; nRow++)
{
m_RowData[nRow] = new GRID_ROW;
m_RowData[nRow]->SetSize(m_nCols);

for (int nCol = 0; nCol < m_nCols; nCol++)
{
GRID_ROW *pRow = m_RowData[nRow];
pRow->SetAt(nCol,CreateCell(nRow,nCol));
}
}
}
CATCH (CMemoryException,e)
 {
e->ReportError();
return FALSE;
 }
END_CATCH

0
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 9762556
What is in your CreateCell?

Are you creating a CPen for each cell?  (Bad idea, use one, or a few,  commen CPen objects)
0
 

Author Comment

by:BalajiS
ID: 9762574
This is what is there in CreateCell. After this, in the OnPaint procedure I enumerate all cells for all rows and each cell draws itself given the CDC * from Paint.

CGridCellBase* CGridCtrl::CreateCell(int nRow,int nCol)
{
      if (!m_pRtcDefault || !m_pRtcDefault->IsDerivedFrom(RUNTIME_CLASS(CGridCellBase)))
      {
            ASSERT( FALSE);
            return NULL;
      }
      CGridCellBase* pCell = (CGridCellBase*) m_pRtcDefault->CreateObject();
      if (!pCell)
            return NULL;

      pCell->SetGrid(this);

      return pCell;
}
0
 
LVL 16

Expert Comment

by:_nn_
ID: 9762838
Since CGridCellBase cannot be instantiated, I assume that CreateObject() returns an instance of some descendant. What is m_pRtcDefault and what does it return ?
0
 

Author Comment

by:BalajiS
ID: 9762870
GOT IT !!!

Your previous suggestion,

"What is in your CreateCell? Are you creating a CPen for each cell?  (Bad idea, use one, or a few,  commen CPen objects)"

was very valuable indeed. Suddenly it occured to me that I was creating a CFont object for each and every cell in the Grid and selecting it into Device context. I did this in the Cell's constructor. This was causing a GDI Assertion for more than 500 cells, and therefore OnPaint was unable to provide a Non Null dc.

I have now rewritten my code to create the font once from the Grid and passing it on to the cell each time it requires. That has solved my problem.

Thanks a lot,

Balaji

0
 
LVL 44

Accepted Solution

by:
AndyAinscow earned 250 total points
ID: 9763008
So are you accepting my comment as an answer then?
0
 

Author Comment

by:BalajiS
ID: 9769055
Thanks for that tip ! It helped me search for the problem in a different perspective.

Balaji
0
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 9769747
Your welcome.
0

Featured Post

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
Get filename and folder into excel 7 68
maxBlock challenge 30 111
maven archtype selection in eclipse 1 53
JQuery serialize and unserialize 8 49
Introduction: Displaying information on the statusbar.   Continuing from the third article about sudoku.   Open the project in visual studio. Status bar – let’s display the timestamp there.  We need to get the timestamp from the document s…
Introduction: The undo support, implementing a stack. Continuing from the eigth article about sudoku.   We need a mechanism to keep track of the digits entered so as to implement an undo mechanism.  This should be a ‘Last In First Out’ collec…
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
This is used to tweak the memory usage for your computer, it is used for servers more so than workstations but just be careful editing registry settings as it may cause irreversible results. I hold no responsibility for anything you do to the regist…

911 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

25 Experts available now in Live!

Get 1:1 Help Now