CRITICAL_SECTION and OnPaint, OnMouseWheel...

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
mikexpertAsked:
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.

minnirokCommented:
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
mikexpertAuthor Commented:
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
minnirokCommented:
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
Cloud Class® Course: Microsoft Windows 7 Basic

This introductory course to Windows 7 environment will teach you about working with the Windows operating system. You will learn about basic functions including start menu; the desktop; managing files, folders, and libraries.

mikexpertAuthor Commented:
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
mikexpertAuthor Commented:
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
mikexpertAuthor Commented:
er... mouse wheel events, not scroll events...
0
mikexpertAuthor Commented:
Latest error:
memory check error at 0x00CC6B94 = 0x00, should be 0xFD
0
mikexpertAuthor Commented:
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
mikexpertAuthor Commented:

I'm starting to grasp at straws....
0
minnirokCommented:
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
AndyAinscowFreelance programmer / ConsultantCommented:
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
AndyAinscowFreelance programmer / ConsultantCommented:
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

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
mikexpertAuthor Commented:
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
AndyAinscowFreelance programmer / ConsultantCommented:
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
mikexpertAuthor Commented:

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
AndyAinscowFreelance programmer / ConsultantCommented:
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
mikexpertAuthor Commented:
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
AndyAinscowFreelance programmer / ConsultantCommented:
More TRACE statements.  In which function is it failing.  IDENTIFY the problem first.
0
mikexpertAuthor Commented:
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
AndyAinscowFreelance programmer / ConsultantCommented:
!

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

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
mikexpertAuthor Commented:

I've got a CWnd derived class - not a CDialog....
0
AndyAinscowFreelance programmer / ConsultantCommented:
OnCreate not getting called - are you subclassing?
0
mikexpertAuthor Commented:

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
AndyAinscowFreelance programmer / ConsultantCommented:
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
mikexpertAuthor Commented:

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
AndyAinscowFreelance programmer / ConsultantCommented:
I'm out of ideas for the next step.
0
mikexpertAuthor Commented:
Thanks for trying Andy.  Very much appreciated.

Have a great day and all the best!  =)
0
AndyAinscowFreelance programmer / ConsultantCommented:
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
mikexpertAuthor Commented:
Switzerland!  Must be nice.  I saw your website.  Freelance - fun!   =)
0
AndyAinscowFreelance programmer / ConsultantCommented:
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
mikexpertAuthor Commented:

Sounds like a good life to me!
0
AndyAinscowFreelance programmer / ConsultantCommented:
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
AndyAinscowFreelance programmer / ConsultantCommented:
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
mikexpertAuthor Commented:
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
AndyAinscowFreelance programmer / ConsultantCommented:
Glad you got it working.
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.