views:

1218

answers:

3

I have a dialog in MFC with a CStatusBar. In a separate thread, I want to change the pane text of status bar. However MFC complains with asserts? How is it done? An example code would be great.

+1  A: 

Maybe this can help you: How to access UI elements from a thread in MFC.

I don't code C++/MFC myself but I had experienced the similar problem in C# which is known as Cross-thread GUI update.

m3rLinEz
+1  A: 

You should use a message (either with Send- or PostMessage) to notify the UI thread that the status bar text should be updated. Don't try to update UI elements from a worker thread, it's bound to give you pain.

Roel
You should PostMessage, not SendMessage. The reason why MFC and C# complain about these things is that SendMessage is a blocking call and can cause deadlocks.
Joel
+1  A: 

You could post a private message to the main frame window and 'ask' it to update the status bar. The thread would need the main window handle (don't use the CWnd object as it won't be thread safe). Here is some sample code:

static UINT CMainFrame::UpdateStatusBarProc(LPVOID pParam);

void CMainFrame::OnCreateTestThread()
{
    // Create the thread and pass the window handle
    AfxBeginThread(UpdateStatusBarProc, m_hWnd);
}

LRESULT CMainFrame::OnUser(WPARAM wParam, LPARAM)
{
    // Load string and update status bar
    CString str;
    VERIFY(str.LoadString(wParam));
    m_wndStatusBar.SetPaneText(0, str);
    return 0;
}

// Thread proc
UINT CMainFrame::UpdateStatusBarProc(LPVOID pParam)
{
    const HWND hMainFrame = reinterpret_cast<HWND>(pParam);
    ASSERT(hMainFrame != NULL);
    ::PostMessage(hMainFrame, WM_USER, IDS_STATUS_STRING);
    return 0;
}

The code is from memory as I don't have access to compiler here at home, so apologies now for any errors.

Instead of using WM_USER you could register your own Windows message:

UINT WM_MY_MESSAGE = ::RegisterWindowsMessage(_T("WM_MY_MESSAGE"));

Make the above a static member of CMainFrame for example.

If using string resources is too basic then have the thread allocate the string on the heap and make sure the CMainFrame update function deletes it, e.g.:

// Thread proc
UINT CMainFrame::UpdateStatusBarProc(LPVOID pParam)
{
    const HWND hMainFrame = reinterpret_cast<HWND>(pParam);
    ASSERT(hMainFrame != NULL);
    CString* pString = new CString;
    *pString = _T("Hello, world!");
    ::PostMessage(hMainFrame, WM_USER, 0, reinterpret_cast<LPARAM>(pString));
    return 0;
}

LRESULT CMainFrame::OnUser(WPARAM, LPARAM lParam)
{
    CString* pString = reinterpret_cast<CString*>(lParam);
    ASSERT(pString != NULL);
    m_wndStatusBar.SetPaneText(0, *pString);
    delete pString;
    return 0;
}

Not perfect, but it's a start.

Rob
The WM_USER range of messages is the right answer here. RegisterWindowMessage is a little heavy-duty for a private application message, since it guarantees uniqueness throughout the system, and that isn't needed here.
Joel