From: SMTP%"john.quillion@stjohns.co.uk" 24-NOV-1997 20:14:15.36 To: , "Mehrshad Setayesh" CC: Subj: Re: [ntdev] Using Sleep to suspend the execution of a primary thread Return-Path: owner-ntdev@atria.com Received: by arisia.gce.com (UCX V4.1-12C, OpenVMS V7.1 VAX); Mon, 24 Nov 1997 20:13:34 -0500 Received: from gw.atria.com (gw.atria.com [192.88.237.2]) by mercury.mv.net (8.8.8/mem-971025) with SMTP id PAA02501 for ; Mon, 24 Nov 1997 15:16:45 -0500 (EST) Received: by gw.atria.com id Mon, 24 Nov 1997 09:49:13 -0500 Received: from mailgate.stjohns.co.uk by gw.atria.com id Mon, 24 Nov 1997 09:48:56 -0500 Received: from epthraig by mailgate.stjohns.co.uk (SMI-8.6/PIPEX simple 1.27) id OAA10694; Mon, 24 Nov 1997 14:51:51 GMT Message-Id: <199711241451.OAA10694@mailgate.stjohns.co.uk> From: "John Sullivan" To: , "Mehrshad Setayesh" Subject: Re: [ntdev] Using Sleep to suspend the execution of a primary thread Date: Mon, 24 Nov 1997 14:47:36 -0000 X-MSMail-Priority: Normal X-Priority: 3 X-Mailer: Microsoft Internet Mail 4.70.1161 MIME-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Sender: owner-ntdev@atria.com Precedence: bulk Mehrshad Setayesh wrote: > In response to my problem of Sleep which was > ////////////////////////// Start of code > m_myBox.AddString ("String1"); > Sleep (1000); > m_myBox.AddString ("String2"); > Sleep (1000); > m_myBox.AddString ("String3"); > ////////////////////////// End of code > I received several responses from other people and I tried them all without > any improvements. All of them yielded the same results. > These responses where: From the responses I've seen, there appears to be a basic misunderstanding of the difference between the use of PostMessage and SendMessage out there. When the user clicks on your window, or selects menu items, or various events occur in the system that Windows wants to notify applications about, messages are *posted* to the application's message queues. These are asynchronous messages, and will not be processed until such time as the application calls GetMessage() or PeekMessage() (in MFC, by returning back to the application message pump in CWinThread::Run()). The paint message, WM_PAINT, is a special case of this: in *general* Windows waits until no other messages are in the message queue, then checks to see if a window has become invalid (by being uncovered or by calling InvalidateRect()) and if so, posts a WM_PAINT to the message queue. In contrast, when you call CComboBox::AddString(), CWnd::GetWindowText(), CWnd::SetRedraw() etc. the messages (CB_ADDSTRING, WM_GETTEXT, WM_SETREDRAW) are *sent* directly to the destination window's window procedure. Such calls are synchronous: they are fielded immediately and control does not return to the calling code until the results are ready. What happens in your code is a CB_ADDSTRING is sent to the combo, which causes it to (straight away) add a new string to its list, then call InvalidateRect() to indicate that it should be redrawn. What would normally happen next is the caller would return to their message pump and (eventually) find a WM_PAINT message which would cause the change to be reflected on screen, but you don't do this - you wait in the call to Sleep() so the combo is never redrawn. Above, I said that WM_PAINT was in general posted to the message queue - this is only in general. If, after you call AddString(), you make a call to UpdateWindow(), this will cause a message to be sent immediately to the window causing it to redraw there and then. This will fix things for you. Actually, I wouldn't do it like this anyway. Suspending the message queue is a pretty nasty thing to do. While you Sleep(), the application will be completely frozen, and the user will be ignored. They won't even be able to pull down menus, and the window will not redraw should they switch between other applications or dialogs. You might not consider this a problem in this specific case, however in general it's a pretty grotty situation. What you could do is move to an asynchronous timer-based system using code such as the following: int timer_state; void CMyWnd::OnTimer(UINT nIDEvent) { if (nIDEvent==6789) { switch (timer_state) { case 0: m_myBox.AddString ("String2"); timer_state++; break; case 1: m_myBox.AddString ("String3"); KillTimer(6789); break; } } } void CMyWnd::YourFunc() { //... m_myBox.AddString(_T("String1")); timer_state = 0; SetTimer(6789, 1000, NULL); } This way, you can still maintain a sequenced demonstration, but the UI is not completely frozen between events. This would be good if you wanted to support a Skip or Exit button. > ///// From: "Phil Daley" > You need to update the UI "UpdataData(False)" after each addition. > ///// Response: did not have any effect No, wouldn't do. UpdateData() is the MFC function which causes data to be pushed/pulled between program variables and dialog controls. It suffers the same problem as above in that the control's internal state is updated but they will not redraw until you re-enter the message pump. > ///// From: "Tim Boldt" > The problem is that you are not returning to the main message loop, and so > the paint messages are not being processed. One solution is to do a > "mylistbox.Redraw(TRUE)" (not sure 100% if that's the name of the function) > after each "AddString". > ///// Response: its exact name is SetRedraw (TRUE). No improvements. Tim is basically right - the function is called UpdateWindow() though. SetRedraw() something completely different. There is a Win32 call, RedrawWindow(), which is a more advanced form of UpdateWindow(), where you can control exact behavious with respect to when the WM_PAINT is sent, whether a WM_ERASEBKGND is generated, whether child windows are also redrawn. For your puposes, UpdateWindow() should be sufficient. > [...] > ///// From: "Nathan Nesbit" Nathan suggests basically the same thing as I have here. > ///// Response: did not have any effect In which case I think you missed the point of what he was saying, and implemented the wrong thing. See my code above. > ///// From: "Nathan Nesbit" > Try calling CWnd::Invalidate() for the > window, right after calling AddString. Maybe that will do something. > ///// Response: did not have any effect No - the combo does this itself anyway. Invalidate() causes an asynchronous redraw to be queued. You need to force a synchronous redraw. > ///// From: "Vincent Goossens" > If you suspend the thread, you suspend the handling of the thread message > queue. This means that you queue the 3 messages corresponding to AddString > and do not process them until you give hand back to the message loop. No - the strings are added immediately. Just you don't see the results until the message pump is re-entered. > In pure SDK, you would have to set a timer, MsgWaitForSingleObject( timer) > and handle all incoming messages (while(PeekMessage()). I don't know the > way to do that with MFC. I think you're getting confused between timers (SetTimer/KillTimer, TimerProc and WM_TIMER), the timeout value to the WaitXxx functions, and using MsgWaitXxx functions to wait on the message queue in addition to system objects. For timers, you use SetTimer() to start the timer, then just drop back to your regular message pump. No extra action needed. Eventually you'll either have your callback function called, or the destination window (specified in the SetTimer() call) will receive a WM_TIMER message. The only change for MFC is that you catch the WM_TIMER with an ON_WM_TIMER() entry in your window's message map, and you don't need to tell SetTimer() the destination window - it's the window that called SetTimer(). > ///// Response: I have tried setting a timer through SetTimer method and > defining its CALLBACK function. However I am in the process of trying your > solution as is. I think it's easier to specify NULL for the callback function. This causes a WM_TIMER message to be posted to you when the timer expires. John. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - [ To unsubscribe, send email to ntdev-request@atria.com with body UNSUBSCRIBE (the subject is ignored). ]