?
Solved

CRITICAL_SECTION and OnPaint, OnMouseWheel...

Posted on 2006-03-27
36
Medium Priority
?
1,620 Views
Last Modified: 2013-11-20
Hi Experts,

I'm using CRITICAL_SECTIONs to make sure that my code in OnPaint and OnMouseWheel and OnLButtonClick, etc, runs in their entirety before events call one of the other functions:  (OnMouseWheel, OnLButtonClick, OnPaint)... i.e., when I'm in one, I don't want to be in the other... I'm getting problems of memory being overwritten and scalars deleting descructors.

My question is this:  First of all, is CRITICAL_SECTION what I'm SUPPOSED to be using for this?  Because it's my understanding that the GUI thread is not interruptable - and there's really only 1 thread running in an MFC app... CRITICAL_SECTION protects you from MULTIPLE threads accessing the same piece of code simultaneously....  So is what I'm doing with CRITICAL_SECTION useless in this case?  Is there only one thread here??  If my OnPaint is called, can my OnMouseWheel get called before OnPaint is finished?  How do I prevent this properly??

Thank you so much!
Mike
0
Comment
Question by:mikexpert
  • 19
  • 14
  • 3
36 Comments
 
LVL 7

Expert Comment

by:minnirok
ID: 16306473
Can you post some pseudo code, and what your exact intentions are - your setup sounds strange - for example - you want to disable painting until scrolling is finished - but wouldn't you want the interface to be painting while scrolling so you can see some feedback?

I don't think the critical section is doing what you want. The messages are being queued already as far as I know, ie. if you middle mouse wheel, left click, mouse wheel - the messages will be processed in that order, there's no need to synchronize that. If you want to block painting until scrolling is done - just don't perform any operations that will cause WM_PAINT messages to be generated, thus there will be no need for painting until you're ready.

Anyway, just give us some more information.
0
 

Author Comment

