CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Page 1 of 2 12 LastLast
Results 1 to 15 of 25
  1. #1
    Join Date
    Mar 2009
    Posts
    5

    MFC - Cdialog::OnOK() in Thread

    Hi,

    I've a problem, which i don't know how to solve.

    I've a class LoginDlg, which is defined this way
    Code:
    class LoginDlg : public CDialog
    Dialog has one button, which has method
    Code:
    void LoginDlg::OnBnClickedOk()
    In this method a I create a Thread
    Code:
    pLoginThread = AfxBeginThread(LogIn, myUserInfo, THREAD_PRIORITY_NORMAL);
    I want to call OnOK() method after pLoginThread finish its job. Part of code is:
    Code:
    UINT LogIn(LPVOID lParam) {
      // Any code... (Downloading file)
      return 1;
    }
    void LoginDlg::OnBnClickedOk() {
      CWinThread* pLoginThread = AfxBeginThread(LogIn, myUserInfo, THREAD_PRIORITY_NORMAL); 
      // If i write OnOK() here, it doesn't wait on finishing thread 
    }
    I really don't know, how to do it. So please, help me

  2. #2
    Join Date
    Mar 2009
    Posts
    7

    Wink Re: MFC - Cdialog::OnOK() in Thread

    you can call a method instead of creating a thread, is there any specific reason to do that??

  3. #3
    Join Date
    Mar 2009
    Posts
    5

    Re: MFC - Cdialog::OnOK() in Thread

    Yes, there is a reason.

    If I would call a method instead of creating thread, Application would be frozen for a while.

  4. #4
    Join Date
    Feb 2003
    Location
    Iasi - Romania
    Posts
    8,244

    Re: MFC - Cdialog::OnOK() in Thread

    One solution is post an user message from the thread function and call OnOK from its handler function.
    See next example:
    Code:
    // TestOKDlg.h
    #define WM_DO_END_DIALOG (WM_APP + 1)
    
    class CTestOKDlg : public CDialog
    {
        //...
        afx_msg LRESULT OnDoEndDialog(WPARAM wParam, LPARAM lParam);
    };
    Code:
    // TestOKDlg.cpp
    BEGIN_MESSAGE_MAP(CTestOKDlg, CDialog)
    // ...
        ON_MESSAGE(WM_DO_END_DIALOG, OnDoEndDialog)
    END_MESSAGE_MAP()
    //...
    UINT AFX_CDECL LogIn(LPVOID lpParam)
    {
        // ...some very long task...
        ::PostMessage((HWND)lpParam, WM_DO_END_DIALOG, 0, 0);
        return 0;
    }
    
    void CTestOKDlg::OnBnClickedOk()
    {
        AfxBeginThread(LogIn, m_hWnd, THREAD_PRIORITY_NORMAL); 
    }
    
    LRESULT CTestOKDlg::OnDoEndDialog(WPARAM wParam, LPARAM lParam)
    {
        OnOK();
        return 0;
    }
    Last edited by ovidiucucu; April 3rd, 2009 at 09:04 AM.
    Ovidiu
    "When in Rome, do as Romans do."
    My latest articles: https://codexpertro.wordpress.com/

  5. #5
    Join Date
    Jun 2004
    Posts
    1,352

    Re: MFC - Cdialog::OnOK() in Thread

    I'm not sure what you are trying to do with the Dialog box. It sounds like you want a screen on the app which is active while downloading a file, or you want to continue using the dialog box while downloading a file.

    Anyhow, if your dialog box is modal, you are going be stuck with in dialog box while downloading.


    If I wanted to continue using the app while download, I would start thread and send it a pointer to the Dialog box once you started it, then execute OnOK() within the thread when it's done downloading:

    Code:
     AfxBeginThread(LogIn, myUserInfo, THREAD_PRIORITY_NORMAL, LoginDlg  *pDlg ){
    
     ... dosomething
    
    AfxMessageBox("Thread Done, Closing Dialog Box");
    pDlg->OnOk();
    
    ....
    
    pDlg->CallanyFunctionIntheDialogBox();
    pDlg->AccessAnyMemberInDialog;
    pDlg->DoAnythingToTheDialogBoxorItItsParentorParentVars;
    pDlg->I_Own_You;
    
    
    
    
    }; 
    
    
     AfxBeginThread(......., ...., this );

    ...If I wanted to use a dialog box/app until the download is done, That's how I would do it, ..... but, if that where the case a Modless Dialog would do the job better called with in the App/Dialog/CFormView.

    Or you create your dialog box as Modless box, which the Thread closes when it is done.

    If you want to start a thread and update a dialog box/app during certain points in the thread, I would think you have to pass a pointer to the DialogBox/App/View/Docuement, .... etc.

    Try passing a pointer to the dialog box in your thread. I haven't done it, but I pass pointers to other dialogboxes from other dialog boxes all the time. In my apps I have about 20 dialog boxes which I can access any button, function, variable, etc at anytime.

    ...I havn't done much with threads, but I think it should work.
    Last edited by ADSOFT; April 3rd, 2009 at 09:27 AM.
    Rate this post if it helped you.

  6. #6
    VictorN's Avatar
    VictorN is offline Super Moderator Power Poster
    Join Date
    Jan 2003
    Location
    Hanover Germany
    Posts
    20,430

    Re: MFC - Cdialog::OnOK() in Thread

    2 Bald3rr and ADSOFT:
    the only acceptable solution is the one provided by ovidiucucu.
    Some more information about using working threads you could find here
    Victor Nijegorodov

  7. #7
    Join Date
    Jun 2004
    Posts
    1,352

    Re: MFC - Cdialog::OnOK() in Thread

    Quote Originally Posted by VictorN View Post
    2 Bald3rr and ADSOFT:
    the only acceptable solution is the one provided by ovidiucucu.
    Some more information about using working threads you could find here

    Maybe accepatable to you, but I wouldn't do it that way.
    Rate this post if it helped you.

  8. #8
    VictorN's Avatar
    VictorN is offline Super Moderator Power Poster
    Join Date
    Jan 2003
    Location
    Hanover Germany
    Posts
    20,430

    Re: MFC - Cdialog::OnOK() in Thread

    Quote Originally Posted by ADSOFT View Post
    Maybe accepatable to you, but I wouldn't do it that way.
    Then you will be in trouble!
    Victor Nijegorodov

  9. #9
    Join Date
    Mar 2009
    Posts
    5

    Re: MFC - Cdialog::OnOK() in Thread

    Thanks everyone, especially to ovidiucucu. It's working fine.

  10. #10
    Join Date
    Jun 2004
    Posts
    1,352

    Re: MFC - Cdialog::OnOK() in Thread

    Quote Originally Posted by VictorN View Post
    Then you will be in trouble!

    I doubt it, I bet I can get a thread to work that that I pass a pointer(to the thread function that is) to a dialog box or better yet a pointer to my View Class with out having to write all that Code the OV did. With a pointer to my View Class I can control anything in View Class, it's related Dialog Boxes and the CDocument Class, and probobly the whole App. Heck the thread could be a monitor to the app makeing sure that the user hasn't done anything to violate integrity or I could update the Application or it's Database without the user knowing it, do backups, ....etc, while working out of just one thread.


    That's cool if OV wants to do it with all that code, to each his own! However; ...."There's more than one way to skin a cat!" I would rather just pass one pointer and go from there.
    Last edited by ADSOFT; April 3rd, 2009 at 06:18 PM.
    Rate this post if it helped you.

  11. #11
    Arjay's Avatar
    Arjay is offline Moderator / EX MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    13,490

    Re: MFC - Cdialog::OnOK() in Thread

    Quote Originally Posted by ADSOFT View Post
    With a pointer to my View Class I can control anything in View Class, it's related Dialog Boxes and the CDocument Class, and probobly the whole App.
    The bottom line is that any shared data needs to be protected when accessed from multiple threads. When you simply pass a view pointer to the thread and access MFC controls within the thread, you aren't synchronizing data access properly and it will lead to race conditions.

  12. #12
    Join Date
    Jun 2004
    Posts
    1,352

    Re: MFC - Cdialog::OnOK() in Thread

    Quote Originally Posted by Arjay View Post
    The bottom line is that any shared data needs to be protected when accessed from multiple threads. When you simply pass a view pointer to the thread and access MFC controls within the thread, you aren't synchronizing data access properly and it will lead to race conditions.
    Well I can tell you know about threads, and when it comes to data synchronizing it's all how you write the thread.


    The OP wanted to access a control from within a thread, I just took it to another level. Data synchronization is not just related to threads, it must also be taken into consideration at the application level.


    The general topic here is access to controls from a worker threads.
    Rate this post if it helped you.

  13. #13
    Arjay's Avatar
    Arjay is offline Moderator / EX MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    13,490

    Re: MFC - Cdialog::OnOK() in Thread

    Quote Originally Posted by ADSOFT View Post
    The OP wanted to access a control from within a thread, I just took it to another level. Data synchronization is not just related to threads, it must also be taken into consideration at the application level.
    Bottom line is that you shouldn't access MFC controls from a secondary thread.

    Sure, you can pass a pointer of an MFC object to a secondary thread, but if you need to access MFC class functionality from the pointer, you'll need to provide your own synchronization. You can do that using the method that Ovidiu and Victor were talking about or you can create thread safe accessor methods.

  14. #14
    Join Date
    Jun 2004
    Posts
    1,352

    Re: MFC - Cdialog::OnOK() in Thread

    Quote Originally Posted by Arjay View Post
    Bottom line is that you shouldn't access MFC controls from a secondary thread.

    Sure, you can pass a pointer of an MFC object to a secondary thread, but if you need to access MFC class functionality from the pointer, you'll need to provide your own synchronization. You can do that using the method that Ovidiu and Victor were talking about or you can create thread safe accessor methods.

    Ok, well I'm just getting into threads and it seems that you have tons of experience with them.

    .... here's what I've ended up with, I'm hungry so I have to stop for a while(maybe you will respond while I get some dinner); I sent the threading function a this pointer and in the worker thread recast it back to a CFormview which has tons of buttons. It works but I keep getting the following Assertion:


    HTML Code:
    	CObject* p;
    		ASSERT((p = pMap->LookupPermanent(m_hWnd)) != NULL ||
    			(p = pMap->LookupTemporary(m_hWnd)) != NULL);
    		ASSERT((CWnd*)p == this);   // must be us
    
    		// Note: if either of the above asserts fire and you are
    		// writing a multithreaded application, it is likely that
    		// you have passed a C++ object from one thread to another
    		// and have used that object in a way that was not intended.
    		// (only simple inline wrapper functions should be used)
    		//
    		// In general, CWnd objects should be passed by HWND from
    		// one thread to another.  The receiving thread can wrap
    		// the HWND with a CWnd object by using CWnd::FromHandle.
    		//
    		// It is dangerous to pass C++ objects from one thread to
    		// another, unless the objects are designed to be used in
    		// such a manner.
    What's a "simple inline wrapper function"? that's probobly why CWD::FormHandle didn't work.


    I understand that the SendMessage via m_hwnd approach works, however it seems like Microsoft is implying to use simple wrapper function with CWD::FormHandle ....
    Last edited by ADSOFT; April 4th, 2009 at 01:21 AM.
    Rate this post if it helped you.

  15. #15
    Arjay's Avatar
    Arjay is offline Moderator / EX MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    13,490

    Re: MFC - Cdialog::OnOK() in Thread

    This is why you can't usually pass MFC objects to another thread. The simple wrapper objects that the MFC comments are referring to are the lightweight inlined functions that MFC uses to send a message to a control. Anything that causes MFC to walk its internal maps or access more complex functionality will be likely to fail.

    At any rate, take a look at the sample code in the post: Sharing a thread safe std::queue between threads w/progress bar updating

    This sample includes code that shares a thread safe std::queue between two worker threads and the main UI thread. The worker threads push items onto the queue (with each worker thread pushing the items on at different rates). The UI thread gets signalled when an item is available in the queue via PostMessage and a user defined message. The pushing the items on the queue and popping them off the queue are done in a thread safe manner (using a critical section lock). The threading and queue access is wrapped into a progress manager class so that the thread management and queue synchronization is encapsulated within the class.

    This approach makes a clean interface between the MFC UI code and the thread code. It makes debugging easier because the UI logic (enabling/disabling buttons, changing state, etc.) can be worked out separately from the threading code. Likewise, the threading code can be debugged separately from the UI code (by calling the methods of the progress class directly).

    Tying this back to the post's related topics, you'll notice I pass in a hWnd from the dialog in the CProgressMgr::Start method. This hWnd lets the threads know where to send the queue notification messages.

    Start (et al) button handler code:
    Code:
    //+------------------------------------------------------
    // Start, pause, and resume button handler
    //+------------------------------------------------------
    void CStartStopDlg::OnBnClickedStartPauseResume()
    {
    	m_btnStartPauseResume.EnableWindow( FALSE );
    	m_btnStop.EnableWindow( FALSE );
    
    	switch( ToggleSPRState( ) )
    	{
    	case TS_START:
    		// Remove the list control items		
    		m_ctrlList.DeleteAllItems( );
    		m_uListItemCount = 0;
    
    		// Start the threads
    		m_ProgressMgr.Start( GetSafeHwnd( ) );
    		break;
    	case TS_PAUSE:
    		// Pause the thread
    		m_ProgressMgr.Pause( );
    		break;
    	case TS_RESUME:
    		// Resume the thread
    		m_ProgressMgr.Resume( );
    		m_ThreadState = TS_START;
    		break;
    	default:
    		ASSERT( 0 ); // We shouldn't reach this
    	}
    
    	m_btnStartPauseResume.EnableWindow( TRUE );
    	m_btnStop.EnableWindow( TRUE );
    }
    WM_USER_INC_PROGRESS handler code. This message gets sent by the secondary thread to notify the main thread that a new item has been pushed on the queue. Sorry about using the magic numbers in the case statements. They represent T1 and T2 (but my bad for using magic numbers).
    Code:
    //+------------------------------------------------------
    // OnIncProgress handler. Receives user defined message 
    // posted from the secondary thread. When we receive this
    // message we increment the progress bar and add the item
    // to the list control.
    //+------------------------------------------------------
    LRESULT CStartStopDlg::OnIncProgress( WPARAM wParam, LPARAM lParam )
    {
    	UINT uID = (UINT) lParam;
    
    	if( TS_START != m_ThreadState ) return 0;
    
    	// Increment the progress controls
    	switch( uID )
    	{
    	case 1:
    		m_ctrlProgress1.StepIt( );
    		break;
    	case 2:
    		m_ctrlProgress2.StepIt( );
    		break;
    	}
    
    	// Get the item out of the queue and put it in the list control
    	InsertItemIntoListView( );
    
    	return 1;
    }
    Let's look at the InsertItemIntoListView( ) method referenced above. This method accesses the first item in the queue, inserts it into the listview control and then deletes it from the queue. You'll notice that there doesn't appear to be any thread synchronization (because it's encapsulated entirely within the CProgressMgr thread).
    Code:
    //+------------------------------------------------------
    // Called when a notification message has been received from
    // one of the secondary threads. Removes one or more items
    // from the shared queue in a thread safe manner and inserts
    // the item into the list control.
    //+------------------------------------------------------
    void CStartStopDlg::InsertItemIntoListView( )
    {
    	CItem* pItem = NULL;
    
    	CString sCount;
    	CString sThreadID;
    	CString sThreadCount;
    
    	// Retrieve the next queue item (thread-safe method)
    	while( NULL != ( pItem = m_ProgressMgr.GetNextItem( ) ) )
    	{
    		// Format the listview column strings
    		sCount.Format( _T( "%d" ), m_uListItemCount );
    		sThreadID.Format( _T( "This is an item from thread: %d" ), pItem->GetID( ) );
    		sThreadCount.Format( _T( "%d" ), pItem->GetCount( ) );
    
    		// Insert the item into the list view and ensure the item is visible
    		m_ctrlList.InsertItem( m_uListItemCount, sCount );
    		m_ctrlList.SetItemText( m_uListItemCount, 1, sThreadID );
    		m_ctrlList.SetItemText( m_uListItemCount, 2, sThreadCount );
    		m_ctrlList.EnsureVisible( m_uListItemCount, TRUE );
    
    		// Remove this item from the queue (thread-safe method)
    		m_ProgressMgr.PopItem( );
    
    		m_uListItemCount++;
        }
    }
    Let's take a look at the thread safe queue accessor methods. There are 3 of them: The GetNextItem and PopItem methods that are accessed by the UI thread and the PushItem method which is accessed by the secondary threads.
    Code:
        // Gets the next item from the ItemQueue
    CItem* GetNextItem( )
    {
    	CAutoLockT< CItemQueue > lock(&m_ItemQueue);
        
    	if( TRUE != m_ItemQueue.empty( ) )
    	{
    		return m_ItemQueue.front( );
    	}
    
    	return NULL;
    }
    
    // Removes the front item from the ItemQueue
    void PopItem( )
    {
    	CAutoLockT< CItemQueue > lock( &m_ItemQueue );
    
    	if( TRUE != m_ItemQueue.empty( ) )
    	{
    		delete m_ItemQueue.front( );
    		m_ItemQueue.pop( );
    	}
    }
    
    HRESULT PushItem( CItem* pItem )
    {
    	HRESULT hr = S_OK;
    
    	CAutoLockT< CItemQueue > lock( &m_ItemQueue );
    
    	try
    	{
    		m_ItemQueue.push( pItem );
    	}
    	catch( std::bad_alloc )
    	{
    		hr = E_OUTOFMEMORY;
    	}		
    
    	return hr;
    }
    The thread safety of these methods may not be obvious, but they are handled by the CAutoLockT class which is locking a wrapper class to a
    critical section object. This class simply obtains a critical section lock (EnterCriticalSection(...)) in its constructor and releases the lock (LeaveCriticalSection(...)) in its destructor which gets called when the CAutoLockT instance goes out of scope. The single line of code that gets this done ensures the queue is only ever accessed by one thread at a time.
    Last edited by Arjay; April 4th, 2009 at 02:40 AM.

Page 1 of 2 12 LastLast

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured