How to safely show messagebox in multi-thread application? - c#

I have something doing background and I want to show a messagebox if something wrong happens.
First I tried
var _timer = new System.Threading.Timer((o) =>
{
if(!DoCheck()){
Messagebox.Show("The message");
}
});
Nothing wrong happens.
And I have another job to be done in background, and it's invoked by button click, like
private void button3_Click(object sender, EventArgs e)
{
var task = new Task(() =>
{
DoWork();
Messagebox.Show("Done");
});
_task.Start();
}
A System.Reflection.TargetInvocationException is thrown when the MessageBox is shown.
I have also tried this.Invoke, it raised an exception, too.
My question is:
Is the first case safe?
How to make the second case work?

No. You should preferably be using System.Windows.Forms.Timer in a WinForms application. The documentation specifically calls this out:
This Windows timer is designed for a single-threaded environment where UI threads are used to perform processing. It requires that the user code have a UI message pump available and always operate from the same thread, or marshal the call onto another thread.
Furthermore, it depends on what your DoCheck method is doing. We will need to see the code of that method.
Use the BeginInvoke method:
var form = this;
var task = new Task(() =>
{
DoWork();
form.BeginInvoke(() =>
{
MessageBox.Show("Done");
});
});

Related

Terminate Window on another thread

I have a simple C# winform app where I spawn a new thread to show another winform. After a process is completed i want to close that form using the below code. The issue I have is that when I call busyForm.BeginInvoke it is bypassing the null check and throw and error. How to correctly close the winform in another thread?
static Indicator busyForm;
public static async Task Execute()
{
Thread busyIndicatorthread = new Thread(new ThreadStart(()=>FormThread()));
busyIndicatorthread.SetApartmentState(ApartmentState.STA);
busyIndicatorthread.Start();
}
private static void FormThread()
{
busyForm = new Indicator();
busyForm.Closed += (sender2, e2) => busyForm.Dispatcher.InvokeShutdown();
Dispatcher.Run();
}
public static Task Execute(){
Thread busyIndicatorthread = new Thread(new ThreadStart(()=>FormThread(hwind)));
busyIndicatorthread.SetApartmentState(ApartmentState.STA);
busyIndicatorthread.Start();
// dos some stuff
if (busyForm != null)
{
busyForm.BeginInvoke(new System.Action(() => busyForm.Close())); <--- throw null error
busyForm = null;
}
}
That is because before calling .Close() method, time has passed and it is not assured that busyForm exists anymore.
In fact, it is possible that, while the new System.Action(() => busyForm.Close() thread is starting, you main thread goes to busyForm = null;.
You can try moving the null to secondary thread.
if (busyForm != null)
{
busyForm.BeginInvoke(new System.Action(() =>
{
lock(busyForm){
busyForm.Close();
busyForm = null;
}
}));
}
Almost no application starts another message pump to display notifications. It's not needed. In all applications, the busy and progress dialog boxes are generated and displayed.by the UI thread. Operations that could block are performed in the background, eg in a background thread or far better, using async/await and Task.Run. The UI is updated using events or callbacks, eg using the Progress< T> class.
In this case though, it seems all that's needed is to display a form before a long-running task and hide it afterward:
public async void btnDoStuff_Async(object sender, EventArgs args)
{
//Disable controls, display indicator, etc
btnDoStuff.Enabled=false;
using var busyForm = new Indicator();
busyForm.Show();
try
{
var result=await Task.Run(()=> ActuallyDoStuffAndReturnResult());
//Back in the UI form
//Do something with the result
}
finally
{
//Close the busy indicator, re-enable buttons etc.
busyForm.Close();
btnDoStuff.Enabled=true;
}
}
The finally block ensures the UI is enabled and the busy form hidden even in case of error.
20+ years ago some Visual Basic 6 applications did start another Window message pump to act as a "server". Visual Basic 6 threading was very quirky, so people used various tricks to get around its limitations.
When you write this code:
busyForm.BeginInvoke(new System.Action(() => busyForm.Close())); <--- throw null error
busyForm = null;
The order in which it executes is almost certainly this:
busyForm = null;
busyForm.Close();
No wonder you're getting a null reference exception!
Simply set the form to null in your invoke. That'll fix it.
However, the correct way to do this is as Panagiotis Kanavos suggests.

WPF, STA thread exception

I'm working on a WPF (MVVM) app.
Using 2 buttons, one to load data from db and another one to delete a selected item.
When clicking the Load button, a LoadCommand is fired and calls the StartLoadingThread
private void StartLoadingThread()
{
ShowLoadProcessing(); // show some text on top of screen ("Loading in progress...")
ThreadStart ts = delegate
{
LoadMyitems();
System.Windows.Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (EventHandler)
delegate
{
HideLoadProcessing(); // hide the text "Loading in progress..."
}, null, null);
};
ts.BeginInvoke(ts.EndInvoke, null);
}
Works fine, now when I select an item and click the Delete button, the DeleteItemCommand is fired and calls the StartDeletingThread
private void StartDeletingThread()
{
ShowDeleteProcessing(); // Show on top of screen "Deleting in progress..."
ThreadStart ts = delegate
{
DeleteSelectedItem();
System.Windows.Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (EventHandler)
delegate
{
HideDeletingProcessing();
}, null, null);
};
ts.BeginInvoke(ts.EndInvoke, null);
}
When StartDeletingThread is started, I'm getting the following exception:
{"The calling thread must be STA, because many UI components require this."}
I think you want something like this, though I am a bit newbie in WPF, you will have to replace this.BeginInvoke with something what is doing same in WPF (Dispatcher). Also code may not compile (add Action type conversion for Thread?), but idea is to simply start thread (you invoke it, for some reasons, why?) and in that thread invoke UI operation after deleting.
private void StartDeletingThread()
{
ShowDeleteProcessing(); // Show on top of screen "Deleting in progress..."
new Thread(() =>
{
DeleteSelectedItem();
this.BeginInvoke(() => HideDeletingProcessing());
}.Start();
}
Try invoking StartDeletingThread using the application dispatcher like this:
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,new
Action(()=>StartDeletingThread())
private void StartDeletingThread()
{
ShowDeleteProcessing(); // Show on top of screen "Deleting in progress..."
Task.Run(() =>
{
DeleteSelectedItem();
Application.Current.Dispatcher.BeginInvoke((Action)HideDeletingProcessing);
});
}
probably will blow up at DeleteSelectedItem();. I know we all love to show progress bars for actions that take less than a second, but why bother when it leads to questions on StackOverflow?
private void StartDeletingThread()
{
DeleteSelectedItem();
}
Done and done. I seriously doubt it takes a long time to delete the selected item.
If, in some rare case it does... then you need to find out in DeleteSelectedItem where the UI is getting touched, and use the application's dispatcher to do the touching.
(Side note, here's how you'd safely multithread in 4.5, using async/await... safe, as long as you understand the repercussions of async void, that is)
private async void StartDeletingThread()
{
// we're in the UI thread
ShowDeleteProcessing();
await DeleteSelectedItem();
// back in the UI thread
HideDeletingProcessing();
}
private Task DeleteSelectedItem()
{
// doing the work on a Task thread
return Task.Run(() => DeleteSelectedItem = null);
}

wpf what happens if ShowDialog() is called from a non-UI thread?

So I currently have this code below, which has a background worker call showdialog(). However, I thought that the UI cannot be updated on a background thread, so how does the dialog display? Does the dialog actually get opened on the UI thread? what happens?
public partial class ProgressDialog : Window
{
BackgroundWorker _worker;
public BackgroundWorker Worker
{
get { return _worker; }
}
public void RunWorkerThread(object argument, Func<object> workHandler)
{
//store reference to callback handler and launch worker thread
workerCallback = workHandler;
_worker.RunWorkerAsync(argument);
//display modal dialog (blocks caller)
//never returns null, but is a nullable boolean to match the dialogresult property
ShowDialog();
}
I have gotten suggestions that I just run the code and check, but how do i check whether the show dialog window was opened on a new thread or on the background thread itself? Not sure how I would check that.
Anyway this was just a post to try to help my understanding of what is actually happening in my code.
Anyway finally understood more of the comments, so I think I understand everything that is going on. Most of my real problems weren't caused by this dialog anyway, they were caused by updating observable collections from a non-ui thread while controls were bound to them.
Technically you are not changing a property on your Main thread just creating a instance of another object.
But it could help if you elaborate a bit more on your method ShowDialog().
I had also problem with calling ShowDialog() from non-UI thread. And my answer is that it depends on the thread which calls the ShowDialog(). If you set the ApartamentState property for this thread before its start then everything will work as called from the UI thread. I have finally ended up with such a code:
private async void button1_Click(object sender, EventArgs e)
{
var foo = new Foo();
// MessageBox.Show(foo.DirPath) - this works as a charm but
// if, it is called from non UI thread needs special handling as below.
await Task.Run(() => MessageBox.Show(foo.DirPath));
}
public class Foo
{
private string dirPath;
public string DirPath
{
get
{
if (dirPath == null)
{
var t = new Thread(() =>
{
using (var dirDialog = new FolderBrowserDialog())
{
if (dirDialog.ShowDialog() == DialogResult.OK)
dirPath = dirDialog.SelectedPath;
}
}
);
t.IsBackground = true;
t.SetApartmentState(ApartmentState.STA);
t.Start();
t.Join();
}
return dirPath;
}
set
{
dirPath = value;
}
}
}
I dont know for sure but i thought that the showDialog doesnt create the object only showing it. So when u say ShowDialog it only tells to show. So it will run on the UI thread instead of the backgroundworker
(dont know for sure)

Threading.task and Dispatcher.Invoke method in the Window Close event

I want to perform series of operations synchronously while closing my MVVM based WPF Application.
Right now I am using Task and Dispatcher.Invoke within the tasks to show the message to the user.
The issue is that when i used Dispatcher.Invoke method in the myfunction function, application gets stuck there. I know this function is working properly when I used these function other than closed event.
So is there any issue of using the Dispatcher.Invoke method in the Close event os the application. How can i solve this?
/// <summary>
/// Main window closing
/// </summary>
private void MainWindowClosing(object args)
{
var task1 = System.Threading.Tasks.Task.Factory.StartNew(() =>
{
myfunction();
}).ContinueWith((cc) => { });
task1.Wait();
}
private void myfunction()
{
//my serries of operation will come here.
System.Windows.Application.Current.Dispatcher.Invoke(new Action(() =>
{
MessageBox.Show("test");
}));
}
You are creating a Deadlock here. It won't work even when you put the code in button click handler.
REASON
You are creating a task and waiting on task using task1.Wait(). Hence UI dispatcher is waiting for task to complete before it can process any further messages.
At same time you are invoking one operation on UI dispatcher from task that too synchronously here
App.Current.Dispatcher.Invoke(new Action(() =>
{
MessageBox.Show("test");
}));
but since UI dispatcher is still waiting on task to complete, you can't invoke any method synchronously on it. Hence the DEADLOCK (waiting on each other).
Possible Solution
If you want to invoke task synchronously and that too without closing window, you can achieve that using following steps:
First of all remove the task1.Wait(). (do not block UI dispatcher)
Second, maintain bool to keep count that close event has been initiated.
Last, cancel closing event by setting e.Cancel = true and manually raise close event from task itself once you finished.
Relevant code:
bool closeInitiated = false;
private void poc_Closing(object sender, CancelEventArgs e)
{
if (!closeInitiated)
{
var task1 = System.Threading.Tasks.Task.Factory.StartNew(() =>
{
myfunction();
}).ContinueWith((cc) => { });
closeInitiated = true;
e.Cancel = true;
}
}
private void myfunction()
{
App.Current.Dispatcher.Invoke(new Action(() =>
{
MessageBox.Show("test");
Close();
}));
}
I think the issue is at task1.Wait(); Go through this UI Thread Wait and DeadLock
I hope this will help.
I have gotten the same issue, and I figured the problem was that the Task where I'm invoking to, was already closed a second after clicking the Close button, so when the Invoking function takes a bit longer - it might crash.
If you still wanna execute this function, I used BeginInvoke instead of Invoke and it worked.
Here is my code:
if (app.Current.Dispatcher.CheckAccess()) //doesn't need to be invoked
myFunction();
else //coming from another thread need invoker
{
//at exiting program this task might already be canceled, so make sure it's not
if (!app.Current.Dispatcher.HasShutdownFinished)
app.Current.Dispatcher.Invoke(myFunction());
else
app.Current.Dispatcher.BeginInvoke(myFunction());

Update UI from thread in WinRT

Since the Windows 8 consumer preview was released a few days ago, I am working on the new WinRT (for Metro Applications) in C# and I had ported my self written IRC class to the new threading and networking.
The problem is: My class is running an thread for receiving messages from the server. If this happens, the thread is making some parsing and then firing an event to inform the application about this. The subscribed function then 'should' update the UI (an textblock).
This is the problem, the thread cannot update the UI and the invoker method that has worked with .NET 4.0 doesn't seem to be possible anymore. Is there an new workaround for this or even an better way to update the UI ? If I try to update the UI from the event subscriber i will get this Exception:
The application called an interface that was marshalled for a
different thread (Exception from HRESULT: 0x8001010E
(RPC_E_WRONG_THREAD))
The preferred way to deal with this in WinRT (and C# 5 in general) is to use async-await:
private async void Button_Click(object sender, RoutedEventArgs e)
{
string text = await Task.Run(() => Compute());
this.TextBlock.Text = text;
}
Here, the Compute() method will run on a background thread and when it finishes, the rest of the method will execute on the UI thread. In the meantime, the UI thread is free to do whatever it needs (like processing other events).
But if you don't want to or can't use async, you can use Dispatcher, in a similar (although different) way as in WPF:
private void Button_Click(object sender, RoutedEventArgs e)
{
Task.Run(() => Compute());
}
private void Compute()
{
// perform computation here
Dispatcher.Invoke(CoreDispatcherPriority.Normal, ShowText, this, resultString);
}
private void ShowText(object sender, InvokedHandlerArgs e)
{
this.TextBlock.Text = (string)e.Context;
}
Here is an easier way to do it I think!
First capture your UI SyncronizationContext with the following:
var UISyncContext = TaskScheduler.FromCurrentSynchronizationContext();
Run your server call operation or any other background thread operation you need:
Task serverTask= Task.Run(()=> { /* DoWorkHere(); */} );
Then do your UI operation on the UISyncContext you captured in first step:
Task uiTask= serverTask.ContinueWith((t)=>{TextBlockName.Text="your value"; }, UISyncContext);
IMO I think "ThreadPool" is the recommended route.
https://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh465290.aspx
public static Task InvokeBackground(Func<Task> action)
{
var tcs = new TaskCompletionSource<bool>();
var unused = ThreadPool.RunAsync(async (obj) =>
{
await action();
tcs.TrySetResult(true);
});
return tcs.Task;
}

Categories