by:mikexpert
ID: 16306677
You're right - what I was doing IS weird - adding critical sections.... But it's because in my OnMouseWheel, I delete items in my array and then fill it up again with the appropriate items (it's a calendar)... So when I scroll, my events are changed in my array and my OnPaint displays these events appropriately.  It all works great until I start clicking events as fast as possible and scrolling at the same time... I get memory check errors (memory being written to after I delete it). and scalar deleting destructors...

Now that you explained that there's a queue and each event gets executed mutually exclusively on the same thread (is that what you were saying?), I'm not so sure what I need to do...

As for pseudocode, here it is:

OnPaint()
{
   EnterCS
  drawevents
  exitCS
}

OnMouseWheel()
{
   EnterCS
  deleteevents
  RepopulateEventsForAppropriateDateRange
  LeaveCS
}

Thanks!
Mike
0
 
LVL 7

Expert Comment

by:minnirok
ID: 16306792
Ok I have the same exact applcation only instead of a calendar I'm drawing huge bitmaps from a stack when the user scrolls with the mouse. This is how I am setup:

OnPaint()
{
    // draw stuff here, no need to protect with critical section.
}

OnMouseWheel()
{
    RepopStuff();
    Invalidate(); // <- now you're only generating a paint msg when your data state is good.
}

RepopStuff()
{
    delete events;
    recreate events;
}

You only need the critical section for multiple threads. Here the GUI messages like scroll and left mouse click are already serialized, in other words they are executed in the order they were generated, and the second one doesn't get handled until the one before it is done.

If all your code is really in the main thread, with the above setup you should see your mouse wheel messages not getting serviced so fast because your RepopStuff() function will still be working. In my application when I try to scroll really fast the paint updates are kept in check by the speed at which RepopStuff() is executed. There is never any overlap of any sort betwixt the functions.

Are you sure you're not doing things in different threads and such?
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 

Author Comment

by:mikexpert
ID: 16306983
Hmm.......

I'm sorry I left some stuff out.. I really thought I was complete - I must be tired.  I am invalidating like you are at the end of OnMouseWheel - but I have one more fact that I completely left out.  The error only occurs when I SELECT my events by left clicking on them AND scroll as fast as possible at the same time.  So sorry I left that out.  That probably makes a big difference right?

So to rephrase my question with what I've learned from what you said and put this new stuff I'm telling you into better perspective, do these messages get executed to completion first?

Add this function to our pseudocode:

OnLeftClick()
{
   GetEventAtPoint()
   Find event in Array
   SetItAsSelected (to highlight it)
   Invalidate()
}

Your help is greatly appreciated.  I have a feeling you'll know what's going on now....

Thank you so much!
Mike
0
 

Author Comment

by:mikexpert
ID: 16306996
Oh - and my scrolling is not "real" scrolling... There is no scrollbar... I don't think that makes a difference...  I actually fake a scroll by drawing the previous and next week with scroll events.  And I don't have any extra threads running either....

Best,
Mike
0
 

Author Comment

by:mikexpert
ID: 16306999
er... mouse wheel events, not scroll events...
0
 

Author Comment

by:mikexpert
ID: 16307277
Latest error:
memory check error at 0x00CC6B94 = 0x00, should be 0xFD
0
 

Author Comment

by:mikexpert
ID: 16307375
Anything wrong with the way I delete the event in DeleteEvents?

void CMyCalendar::DeleteEvents()
{
      while(m_Events.GetSize())
      {
                            // if this is commented out, I can't reproduce the error... but lots of memory leaks of course
            delete m_Events.GetAt(0);

            m_Events.RemoveAt(0);
      }
}

m_Events is defined like this:

typedef CTypedPtrArray<CPtrArray, CMyEvent*> EVENT_ARRAY;

Also, latest error with the delete not commented out:
HEAP[MBS.exe]: HEAP: Free Heap block cc6788 modified at cc682c after it was freed
0
 

Author Comment

by:mikexpert
ID: 16307384

I'm starting to grasp at straws....
0
 
LVL 7

Expert Comment

by:minnirok
ID: 16307446
Sorry was out for a bit  - I've never used CTypedPtArray or any of the ms container types, I always stick to std::vector + std::map, they're fantastic.

Are you sure that calling RemoveAt(0) after delete GetAt(0) is legal? I just don't know these container methods to know what the effect is - I can tell you though that I have done almost the same exact operations with maps and vectors and it works, so I'm hoping your error is localized just to this one deletion part, relating to the characteristics of the ms containers.

When does it crash - on the first iteration of the while loop - the last? Have you tried TRACE()'ing the while loop as it executes?

0
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 16308503
Are you attempting to draw the selected item (and removing it) to cause the crash.

Maybe try using TRACE to print to the debug window the pointer/item index you delete AND the pointer/item index you are currently drawing.
0
 
LVL 45

Accepted Solution

by:
AndyAinscow earned 2000 total points
ID: 16308524
Don't forget that messages are handled in the order that they appear in the queue BUT windows won't put WM_PAINT messages into the queue until the queue is empty - You can NOT assume a synchronisation between the invalidate and the actual painting.
0
 

Author Comment

by:mikexpert
ID: 16309289
Good ideas and all stuff I didn't know about.  I'll be without internet for the next 10 hours...  But I've saved your comments to try them all.  Perhaps I should use std::vector and I will definitely trace the loop.  Also, I did not know WM_PAINT is only placed in the loop when the queue is empty!  Makes sense that it does though - I'll keep that in mind.

Thanks a lot!

Mike
0
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 16309611
I think I said something not quite correct.
An UpdateWindow WILL force a WM_PAINT message to be sent immediately irrespective of the state of the queue.  An Invalidate will flag an area as requiring painting, the system will then generate a WM_PAINT at an appropriate time.  I *think* the WM_PAINT (and WM_TIMER) messages are of low priority and only sent when the queue is empty.

ps. I don't see anything wrong with the typed pointer arrays - use whichever collection class you feel happy coding with.  Each has its own advantages and disadvantages.


pps. For clearing out arrays I usually work from the tail end upwards - efficiency - removing the head item will result in the rest of the items/pointers in the array being moved, removing the tail doesn't.
0
 

Author Comment

by:mikexpert
ID: 16309970

Latest error messages:
HEAP[MBS.exe]: Invalid Address specified to RtlValidateHeap( 003F0000, 00CC6BE8 )

Another run:
HEAP[MBS.exe]: HEAP: Free Heap block cc67d0 modified at cc6894 after it was freed


Hey Andy - good point for the deleting in reverse... I used to do it that way too - I just got lazy and took the "no thinking" route... You reconverted me with the point that they'll all need to be shifted over by one..

0
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 16310028
I think you need to put in some TRACE statements, check what 'pointer' is being drawn and what has been deleted.
I suspect you have just got some timing wrong (or made an invalid assumption in what happens in what order)
0
 

Author Comment

by:mikexpert
ID: 16310196
Crap - now it's happening in the very first OnPaint!!!  As soon as it pops up I'm able to quickly reproduce the bug by selecting 2 events and scrolling quickly at the same time (as much as possible)...

I get this error:
HEAP[MBS.exe]: HEAP: Free Heap block cc1270 modified at cc1314 after it was freed

This is the OnPaint:
void CMyCalendar::OnPaint()
{
      ATLTRACE("BEGIN PAINTING\n");
      if(!m_bInitializedEvents)
      {
            GetEventsForCurrentDateRange();
            SortEventsForProperRedrawing();
            m_bInitializedEvents = TRUE;
      }
      CPaintDC dc(this); // device context for painting

      CMemoryDC dcMem(&dc);
      DoPaint(&dcMem);
      ATLTRACE("END PAINTING\n\n");
}

And this is my stacktrace:
NTDLL! 77f75a58()
NTDLL! 77f9d959()
NTDLL! 77f83eb1()
NTDLL! 77f589f2()
_heap_alloc_base(unsigned int 160) line 200
_heap_alloc_dbg(unsigned int 124, int 1, const char * 0x5f4d0af4 THIS_FILE, int 31) line 378 + 9 bytes
_nh_malloc_dbg(unsigned int 124, int 0, int 1, const char * 0x5f4d0af4 THIS_FILE, int 31) line 248 + 21 bytes
_malloc_dbg(unsigned int 124, int 1, const char * 0x5f4d0af4 THIS_FILE, int 31) line 165 + 27 bytes
operator new(unsigned int 124, int 1, const char * 0x5f4d0af4 THIS_FILE, int 31) line 373 + 22 bytes
operator new(unsigned int 124, const char * 0x5f4d0af4 THIS_FILE, int 31) line 65 + 19 bytes
CPlex::Create(CPlex * & 0x00000000, unsigned int 10, unsigned int 12) line 31 + 23 bytes
CMapPtrToPtr::NewAssoc() line 114 + 21 bytes
CMapPtrToPtr::operator[](void * 0x0d011059) line 222 + 8 bytes
CHandleMap::SetPermanent(void * 0x0d011059, CObject * 0x0012f1fc {CPaintDC hWnd=0x000906c8}) line 183 + 12 bytes
CDC::Attach(HDC__ * 0x0d011059) line 118
CPaintDC::CPaintDC(CWnd * 0x0012f728 {CMyCalendar hWnd=???}) line 1048 + 41 bytes
<b>CMyCalendar::OnPaint() line 278 + 18 bytes</b>
CWnd::OnWndMsg(unsigned int 15, unsigned int 0, long 0, long * 0x0012f3a4) line 1836
CWnd::WindowProc(unsigned int 15, unsigned int 0, long 0) line 1596 + 30 bytes
AfxCallWndProc(CWnd * 0x0012f728 {CMyCalendar hWnd=???}, HWND__ * 0x000906c8, unsigned int 15, unsigned int 0, long 0) line 215 + 26 bytes
AfxWndProc(HWND__ * 0x000906c8, unsigned int 15, unsigned int 0, long 0) line 379
AfxWndProcBase(HWND__ * 0x000906c8, unsigned int 15, unsigned int 0, long 0) line 220 + 21 bytes
USER32! 77d43a50()
USER32! 77d43b1f()
USER32! 77d444f5()
USER32! 77d44525()
NTDLL! 77f75da3()
USER32! 77d44374()
CWnd::RunModalLoop(unsigned long 4) line 3489 + 19 bytes
CDialog::DoModal() line 539 + 12 bytes
CMBSView::OnToolsCalendar() line 801
_AfxDispatchCmdMsg(CCmdTarget * 0x00cbc5a8 {CMBSView}, unsigned int 32793, int 0, void (void)* 0x004027fc CMBSView::OnToolsCalendar(void), void * 0x00000000, unsigned int 12, AFX_CMDHANDLERINFO * 0x00000000) line 88
CCmdTarget::OnCmdMsg(unsigned int 32793, int 0, void * 0x00000000, AFX_CMDHANDLERINFO * 0x00000000) line 302 + 39 bytes
CView::OnCmdMsg(unsigned int 32793, int 0, void * 0x00000000, AFX_CMDHANDLERINFO * 0x00000000) line 162 + 24 bytes
CFrameWnd::OnCmdMsg(unsigned int 32793, int 0, void * 0x00000000, AFX_CMDHANDLERINFO * 0x00000000) line 894 + 33 bytes
CWnd::OnCommand(unsigned int 32793, long 0) line 2099
CFrameWnd::OnCommand(unsigned int 32793, long 0) line 321
CWnd::OnWndMsg(unsigned int 273, unsigned int 32793, long 0, long * 0x0012fb00) line 1608 + 28 bytes
CWnd::WindowProc(unsigned int 273, unsigned int 32793, long 0) line 1596 + 30 bytes
AfxCallWndProc(CWnd * 0x00cbc418 {CChildFrame hWnd=???}, HWND__ * 0x0021041a, unsigned int 273, unsigned int 32793, long 0) line 215 + 26 bytes
CMDIFrameWnd::OnCommand(unsigned int 32793, long 0) line 55 + 35 bytes
CWnd::OnWndMsg(unsigned int 273, unsigned int 32793, long 0, long * 0x0012fcc8) line 1608 + 28 bytes
CWnd::WindowProc(unsigned int 273, unsigned int 32793, long 0) line 1596 + 30 bytes
AfxCallWndProc(CWnd * 0x003f4f48 {CMainFrame hWnd=???}, HWND__ * 0x0029041c, unsigned int 273, unsigned int 32793, long 0) line 215 + 26 bytes
AfxWndProc(HWND__ * 0x0029041c, unsigned int 273, unsigned int 32793, long 0) line 379
AfxWndProcBase(HWND__ * 0x0029041c, unsigned int 273, unsigned int 32793, long 0) line 220 + 21 bytes
USER32! 77d43a50()
USER32! 77d43b1f()
USER32! 77d43d79()
USER32! 77d44374()
CWinThread::Run() line 487 + 11 bytes
CWinApp::Run() line 400
AfxWinMain(HINSTANCE__ * 0x00400000, HINSTANCE__ * 0x00000000, char * 0x00141efa, int 1) line 49 + 11 bytes
WinMain(HINSTANCE__ * 0x00400000, HINSTANCE__ * 0x00000000, char * 0x00141efa, int 1) line 30
WinMainCRTStartup() line 330 + 54 bytes
KERNEL32! 77e8141a()
0
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 16310226
More TRACE statements.  In which function is it failing.  IDENTIFY the problem first.
0
 

Author Comment

by:mikexpert
ID: 16310458
ok - I put TRACE statements everywhere.

It's failing on the line

CPaintDC dc(this); // device context for painting

(the trace before shows up but not the one after) - and the call is in the stack trace above....  I had tried to bold it but the HTML codes I put in don't get interpreted as I thought..

void CMyCalendar::OnPaint()
{
     if(!m_bInitializedEvents)
     {
          GetEventsForCurrentDateRange();
          SortEventsForProperRedrawing();
          m_bInitializedEvents = TRUE;
     }
 
   ATLTRACE("THIS SHOWS UP\n");
     CPaintDC dc(this); // device context for painting
    ATLTRACE("THIS DOES NOT\n");

     CMemoryDC dcMem(&dc);
     DoPaint(&dcMem);
}
0
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 16310544
!

Would this code
     if(!m_bInitializedEvents)
     {
          GetEventsForCurrentDateRange();
          SortEventsForProperRedrawing();
          m_bInitializedEvents = TRUE;
     }
be better elsewhere?  OnInitDialog, OnInitialUpdate, OnCreate
0
 

Author Comment

by:mikexpert
ID: 16310730

Yes it looks bad but I had a tough time finding the right entry point function for initDialog equivalents... These don't seem right either though - the only one available in Classwizard is OnCreate and it doesn't get called...!?

0
 

Author Comment

by:mikexpert
ID: 16310738

I've got a CWnd derived class - not a CDialog....
0
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 16310753
OnCreate not getting called - are you subclassing?
0
 

Author Comment

by:mikexpert
ID: 16310790

Well, I'm not sure of the term subclassing - I have a CWnd derived class which is plugged up to the Custom Control item in the tools of Visual C++ which has it's window procedure registered - with this code:

BOOL CMyCalendar::RegisterWindowClass()
{
  WNDCLASS wndcls;
  HINSTANCE hInst = AfxGetInstanceHandle();

  if (!(::GetClassInfo(hInst, CALENDAR_CLASSNAME, &wndcls)))
  {
    // otherwise we need to register a new class
    wndcls.style            = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
    wndcls.lpfnWndProc      = ::DefWindowProc;
    wndcls.cbClsExtra       = wndcls.cbWndExtra = 0;
    wndcls.hInstance        = hInst;
    wndcls.hIcon            = NULL;
    wndcls.hCursor          = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
    wndcls.hbrBackground    = (HBRUSH) (COLOR_3DFACE + 1);
    wndcls.lpszMenuName     = NULL;
    wndcls.lpszClassName    = CALENDAR_CLASSNAME;

    if (!AfxRegisterClass(&wndcls))
    {
      AfxThrowResourceException();
      return FALSE;
    }
  }

  return TRUE;
}
0
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 16310861
subclassing - that is attaching an existing window to another (related) type of class.
It is what the wizard does when you hook an edit on a dialog to your CEdit derived class for example.

I don't know for sure but I suspect that is what is happening here.
0
 

Author Comment

by:mikexpert
ID: 16310890

Yes it is what's happening here - I drop a custom control on a dialog - and give it a class name.  In my code, I link the two by giving them the same class name.  It's so I can create my own custom control...  (my calendar)...
0
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 16310937
I'm out of ideas for the next step.
0
 

Author Comment

by:mikexpert
ID: 16310957
Thanks for trying Andy.  Very much appreciated.

Have a great day and all the best!  =)
0
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 16311090
If I have any further ideas I'll be back (but it is almost the end of the work day here so don't hold your breath ;-) ).
0
 

