Ask a questionAsk a question
 

QuestionPostMessaage and UI hangs

  • Friday, November 06, 2009 5:58 AMabhay4u Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Hi,

    I've created an MFC application with one worker thread. The worker thread frequently post message to UI thread to update its control. During this period, UI does not receive input message such as mouse or keyboard messages. The UI receive input message when worker thread is stopped.

    I've read in MSDN that PostMessage has higher priority than input message. My requirement is that I want my window to process input message while its controls are being updated.

    If I reduce the frequency of PostMessaage, the problem is solved, but that's not my requirement.

    I've tried another non-queued message SendNotifyMessage() from worker thread, but that also does not work.

    I am uploading my sample application. Is there any way to filter out some messages? Please help me out.

    Thank you.


    uploads/27862/uithreadtest.zip

All Replies

  • Friday, November 06, 2009 7:28 AMViorel_MVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    I think you have to avoid putting of multiple update messages. Since PeekMessage cannot be used from threads, you can consider another way. For example define a common flag (volatile bool mFlag), which will be true when there is a pending update message. Use appropriate thread synchronisation.

     

    The body of posting loop can look like this:

     

        ::EnterCriticalSection(&mCritSec);

        // . . . Update data . . .

        if(!mFlag)

        {

            pDlg->PostMessage(WM_UPDATE_DLG, 0, 0);

            mFlag = true;

        }

        ::LeaveCriticalSection(&mCritSec);

     

    The handler can look like this:

     

        ::EnterCriticalSection(&mCritSec);

        // . . . Update controls . . .

        mFlag = false;

        ::LeaveCriticalSection(&mCritSec);

     

  • Friday, November 06, 2009 7:47 AMhgn Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    You are spamming your own message queue. Updates like this should be done when the the application is idle, that is when the message que is empty. A solution how OnIdle can be implemented in CDialog can be found here.

  • Saturday, November 07, 2009 6:46 AMabhay4u Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Hi,

    Thank you for your quick reply, but that doesn't solve my problem because if I use critical section, only one thread can use it at a time which makes application slow. I am actually enumerating drives and showing file count, folder count, and path. This is job of worker thread while UI thread' job is to show these information. If i use critical section, the worker thread's performance will be hampered, and I don't want to do that. Some other suggestion please.

    Thank you.
  • Saturday, November 07, 2009 6:50 AMabhay4u Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Hi,

    Nice suggestions, but that doesn't solve my problem. Actually, I have to enumerate drives in worker thread, and I've to show file count, folder count, and file path on UI while enumeration is going on. If I do this in OnIdle(), the information will be shown on UI even after worker thread finishes enumeration. Please suggest some other solution.

    Thank you.
  • Saturday, November 07, 2009 8:28 AMhgn Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Has Code
    unsigned int WINAPI SearchThreadFn(	PVOID pvContext	)
    {
    	if (NULL == pvContext)
    	{
    		return 0;
    	}
    	CuithreadtestDlg *pDlg = (CuithreadtestDlg *)pvContext;
    	for (int i = 0; i < 10000; i++)
    	{
    		_stprintf(pDlg->m_szFileCnt, _T("%d"), i);
    		_stprintf(pDlg->m_szFldrCnt, _T("%d"), i * i);
    		_stprintf(pDlg->m_szPath, _T("%s"), _T("C:\\abc\\xyz\\dbf\\test.txt"));
    	}
    	wcscpy(	pDlg->m_szFileCnt, _T("") );
    	wcscpy( pDlg->m_szFldrCnt, _T("") );
    	wcscpy( pDlg->m_szPath, _T("") );
    	return 0;
    }
    
    bool CuithreadtestDlg::OnIdle(LONG lCount)
    {
    	if ( ::IsWindow(m_Stc_FileCount.m_hWnd) )
    		m_Stc_FileCount.SetWindowText(m_szFileCnt);
    	if ( ::IsWindow(m_Stc_FolderCount.m_hWnd) )
    		m_Stc_FolderCount.SetWindowText(m_szFldrCnt);
    	if ( ::IsWindow(m_Stc_Path.m_hWnd) )
    		m_Stc_Path.SetWindowText(m_szPath);
    	return false;
    }
    
    LRESULT CuithreadtestDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
    {
        DWORD QueueStatus;
        LRESULT resValue = 0;
        bool OnIdleRetVal = true;
    
        if(message == WM_IDLE) {
            OnIdleRetVal = OnIdle((UINT)wParam);
            if(!OnIdleRetVal)
                wParam = 0;
        } else
            resValue = CDialog::WindowProc(message, 
            wParam, lParam);
    
        QueueStatus = GetQueueStatus(QS_ALLINPUT);
    
        if(HIWORD(QueueStatus) == 0)
            PostMessage(WM_IDLE, 
                wParam + (OnIdleRetVal ? 1 : 0), 0);
    
        return resValue;
    
    }
    
  • Saturday, November 07, 2009 12:13 PMabhay4u Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Hi,

    Thanks for your effort. I tried it in my application, but UI updates are quite non-interactive. UI is updated less frequently. I've tried WM_TIMER, using this UI update is quite interactive & UI doesn't hang as well.

    So how about using WM_TIMER will 1000 ms interval, in which UI will be updated. I've read in MSDN that WM_TIMER message is picked up from message queue when there is no another pending message in the queue. So I think, it's like OnIdle. Well, what is the difference between these both technique, and is there any other issue.

    Any suggestion please.

    Thank you.