TestExecWindow
Do not forget to deal with multi threading

Overview:

The problem - unknown threads may call

Within TestExecWindow many interactions start with pushing a button at the GUI. Those actions are executed within the safe main GUI thread (dispatcher thread).

But we also react on asynhronous events which will arrive on some notification thread:

Critical situations may occur when data are accessed both from GUI thread and some notification thread. Unsynchronized changes may lead to corrupt data and program crash. When concerning GUI elements (buttons, list boxes etc.) access is only allowed from GUI thread. Visual Studio will rise an exception when access is tried from some other thread.

A simple solution - always synchronize with main thread

Assume we want to write a message to the log pane by calling the service function MyToolWindow.WriteLine() from any other source file. We cannot be sure which thread will call this function. The service function calls method AddInfoToEventList() within GUI class MyToolWindowControl.xaml.cs where we can pass the call to the main thread:

public void AddInfoToEventList(string info)
{
// Redirect to main GUI thread
Microsoft.VisualStudio.Shell.ThreadHelper.JoinableTaskFactory.Run(async delegate {
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
AddInfoToEventListFromGuiThread(info);
});
}
private void AddInfoToEventListFromGuiThread(string info)
{
// here we are sure to be within GUI thread and we can
// safely access the list box
lstEvents.Items.Add(info);
/// ...

An analogous problem occurs when we receive the message about the end of the test process within main class MyToolWindow. In this case we also use the main thread for synchronization:

public void OnTestTerminated(bool in_success, string in_args, bool in_memLeaksDetected, TimeSpan in_executionTime)
{
// This function is called when the test app terminates.
// The call may arrive on any system thread and needs to be synchronized with the GUI thread.
Microsoft.VisualStudio.Shell.ThreadHelper.JoinableTaskFactory.Run(async delegate
{
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
Gui().TestTerminatedWithinGuiThread(in_success, "", in_args, in_memLeaksDetected, in_executionTime);
});
}