Author Comment

by:mikexpert
ID: 16311162
Switzerland!  Must be nice.  I saw your website.  Freelance - fun!   =)
0
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 16311423
Very nice country.  Being a native english speaker means language problems though.
Freelance - Fun, well I can decide when I want to work but it is less security than being employed 'properly'.
Up to now it has gone OK, I have a few clients that keep me busy and I can spend time with the kids.
0
 

Author Comment

by:mikexpert
ID: 16311456

Sounds like a good life to me!
0
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 16312308
There are functions like
PreSubclassWindow, SubclassWindow, SubclassDlgItem
You might want to try putting those into your custom window and seeing if any get called - they could be places to initialise stuff.
0
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 16330348
Ought of interest what happens if you comment out the current code in the OnPaint of this calender?

Try something simple like this to just draw coloured rectangles, cycles between red, green and blue.

static long l=0;
CPaintDC dc(this);
COLORREF rgb;
switch(l++ % 3)
{
case 0:
rgb = RGB(255,0,0);
break;
rgb = RGB(0,255,0);
case 1:
rgb = RGB(0,0,255);
break;
default:
break;
}

dc.FillSolidRect(CRect(0,0,20,20), rgb);


Does it crash or draw anything?
0
 

Author Comment

by:mikexpert
ID: 16330828
Andy - two words:  stray pointer.  Squashed that little critter.

Thanks for all your help!!

It was so tough to debug because I was looking in the wrong place - it was crashing everywhere except for where the true problem was...  =)

Have a great day!
0
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 16331031
Glad you got it working.
0

Featured Post

How to Use the Help Bell

Need to boost the visibility of your question for solutions? Use the Experts Exchange Help Bell to confirm priority levels and contact subject-matter experts for question attention.  Check out this how-to article for more information.

Question has a verified solution.

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

Introduction: Hints for the grid button.  Nested classes, templated collections.  Squash that darned bug! Continuing from the sixth article about sudoku.   Open the project in visual studio. First we will finish with the SUD_SETVALUE messa…
Have you tried to learn about Unicode, UTF-8, and multibyte text encoding and all the articles are just too "academic" or too technical? This article aims to make the whole topic easy for just about anyone to understand.
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.
Is your OST file inaccessible, Need to transfer OST file from one computer to another? Want to convert OST file to PST? If the answer to any of the above question is yes, then look no further. With the help of Stellar OST to PST Converter, you can e…

750 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