dimanche 3 juin 2018

C++11 threads to update MFC application windows. SendMessage(), PostMessage() required?

After spending a bit of time with simple UWP applications with C++/CX and ++/WinRT I have begun to enjoy some of the features of targeting that environment for Windows UI app development.

Now having to go back to a more familiar MFC application development I want to change my approach to something that is similar to UWP app development. The idea is to use asynchronous C++11 threads to generate content and modify content that is displayed in the MFC UI.

The main change I want to make is to use C++11 threads to off load some time consuming tasks and have those threads communicate the results back to the main MFC UI.

Some of the tasks that I am looking to off load onto C++11 threads, which are similar to what I would use with asynchronous tasks with C++/CX and C++/WinRT in UWP apps are:

  • connect to and exchange data with another computer
  • open a data file and parse through it to update the UI view
  • convert a data file to another format such as CSV and export to a file
  • read a file in a format such as CSV and convert the content into a data file
  • perform searches and filtering of the presentation of the data file in the UI

The problem I am running into is similar to the problem described in Can I have multiple GUI threads in MFC? however I am looking for a general approach rather than the specific progress bar update in that question.

I have been trying a simple test with an experimental MFC app using the Visual Studio template which has a tree control docked on the left to build the tree in a worker thread.

If I have a CViewTree, an MFC window that displays a tree view, which I want to update from a C++11 thread, I am currently using ::PostMessage() to request that the tree control in the docked pane is updated.

If I use a lambda with a global std::thread such as the following code:

std::thread t1;

void CClassView::FillClassView()
{
    // ::SendMessage() seems to deadlock so ::PostMessage() is required.
    t1 = std::thread([this]() { Sleep(5000);  ::PostMessage(this->m_hWnd, WM_COMMAND, ID_NEW_FOLDER, 0); });

}

the message handler for the MFC docked pane which looks like:

void CClassView::OnNewFolder()
{
    t1.join();   // this join seems to deadlock if ::SendMessage() is used.

    AddItemsToPane(m_wndClassView);
}

does indeed update the MFC docked pane with the tree control content just as if the function AddItemsToPane(m_wndClassView); were called at the same place where the C++11 thread is created. The pane update is delayed by 5 seconds when the C++11 thread is used just to provide a visible indication that the thread approach is actually working.

My problem is that I want the C++11 thread to create the content for the tree control and provide it to the docked pane rather than having the docked pane generate the content.

Thus far the only approach I can think of is to develop my own class library that will provide C++11 thread analogues to the MFC library and controls using ::PostMessage() to send the appropriate Windows messages to the designated MFC window or control.

I am wondering if it is possible to have the C++11 threads have their own, shadowing MFC control which they update and then send a message to the UI asking the UI to update its displayed control with the contents of the shadow MFC control? Or is there some other approach that people are using?

I am looking for some other, less arduous approaches to solving this problem of updating an MFC UI from C++11 threads.

By the way #1 ::SendMessage() appears to deadlock on the join() in CClassView::OnNewFolder() which I assume means that some kind of synchronization between the C+11 thread and the UI thread is blocking the C++11 thread from reaching it's side of the join()?

By the way #2 It would also seem that using the actual Window handle rather than the this pointer in the lambda for the C++11 thread would be safer. Just in case the this pointer becomes undefined for some reason such as the control is removed?

Aucun commentaire:

Enregistrer un commentaire