C++11 threads to update MFC application windows. SendMessage(), PostMessage() required? - multithreading
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 offload 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 offload 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 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 its side of the join()?
Yes there is a deadlock as the thread waits for SendMessage() to return while the message handler is waiting at the join() for the thread to finish. According to the Windows Dev Center SendMessage function:
Sends the specified message to a window or windows. The SendMessage
function calls the window procedure for the specified window and does
not return until the window procedure has processed the message.
To send a message and return immediately, use the SendMessageCallback
or SendNotifyMessage function. To post a message to a thread's message
queue and return immediately, use the PostMessage or PostThreadMessage
function.
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?
By the way #3 The Microsoft concurrency namespace, provided via #include <ppltasks.h>, is an alternative to C++11 threads. The concurrency namespace functions are at a higher level of abstraction than C++11 threads and are easier to use.
For instance the above use of std:thread could be rewritten as:
void CClassView::FillClassView()
{
concurrency::create_task([this]() { Sleep(5000); ::SendMessage(this->m_hWnd, WM_COMMAND, ID_NEW_FOLDER, 0); });
}
and this does not require the use of a std::thread join() to cleanly terminate the thread. Also SendMessage() or PostMessage() may be used to send the Windows message since we do not have the same deadlock issue as we have with C++11 threads.
Notes
Note #1: About Messages and Message Queues as well as Using Messages and Message Queues.
For MFC specific content see Messages and Commands in the Framework.
Note #2: Multithreading with C++ and MFC and specifically Multithreading: Programming Tips which says.
If you have a multithreaded application that creates a thread in a way
other than using a CWinThread object, you cannot access other MFC
objects from that thread. In other words, if you want to access any
MFC object from a secondary thread, you must create that thread with
one of the methods described in Multithreading: Creating
User-Interface Threads or Multithreading: Creating Worker Threads.
These methods are the only ones that allow the class library to
initialize the internal variables necessary to handle multithreaded
applications.
Note #3: UWP APIs callable from a classic desktop app which says:
With some notable exceptions, the general rule is that a Universal
Windows Platform (UWP) API can be called from a classic desktop app.
The two main areas of APIs that are an exception to this general rule
are XAML UI APIs, and APIs that require the calling app to have a
package identity. UWP apps have a well-defined app model, and they
have a package identity. Classic desktop apps do not have a
well-defined app model, and they do not have a package identity. A
classic desktop app that has been converted to a UWP app does have a
package identity.
See as well the following blogs from Sep 2012 about WinRT with VS 2012 and Windows 8. Though C++/WinRT with VS 2017 seems more appropriate for Windows 10 than Windows Runtime template Library (WRL) used:
Accessing WinRT From Desktop apps (Part 1)
Accessing WinRT from Desktop Apps (Part 2)
Note #4: MFC Desktop Applications which is a jumping off point with lots of links. See also MFC COM which is a jumping off point with lots of links about MFC with COM along with this article Introduction to COM. See as well MFC Macros and Globals.
As for using AfxGetMainWnd() to get the main application window, the Microsoft developer center has this to say in the article AfxGetMainWnd:
If AfxGetMainWnd is called from the application's primary thread, it
returns the application's main window according to the above rules. If
the function is called from a secondary thread in the application, the
function returns the main window associated with the thread that made
the call.
After some experimentation, there are a few recommendations that I feel comfortable making.
the concurrency task functionality is easier to use than C++11 std:thread and is more flexible in using with coroutines however std::async is easier to use than std::thread and works with co_await as well
coroutines using co_await look to be a great addition with concurrency when using C++/WinRT and the Async type functions in WinRT (see C++ Coroutines: Understanding operator co_await for a technical explanation)
you can make your own async functions using the concurrency::task<> template as the return type of the function or use concurrency::create_task() and you can use co_await with such a task
you can also use co_await with std::async() since std::async() returns a std::future<> which has an Awaitable interface (see await/yield: C++ coroutines though it is dated Nov-2016)
you can also use co_await with a std::future<> as provided by the get_future() method of a std::packaged_task<> ( see also What is the difference between packaged_task and async )
you can make generator functions using std::experimental::generator<type> as a function return type along with the co_yield operator to return a value of the specified type in the generated series
to update the MFC UI requires that any code runs in the MFC UI thread the MFC object was created so Windows messages are needed to communicate with the MFC windows and window class objects from other threads or you must switch the thread context/affinity to the UI thread context for that object
winrt::apartment_context can be used to capture the current thread context and later resumed using co_await which may be used to capture the main UI thread context to be reused later (see Programming with thread affinity in mind in the article Concurrency and asynchronous operations with C++/WinRT)
co_await winrt::resume_background(); can be used to push the current thread's context to a background thread which can be useful for a lengthy task that may be in the main UI thread context and you want to make sure that it is not
when sending messages to a window, make sure that the window actually has been created and exists, during application startup the application must create windows before you can use them; just because the MFC class exists does not mean the underlying window has been created yet
::SendMessage() is synchronous in which the message is sent and a response is returned
::PostMessage() is asynchronous in which the message is sent and a response is not returned
using ::PostMessage() be careful that pointers sent in messages do not go out of scope before the receiver uses them as there is typically a delay between when ::PostMessage() returns and the message handle receiving the message actually does something with the message
probably the most straightforward approach is to use the ON_MESSAGE() macro in the message map with a message handler interface of afx_msg LRESULT OnMessageThing(WPARAM, LPARAM)
you can use Windows message identifiers in the space beginning with the defined constant of WM_APP and the same identifier can be used in different classes
you can use much of C++/WinRT quite easily with MFC with just a bit of care though admittedly I only have tried a few things and there are some limitations such as not using XAML according to the documentation
if you do use C++/WinRT within an MFC application, you are limiting your application to versions of Windows that has the Windows Runtime which pretty much means Windows 10 (this rules out using C++/WinRT with Windows 7, POS Ready 7, etc.)
using C++/WinRT requires adding compiler option /stdc++17 to enable the ISO C++17 Standard for the C++ Language Standard and using coroutines requires the /await compiler option
Here are a view resources.
Microsoft Build 2018
Effective C++/WinRT for UWP and Win32
May 06, 2018 at 3:27PM by Brent Rector, Kenny Kerr
CppCon 2017: Scott Jones & Kenny Kerr
C++/WinRT and the Future of C++ on Windows
Published on Nov 2, 2017
Using Visual Studio 2017 Community edition, I created a new MFC Single Document Interface (SDI) project using the Visual Studio style. After the application comes up, it looks like the following image.
Helper functions for messages
The first change I made was to provide a way to send a Windows message to one of the panes (ClassView or OutputWindow) that I would like to update. Since the CMainFrame class in MainFrm.h had the MFC objects for these windows as in:
protected: // control bar embedded members
CMFCMenuBar m_wndMenuBar;
CMFCToolBar m_wndToolBar;
CMFCStatusBar m_wndStatusBar;
CMFCToolBarImages m_UserImages;
CFileView m_wndFileView;
CClassView m_wndClassView;
COutputWnd m_wndOutput;
CPropertiesWnd m_wndProperties;
I modified the class to provide a way to send a message to these windows. I chose to use SendMessage() rather than PostMessage() to eliminate the pointer going out of scope issue. The concurrency class works fine with SendMessage().
LRESULT SendMessageToFileView(UINT msgId, WPARAM wParam, LPARAM lParam) { return m_wndFileView.SendMessage(msgId, wParam, lParam); }
LRESULT SendMessageToClassView(UINT msgId, WPARAM wParam, LPARAM lParam) { return m_wndClassView.SendMessage(msgId, wParam, lParam); }
LRESULT SendMessageToOutputWnd(UINT msgId, WPARAM wParam, LPARAM lParam) { return m_wndOutput.SendMessage(msgId, wParam, lParam); }
These are the raw, bare infrastructure for sending the messages to update the various MFC windows. I put these into CMainFrame class as that is a central point and the AfxGetMainWnd() function allows me to access the object of that class at any place within the MFC application. Additional classes to wrap these raw functions would be appropriate.
I then put message handlers into each of the classes within the BEGIN_MESSAGE_MAP and the END_MESSAGE_MAP macros. The output window update was the easiest and simplest and look like:
BEGIN_MESSAGE_MAP(COutputWnd, CDockablePane)
ON_WM_CREATE()
ON_WM_SIZE()
// ADD_ON: message handler for the WM_APP message containing an index as
// to which output window to write to along with a pointer to the
// text string to write.
// this->SendMessageToOutputWnd(WM_APP, COutputWnd::OutputBuild, (LPARAM)_T("some text"));
ON_MESSAGE(WM_APP, OnAddItemsToPane)
END_MESSAGE_MAP()
with the message handler looking like:
// ADD_ON: message handler for the WM_APP message containing an array of the
// struct ItemToInsert above. Uses method AddItemsToPane().
LRESULT COutputWnd::OnAddItemsToPane(WPARAM wParam, LPARAM lParam)
{
switch (wParam) {
case OutputBuild:
m_wndOutputBuild.AddString((TCHAR *)lParam);
break;
case OutputDebug:
m_wndOutputDebug.AddString((TCHAR *)lParam);
break;
case OutputFind:
m_wndOutputFind.AddString((TCHAR *)lParam);
break;
}
return 0;
}
I added the method prototype to the class along with this enumeration to make the functionality a bit easier to use.
enum WindowList { OutputBuild = 1, OutputDebug = 2, OutputFind = 3 };
With the above changes I was able to insert into the message handler for "New" in BOOL CMFCAppWinRTDoc::OnNewDocument() the following code to put a text string into the "Build" Output Window:
CMainFrame *p = dynamic_cast <CMainFrame *> (AfxGetMainWnd());
if (p) {
p->SendMessageToOutputWnd(WM_APP, COutputWnd::OutputBuild, (LPARAM)_T("this is a test from OnNewDocument()."));
}
Using C++/WinRT with MFC and concurrency
To test this along with testing using C++/WinRT with MFC, I added the following concurrency task to CMainFrame::OnCreate() which is called when the application is starting up. This source spins off a task which then uses the Syndication functionality of C++/WinRT to fetch an RSS feed list and displays the titles in the OutputWindow pane labeled "Build" as seen in the screen shot above.
concurrency::create_task([this]() {
winrt::init_apartment();
Sleep(5000);
winrt::Windows::Foundation::Uri uri(L"http://kennykerr.ca/feed");
winrt::Windows::Web::Syndication::SyndicationClient client;
winrt::Windows::Web::Syndication::SyndicationFeed feed = client.RetrieveFeedAsync(uri).get();
for (winrt::Windows::Web::Syndication::SyndicationItem item : feed.Items())
{
winrt::hstring title = item.Title().Text();
this->SendMessageToOutputWnd(WM_APP, COutputWnd::OutputBuild, (LPARAM)title.c_str()); // print a string to an output window in the output pane.
}
winrt::uninit_apartment();
});
To use the concurrency and the C++/WinRT functionality I had to add a couple of include files near the top of MainFrm.c source file.
// ADD_ON: include files for using the concurrency namespace.
#include <experimental\resumable>
#include <pplawait.h>
#pragma comment(lib, "windowsapp")
#include "winrt/Windows.Foundation.h"
#include "winrt/Windows.Web.Syndication.h"
In addition, I had to modify the Properties of the solution to specify C++17 and an additional compiler option of /await which are marked with blue arrows in the screen shot below.
Using co_await with MFC and C++/WinRT
From a helpful comment from #IInspectable I took a look at coroutines with Visual Studio 2017 and MFC. I have been curious about them however it seemed that I could not come up with anything that would compile without errors from using co_await.
However starting with a link in the comment from #IInspectable I found a link to this YouTube video, CppCon 2016: Kenny Kerr & James McNellis “Putting Coroutines to Work with the Windows Runtime", which had a source code sample around time 10:28 that finally I was able to come up with something that would compile and work.
I created the following function which I then used to replace the above source with concurrency::create_task() and the lambda with a function call to the following function. The function call was simple, myTaskMain(this); replacing the concurrency::create_task([this]() { lambda in the int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) method and then adding the following source code above the OnCreate() function body.
winrt::Windows::Foundation::IAsyncAction myTaskMain(CMainFrame *p)
{
winrt::Windows::Foundation::Uri uri(L"http://kennykerr.ca/feed");
winrt::Windows::Web::Syndication::SyndicationClient client;
winrt::Windows::Web::Syndication::SyndicationFeed feed = co_await client.RetrieveFeedAsync(uri);
Sleep(5000);
for (winrt::Windows::Web::Syndication::SyndicationItem item : feed.Items())
{
winrt::hstring title = item.Title().Text();
p->SendMessageToOutputWnd(WM_APP, COutputWnd::OutputBuild, (LPARAM)title.c_str()); // print a string to an output window in the output pane.
}
}
There are two changes that I made from the source of the concurrency::create_task() being replaced:
removed the winrt::init_apartment(); and winrt::uninit_apartment(); since using them triggered an exception and removing them seems to make no difference
moved the Sleep(5000); to after the co_await since leaving it where it was caused the function to sleep for 5 seconds which meant the UI thread slept for 5 seconds
What I found with the debugger was that at the point that the function myTaskMain() was called, there was an immediate return from the function and the UI thread continued running while the coroutine executed in the background. The UI displayed promptly and then some five seconds later the additional actions, updating the Class View tree and the RSS feed list in the "Build" tab of the Output Window happened.
Note #1: One other thing I have run into with other testing is that the UI will freeze for a few seconds (menu is non-functional). This seems to be due to the Sleep(5000); indicating that the code after the co_await is being run on the main UI thread. This change in application behavior started after I began to explore using winrt::apartment_context ui_thread; to capture the main UI thread context in order to then use co_await ui_thread; to return my coroutine thread to the main UI thread context.
What may be happening is that client.RetrieveFeedAsync(uri) is immediately being satisfied with no delay, perhaps from a cache, so rather than pushing the task to another thread and then returning to the caller, the co_await is getting an immediate result back and the function myTaskMain() is able to immediately continue using the current thread which is the main UI thread?
I noticed that in Visual Studio 2017 the co_await used with client.RetrieveFeedAsync(uri) is colored green while the co_await used with co_await ui_thread; is blue. Doing a mouse hover over the green colored co_await I get a tool tip indicating this is a different version of co_await.
Note #2: There is a C++/WinRT function to move to a background thread context, winrt::resume_background() which can be used with co_await. If I modify the above function myTaskMain() by replacing the line of code of Sleep(5000); after the call to client.RetrieveFeedAsync(uri) with the following two lines of code to push the thread context to a background thread, I do not see the freeze (the UI is responsive to menu selections) and the RSS feed text lines are displayed in the "Build" tab of the Output Window after some 15 seconds.
co_await winrt::resume_background(); // switch context to background thread
Sleep(15000);
Rolling an async task using concurrency::task<> that works with co_await
One thing I was curious about was being able to create my own async task that I could used with co_await similar to the async type functions of C++/WinRT. I spent some time searching about until I finally found this article, Concurrency and asynchronous operations with C++/WinRT, with a section called Asychronously return a non-Windows-Runtime type.
Here is a simple demo function that creates a concurrency::task<> with a lambda and returns the task which is then used with co_await. This particular lambda is returning an int so the function is defined as a task that returns an int, concurrency::task<int>
concurrency::task<int> mySleepTaskAsync()
{
return concurrency::create_task([]() {
Sleep(15000);
return 5;
});
}
The above function is then used with the co_await operator in a statement such as:
int jj = co_await mySleepTaskAsync();
which will cause the variable jj to have a value of 5 after a wait of 15 seconds.
The above is used in a function that returns a winrt::Windows::Foundation::IAsyncAction such as the function myTaskMain() above.
If you like you can also just directly use the lambda with the co_await as in:
int jj = co_await concurrency::create_task([]() {
Sleep(15000);
return 5;
});
Or you could have a normal function such as:
int mySleepTaskAsyncInt()
{
Sleep(15000);
return 5;
}
and then use it with a co_await using concurrency::task<> as in:
int jj = co_await concurrency::create_task(mySleepTaskAsyncInt);
Rolling an async task using std::async that works with co_await
While std::thread does not work with co_await, causing a compilation error, you can use std::async with co_await. The reason is the kind of return value that co_await operator requires and the difference in the return value of std::thread, a std::thread, and the return value of std::async, a std::future<>.
The co_await operator requires that the variable it is operating on is a std::future<>, has a get() method to retrieve a result from the thread, and is Awaitable.
#include <future>
int mySleepTaskAsyncInt()
{
Sleep(7000);
return 5;
}
winrt::Windows::Foundation::IAsyncAction myTaskMain(CMainFrame *p)
{
auto t1 = co_await std::async (std::launch::async, mySleepTaskAsyncInt);
// do something with the variable t1
}
Rolling an async task with std::packaged_task<> and std::future<> with co_await
As the co_await requires an Awaitable object, another way to create such an object is to create a task with std::packaged_task<> then start the task and use the get_future() method of the task to obtain a std::future<> which is then usable with co_await.
For instance we can have the following simple function that will create a task package, start the task executing, and then return a std::future<>. We can then use this function as the target for the co_await operator to implement a coroutine.
#include <future>
std::future<int> mySleepTaskStdFutureInt()
{
// create the task to prepare it for running.
std::packaged_task<int()> task([]() {
Sleep(7000);
return 455; // return an int value
});
// start the task running and return the future
return task(), task.get_future();
}
and then in our source code we would use this function similar to:
int jkjk = co_await mySleepTaskStdFutureInt();
The return statement is using the comma operator to introduce a sequence point so that we start the task running and then call the get_future() method on the running task. The result of the get_future() method, a std::future<int> is what is actually returned by the function.
The task that is created with std::packaged_task() must be started with a function like call using the variable. If you do not start the task then the std::future<> returned by the function will never have a variable and the co_await that is waiting for the Awaitable to complete and to provide a value will never fire. The result is that the source after your co_await will not be executed because the co_await will never be triggered.
Generator with co_yield and std::experimental::generator<type>
While investigating co_await I came across co_yield which is used to return a value as part of a generator of a set of values. With Visual Studio 2017 using co_yield requires that the header file experimental/generator be included. Here is a simple example of a generator that generates a series of integers.
#include <experimental/generator>
std::experimental::generator<int> makeSomeInts(int kCount)
{
for (int i = 0; i < kCount; i++) {
co_yield i;
}
}
And this function can be used with a ranged for as in:
for (int kkk : makeSomeInts(10)) {
// code that uses the variable kkk which contains
// an int from the generated range 0 up to be not including 10.
}
The above loop will be executed for each integer value of 0 through and including 9.
More complex message: updating ClassView pan
I also did an experiment with the ClassView tree control to provide a simple way of doing the most elementary actions: create an initial tree control and add to it.
In CClassView class in the file ClassView.h, I added the following data structs. By the way after I was done, I realized this was probably the wrong place to put this since the CFileView class uses the same tree structure so the same approach would work for both of these panes. Anyway, I added the following:
// ADD_ON: enumeration listing the various types of tree control icons which
// correspond to the position of a control in the tree.
// choose either classview_hc.bmp or classview.bmp for the bitmap strip that
// contains the 7 icons we are using for the images in our tree control.
// icons are standard size in height and width (15x15 pixels) in the order of:
// - main root icon
// - tree node icon which can be opened to show nodes beneath it
// - folder icon which is used to indicate a folder
// - method icon indicating a method of a class
// - locked method icon
// - member variable icon
// - locked member variable icon
enum IconList { MainRoot = 0, TreeNode = 1, FolderNode = 2, MethodNode = 3, MethodLockedNode = 4, MemberNode = 5, MemberLockedNode = 6 };
// ADD_ON: struct used to contain the necessary data for a node in the tree control.
struct ItemToInsert {
std::wstring label; // text to be displayed with the node.
int nImage; // zero based offset of the node's icon in the image, one of enum IconList above.
int nSelectedImage; // zero based offset of the node's icon in the image, one of enum IconList above.
};
I created a message handler, and added it to the message map in ClassView.cpp
ON_MESSAGE(WM_APP, OnAddItemsToPane)
and added the actual message handler itself along with a helper function that does the actual processing.
// ADD_ON: function for filling in the ClassView pane using an array of the
// struct ItemToInsert above. array is terminated by an entry with
// all zeros as in { _T(""), 0, 0 }
void CClassView::AddItemsToPane(CViewTree &xwndClassView, void *xrayp)
{
if (xrayp == 0) return;
// the images are icons that are laid out in a line of icons within a single bitmap image.
// see class method OnChangeVisualStyle() for when the bitmap image is loaded and then
// divided up into sections, 0 through 6, of the single bitmap image loaded.
// see classview.bmp and classview_hc.bmp in the ResourceFiles list.
HTREEITEM hRoot = xwndClassView.GetRootItem();
HTREEITEM hClass = 0;
ItemToInsert *xray = (ItemToInsert *)xrayp;
for (int i = 0; xray[i].label.size() != 0; i++) {
switch (xray[i].nImage) {
case MainRoot:
hRoot = xwndClassView.InsertItem(xray[i].label.c_str(), xray[i].nImage, xray[i].nSelectedImage);
xwndClassView.SetItemState(hRoot, TVIS_BOLD, TVIS_BOLD);
xwndClassView.Expand(hRoot, TVE_EXPAND);
break;
case TreeNode:
hClass = xwndClassView.InsertItem(xray[i].label.c_str(), xray[i].nImage, xray[i].nSelectedImage, hRoot);
break;
case FolderNode:
hClass = xwndClassView.InsertItem(xray[i].label.c_str(), xray[i].nImage, xray[i].nSelectedImage, hRoot);
break;
case MethodNode:
case MethodLockedNode:
case MemberNode:
case MemberLockedNode:
xwndClassView.InsertItem(xray[i].label.c_str(), xray[i].nImage, xray[i].nSelectedImage, hClass);
break;
default:
break;
}
}
}
// ADD_ON: message handler for the WM_APP message containing an array of the
// struct ItemToInsert above. Uses method AddItemsToPane().
LRESULT CClassView::OnAddItemsToPane(WPARAM wParam, LPARAM lParam)
{
switch (wParam) {
case 1:
AddItemsToPane(m_wndClassView, (void *)lParam);
break;
}
return 0;
}
I then created some sample data for an initial tree and then an add on node.
// ADD_ON: this is the content to be put into the ClassView tree pane.
// this is a tree structure.
CClassView::ItemToInsert xray2[] = {
{ _T("CFakeMainProject"), CClassView::MainRoot, CClassView::MainRoot },
{ _T("CFakeAboutDlg"), CClassView::TreeNode, CClassView::TreeNode },
{ _T("CFakeAboutDlg()"), CClassView::MethodNode, CClassView::MethodNode },
{ _T("CFakeApp"), CClassView::TreeNode, CClassView::TreeNode },
{ _T("CFakeApp()"), CClassView::MethodNode, CClassView::MethodNode },
{ _T("InitInstance()"), CClassView::MethodNode, CClassView::MethodNode },
{ _T("OnAppAbout()"), CClassView::MethodNode, CClassView::MethodNode },
{ _T("CFakeAppDoc"), CClassView::TreeNode, CClassView::TreeNode },
{ _T("CFakeAppDoc()"), CClassView::MethodLockedNode, CClassView::MethodLockedNode },
{ _T("~CFakeAppDoc()"), CClassView::MethodNode, CClassView::MethodNode },
{ _T("OnNewDocument()"), CClassView::MethodNode, CClassView::MethodNode },
{ _T("CFakeAppView"), CClassView::TreeNode, CClassView::TreeNode },
{ _T("CFakeAppView()"), CClassView::MethodLockedNode, CClassView::MethodLockedNode },
{ _T("~CFakeAppView()"), CClassView::MethodNode, CClassView::MethodNode },
{ _T("GetDocument()"), CClassView::MethodNode, CClassView::MethodNode },
{ _T("CFakeAppFrame"), CClassView::TreeNode, CClassView::TreeNode },
{ _T("CFakeAppFrame()"), CClassView::MethodNode, CClassView::MethodNode },
{ _T("~CFakeAppFrame()"), CClassView::MethodNode, CClassView::MethodNode },
{ _T("m_wndMenuBar"), CClassView::MemberLockedNode, CClassView::MemberLockedNode },
{ _T("m_wndToolBar"), CClassView::MemberLockedNode, CClassView::MemberLockedNode },
{ _T("m_wndStatusBar"), CClassView::MemberLockedNode, CClassView::MemberLockedNode },
{ _T("Globals"), CClassView::FolderNode, CClassView::FolderNode },
{ _T("theFakeApp"), CClassView::MemberNode, CClassView::MemberNode },
{ _T(""), 0, 0 }
};
CClassView::ItemToInsert xray3[] = {
{ _T("CAdditionalDelay"), CClassView::TreeNode, CClassView::TreeNode },
{ _T("CAdditionalDelayMethod()"), CClassView::MethodNode, CClassView::MethodNode },
{ _T(""), 0, 0 }
};
I then exercises this message handler by spinning off two concurrency tasks in the CMainFrame::OnCreate() method which did a time delay and then updated the ClassView window tree contents.
concurrency::create_task([this]() { Sleep(5000); this->SendMessageToClassView(WM_APP, 1, (LPARAM)xray2); });
concurrency::create_task([this]() { Sleep(10000); this->SendMessageToClassView(WM_APP, 1, (LPARAM)xray3); });
Related
How can I update progress bar without blocking UI? [duplicate]
This question already has answers here: How do I update the GUI from another thread? (47 answers) Closed 1 year ago. Currently I am creating a background STA thread to keep the UI responsive but it slows down my function calls on the main thread. Based on this thread How to update progress bar while working in the UI thread I tried the following but the UI only gets updated after all of the work has finished. I tried playing around with the Dispatcher priorities but none of them seem to work. What I also tried is adding _frmPrg.Refresh() to my Progress callback but this does not seem to change anything. Dim oProgress = New Progress(Of PrgObject)(Sub(runNumber) _frmPrg.Invoke((Sub() _frmPrg.Status = runNumber End Sub)) End Sub) System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(Sub() DoLongRunningWork(oProgress, _cancellationToken) End Sub, System.Windows.Threading.DispatcherPriority.Background)
I can't really help you with your problem, but I'll try to clarify what happens in your posted code. DoLongRunningWork will be invoked through Dispatcher on the UI thread, when the UI thread is not busy. But once started, it will block the UI thread until it completes. So you can't show a progress this way. Your single chance is, to let DoLongRunningWork run on a background thread. That brings you nothing, if the long-running methods come from office objects, which must be accessed from the UI thread... The Progress class (see the remarks section) invokes your event handler on the UI thread automatically, so you don't need _frmPrg.Invoke in your event handler. Maybe you can start a STAthread for your progress form and show it from there. The instance of your Progress class must be created in this thread too, but not before your form is shown to ensure, that the thread becomes a WindowsFormsSynchronisationContext (or you set one explicitly after starting the thread). A plain SynchronisationContext won't work! At least you get updates in your form this way, but the UI thread of the office app will still be blocked. And of course, any action you make with your progress form must be invoked on the UI thread, if accessing office objects.
After reading some other posts, I decided to suggest another solution. My previous answer still contains usable information, so I'll leave it there. I'm not familiar with VB.NET syntax, so the samples are in C#. I have tested the code in a VSTO plugin for PowerPoint, but it should run in any office application. Forget the Progress class and background threads. Run everything on the UI thread! Now use some async code. To stay on the UI thread, we need a "good" SynchronizationContext. private static void EnsureWinFormsSyncContext() { // Ensure that we have a "good" SynchronisationContext // See https://stackoverflow.com/a/32866156/10318835 if (SynchronizationContext.Current is not WindowsFormsSynchronizationContext) SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext()); } This is the event handler of a button. Note the manually added async keyword. The SynchronizationContext.Current gets resetted again and again, so ensure the good one in the EventHandler: private async void OnButtonClick(object sender, EventArgs e) { EnsureWinFormsSyncContext(); // Return from event handler, ensure that we are really async // See https://stackoverflow.com/a/22645114/10318835 await Task.Yield(); await RunLongOnUIThread(); } This will be the worker method, also running on the UI thread. private async Task RunLongOnUIThread() { //Dummy code, replace it with your code var pres = addIn.Application.Presentations.Add(); for (int i = 0; i < 100; i++) { Debug.Print("Creating slide {0} on thread {1}", i, Thread.CurrentThread.ManagedThreadId); // If you have some workloads that can be run on a background // thread, execute them with await Task.Run(...). try { var layout = pres.Designs[1].SlideMaster.CustomLayouts[1]; var slide = pres.Slides.AddSlide(i + 1, layout); var shape = slide.Shapes.AddLabel(Microsoft.Office.Core.MsoTextOrientation.msoTextOrientationHorizontal, 0, 15 * i, 100, 15); shape.TextFrame.TextRange.Text = $"Text on slide {i + 1}"; } catch (Exception ex) { Debug.Print("I don't know what am I doing here, I'm not familiar with PowerPoint... {0}", ex); } // Update UI statusLabel.Text = $"Slide {i + 1} done"; progressBar1.Value = i + 1; // This is the magic! It gives the main thread the opportunity to update the UI. // It also processes input messages so you need to disable unwanted buttons etc. await IdleYield(); } } The following method is for Windows Forms Applications where it does the job perfect. I've tried it also in PowerPoint. If you are facing problems, try the WPF flavour with await Dispatcher.Yield(DispatcherPriority.ApplicationIdle) instead of await IdleYield(). private static Task IdleYield() { var idleTcs = new TaskCompletionSource<bool>(); void handler(object s, EventArgs e) { Application.Idle -= handler; idleTcs.SetResult(true); } Application.Idle += handler; return idleTcs.Task; } Here are the (clickable) links to the answers that I used (I can't put them in the code-blocks...). Incorrect async/await working, Excel events in Excel Application Level Add-in When would I use Task.Yield()? Task.Yield - real usages? If in your real code something runs not as expected, check the thread you are running on and SynchronizationContext.Current.
Executing GTK functions from other threads
This question is about GTK and threads. You may find it useful if your application crashes, freezes or you want to have a multithreaded GTK application.
Main Loop In order to understand GTK you must understand 2 concepts. All contemporary GUIs are single-threaded. They have a thread which processes events from window system (like button, mouse events). Such a thread is called main event loop or main loop. GTK is also single threaded and not MT-safe. This means, that you must not call any GTK functions from other threads, as it will lead to undefined behaviour. As Gtk documentation states, Like all GUI toolkits, GTK+ uses an event-driven programming model. When the user is doing nothing, GTK+ sits in the “main loop” and waits for input. If the user performs some action - say, a mouse click - then the main loop “wakes up” and delivers an event to GTK+. GTK+ forwards the event to one or more widgets. Gtk is event-based and asynchronous. It reacts to button clicks not in the exact moment of clicking, but a bit later. It can be very roughly written like this (don't try this at home): static list *pollable; int main_loop (void) { while (run) { lock_mutex() event_list = poll (pollable); // check whether there are some events to react to unlock_mutex() dispatch (event_list); // react to events. } } void schedule (gpointer function) { lock_mutex() add_to_list (pollable, something); unlock_mutex() } I want a delayed action in my app For example, hide a tooltip in several seconds or change button text. Assuming your application is single-threaded, if you call sleep() it will be executed in main loop. sleep() means, that this particular thread will be suspended for specified amount of seconds. No work will be done. And if this thread is main thread, GTK will not be able to redraw or react to user interactions. The application freezes. What you should do is schedule function call. It can be done with g_timeout_add or g_idle_add In the first case our poll() from snippet above will return this event in several seconds. In the latter case it will be returned when there are no events of higher priority. static int count; gboolean change_label (gpointer data) { GtkButton *button = data; gchar *text = g_strdup_printf ("%i seconds left", --count); if (count == 0) return G_SOURCE_REMOVE; return G_SOURCE_CONTINUE; } void button_clicked (GtkButton *button) { gtk_button_set_label (button, "clicked"); count = 5; g_timeout_add (1 * G_TIME_SPAN_SECOND, change_label, button); } Returning a value from function is very important. If you don't do it, the behaviour is undefined, your task may be called again or removed. I have a long-running task Long-running tasks aren't different from calling sleep. While one thread is busy with that task, it can't perform any other tasks, obviously. If that is a GUI thread, it can't redraw interface. That's why you should move all long-running tasks to other threads. There is an exception, though: non-blocking IO, but it's out of topic of my answer. I have additional threads and my app crashes As already mentioned, GTK is not MT-safe. You must not call Gtk functions from other threads. You must schedule execution. g_timeout_add and g_idle_add are MT-safe, unlike other GTK functions. That callbacks will be executed in main loop. If you have some shared resources between callback and thread you must read/write them atomically or use a mutex. static int data; static GMutex mutex; gboolean change_label (gpointer data) { GtkButton *button = data; int value; gchar *text; // retrieve data g_mutex_lock (&mutex); value = data; g_mutex_unlock (&mutex); // update widget text = g_strdup_printf ("Current data value: %i", value); return G_SOURCE_REMOVE; } gpointer thread_func (gpointer data) { GtkButton *button = data; while (TRUE) { sleep (rand_time); g_mutex_lock (&mutex); ++data; g_mutex_unlock (&mutex); g_idle_add (change_label, button); } } Make sure mutexes are held as little as possible. Imagine you lock a mutex in another thread and do some IO. The main loop will be stuck until the mutex is released. There is g_mutex_try_lock() that returns immidiately, but it can bring additional syncronization problems because you can't guarantee that the mutex will be unlocked when mainloop tries to lock it. Follow up: but python is single-threaded and GIL et cetera? You can imagine that python is multi-threaded application run on a single-core machine. You never know when the threads will be switched. You call a GTK function but you don't know in which state the main loop is. Maybe it free'd resources just a moment before. Always schedule. What is not discussed and further reading Detailed documentation on glib main loop can be found here GSource as a more low-level primitive. GTask
Without AsyncTask, running a thread in background and updating the UI Thread
I was trying to update the recycler view content from a background thread in Kotlin. I am not using AsyncTask. Here is my code, i want to know if there is any better way than this: In my MainActivity, i have progressThread as a member variable. var progressThread = Thread() Then in my method where i want to run the thread first i am defining it...like progressThread = Thread ( Runnable { kotlin.run { try { while (i <= 100 && !progressThread.isInterrupted) { Thread.sleep(200) //Some Logic runOnUiThread { //this runs in ui thread } i++ } }catch (e:InterruptedException){ progressThread.interrupt() } } }) after that i am starting it in the same method as progressThread.start() and for stopping it, i have a listener to cancel the progress and in the callback of that listener, i have written: progressThread.interrupt()
Updated Coroutines are stable now,: https://kotlinlang.org/docs/reference/coroutines-overview.html Old Answer Yes, you can do this using doAsync from kotlin anko library that is fairly simple and easy to use. add following line in module level gradle file: compile "org.jetbrains.anko:anko-commons:0.10.0" Code example: val future = doAsync { // do your background thread task result = someTask() uiThread { // use result here if you want to update ui updateUI(result) } } code block written in uiThread will only be executed if your Activity or Fragment is in foreground mode (It is lifecycle aware). So if you are trying to stop thread because you don't want your ui code to execute when Activity is in background, then this is an ideal case for you. As you can check doAsync returns a Future object so you can cancel the background task, by cancel() function: future.cancel(true) pass true if you want to stop the thread even when it has started executing. If you have more specialised case to handle stopping case then you can do the same thing as in your example. You can use Kotlin Coroutines also but its in Experimental phase, still you can try it out: https://kotlinlang.org/docs/reference/coroutines.html
SHGetFileInfo doesn't return correct handle to icon
I'm using SHGetFileInfo function for getting icons for folders and different file types. According to MSDN call of this function should be done from background thread and before call Component Object Model (COM) must be initialized with CoInitialize or OleInitialize. My code looks like this: public void SetHlinkImage(string path) { Shell32.OleInitialize(IntPtr.Zero); Task task = Task.Factory.StartNew(() => { LoadIcons(path); }); } private void LoadIcons(string path) { image = GetHlinkImage(path); if (OwnerControl.InvokeRequired) layout.ModuleControl.BeginInvoke((MethodInvoker)delegate () { Shell32.OleUninitialize(); }); } public Icon GetHlinkImage(string path) { uint flags = Shell32.SHGFI_ICON | Shell32.SHGFI_ATTRIBUTES | Shell32.SHGFI_SMALLICON; Shell32.SHFILEINFO shfi = new Shell32.SHFILEINFO(); IntPtr result = Shell32.SHGetFileInfo(path, Shell32.FILE_ATTRIBUTE_DIRECTORY, ref shfi, (uint)Marshal.SizeOf(shfi), flags); Icon icon = (Icon)Icon.FromHandle(shfi.hIcon).Clone(); WinApi.DestroyIcon(shfi.hIcon); // cleanup return icon; } Mostly the problem appears after first call of the code and as result I get an exception when I tried to create Icon from icon handle: System.ArgumentException: Win32 handle that was passed to Icon is not valid or is the wrong type And further calls of the code work without problems. Actually behaviour also somehow depends on the test system. It is hardly possible to reproduce this issue on Windows10 systems but on Windows 7 it happens quite often. Has anyone experienced this problem?
From comment of Hans Passant: Calling OleInitialize() is pointless, the CLR already initializes COM before it starts a thread. And it failed, something you cannot see because you are not checking its return value. Not knowing that, it just spirals into undiagnosable misery from there. Yes, more of it on Win7. You must provide an STA thread, if this needs to run in the background then consider this solution.
Automation implementation using Busy Waiting or Threads in c++
I am trying to automate the handler equipment(a robot picks a chip and put it onto a hardware platform) with the following requirement: 1.There are 6 sites for the handler , once handler puts a device onto that site, handler will return an errorcode: code1 for ready to test, code2 for error, and if in process no code have returned. 2.There is a master PC that controls the handler operation, and the communication b/w master and site PCs are using Staf 3.I need to use that code to run some tests(which already implemented and working properly). Handler puts the device in a FIFO order, first site returns code first, and last site returns code last. 4.The Site PC is acting passively, which master PC will determine when to run and how to run the tests. Site PC will only know if handler is ready then execute the tests. So my question would be: In this case, for the site-PCs(Windows based with perl and .net enabled), is busy waiting method better or is the wait condition mechanism suits better: For example: the sample code would be: void runTestonSite() { for(;;) { if(returnCode == code1) { testStart(arg1,arg2,arg3); } } } or is there any better way to do this kind of task? #include <boost/thread.hpp> void getReturnCode() { // do stuff } void RunTestOnSite() { // do stuff } int main (int argc, char ** argv) { using namespace boost; thread thread_1 = thread(getReturnCode); thread thread_2 = thread(RunTestOnSite); // do other stuff thread_2.join(); thread_1.join(); return 0; } Please advise, thanks