Application.ThreadException event for ALL GUI threads - c#

I have a WinForms application that creates a multiple forms, each in their own GUI thread, (not the main GUI thread). I would like to handle the Unhandled Exception event (Application.ThreadException) for all these forms to handle any errors. I would also like to handle exceptions from worker threads - this bit seems to be working correctly, but I'm having trouble with exceptions from GUI threads still:
Program.cs:
[STAThread]
static void Main()
{
AttachExceptionHandlers();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
public static void AttachExceptionHandlers()
{
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
Dispatcher.CurrentDispatcher.UnhandledException += new System.Windows.Threading.DispatcherUnhandledExceptionEventHandler(CurrentDispatcher_UnhandledException);
}
Form1.cs:
//GUI Thread Crash
private void button1_Click(object sender, EventArgs e)
{
object a = null;
a.ToString();
}
//Worker Thread Crash
private void button2_Click(object sender, EventArgs e)
{
Thread myThread = new Thread(() =>
{
object a = null;
a.ToString();
});
myThread.Start();
myThread.Join();
}
//New Thread, New Gui Crash
private void button3_Click(object sender, EventArgs e)
{
Thread myThread = new Thread(() =>
{
using (CrashingForm form = new CrashingForm()) //Crashing Form Crashes in it's FormLoad event.
{
Application.Run(form);
}
});
myThread.Start();
myThread.Join();
}
This code will call my exception handler in the first 2 instances (GUI Thread Crash and Worker Thread Crash) but does not handle the third instance where a new GUI thread is created. I have found that if I call Program.AttachExceptionHandlers(); before the Application.Run(form) line, all is OK, but this is undesirable as I would have to implement some logic to make sure the call to Program.AttachExceptionHandlers() is made before we call a form is created on each thread (the call to Application.SetUnhandledExceptionMode fails if called after creating a form on the thread).
This example is part of a bigger bit of code which would ideally give the user of my code a simple API to call at the start of their application (like in Program.cs) to attach exception handlers. The exception handler then does some magic to record details about the exception being thrown before the application dies. So telling the user they have to track down each time they create a new GUI thread (worker threads don't seem to be effected by this issue) and reattach the Application.ThreadException Handler is not such a clean solution.
Is there another way to achieve this, without having to re registerer for the Application.ThreadException event each time a new GUI thread is created?

Is there another way to achieve this, without having to re registerer
for the Application.ThreadException event each time a new GUI thread
is created?
I'm not aware of any, and my teammate and I have spent a good amount of time looking into it. .NET WinForms doesn't appear to be very opinionated when it comes to how to create / manage / destroy multiple message pumps.
We use framework methods similar to the one below, in addition to Retlang's WinFormsFiber.
using System;
using System.Threading;
using System.Windows.Forms;
internal static class Program
{
[STAThread]
private static void Main()
{
CreateFormAndStartMessagePump(() => CreateForm("first"), OnException, OnException, false, "pumpThread1");
CreateFormAndStartMessagePump(() => CreateForm("second"), OnException, OnException, false, "pumpThread2");
// note app shutdown not handled here
}
private static T CreateFormAndStartMessagePump<T>(
Func<T> createForm,
ThreadExceptionEventHandler onThreadException,
UnhandledExceptionEventHandler onDomainException,
bool isBackground,
string name) where T : Form
{
var latch = new ManualResetEvent(false);
T form = null;
var thread = new Thread(ts =>
{
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
Application.ThreadException += onThreadException;
AppDomain.CurrentDomain.UnhandledException += onDomainException;
form = createForm();
latch.Set();
Application.Run();
})
{
IsBackground = isBackground,
Name = name
};
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
latch.WaitOne();
return form;
}
private static Form CreateForm(string name)
{
var form = new Form();
form.Text = name;
form.Show();
return form;
}
private static void OnException(object sender, UnhandledExceptionEventArgs e)
{
// ...
}
private static void OnException(object sender, ThreadExceptionEventArgs e)
{
// ...
}
}

Related

Trying to call Invoke method throws an exception in a winform app

I have a legacy project (Visual Studio 2003) which was made in the past by other people and now I need to maintain it. This legacy app communicate with another one using Windows Message Queues. One queue for receive, queueInput, and another one for send, queueOutput.
There is an action which is divided in six steps. While the action is being performed the progress is shown in the winform, let's say MyFrm.
So the action is started by instantiating a winform and then from its load event, first step is launched through a thread:
MyFrm frm = new MyFrm(param1, param2);
frm.ShowDialog();
Load event:
private void MyFrm_Load(object sender, System.EventArgs e)
{
Thread th = new Thread(new ThreadStart(BeginFirstStep));
th.Start();
}
Then some threads are created and executed in cascade:
private void BeginFirstStep()
{
// Do some stuff
Invoke(new ThreadStart(FirstStepCompleted));
}
private void FirstStepCompleted()
{
myLabel1.Visible = true;
// Do some other stuff
Thread th = new Thread(new ThreadStart(BeginSecondStep));
th.Start();
}
private void BeginSecondStep()
{
// Do some stuff
Invoke(new ThreadStart(SecondStepCompleted));
}
private void SecondStepCompleted()
{
label3.enabled = false;
// Do some other stuff
if (some_condition)
{
ThirdSteepCompleted();
return;
}
else if (some_other_condition)
{
Thread th = new Thread(new ThreadStart(Do_AnotherTask));
th.Start();
}
else
{
Do_AnotherTask2();
}
}
private MessageQueue queueInput;
private MessageQueue queueOutput;
private void Do_AnotherTask2(object state)
{
if(InvokeRequired)
{
Invoke(new TimerCallback(Do_AnotherTask2), new object[]{state});
}
else
{
queueOutput = new MessageQueue(#".\Private$\Actions");
queueInput = MessageQueue.Create(#".\Private$\Response");
queueInput.ReceiveCompleted += new ReceiveCompletedEventHandler(queueInput_ReceiveCompleted);
queueInput.BeginReceive();
queueOutput.Send(BuildMsgToSend());
}
}
From another winform app, the response is added to this queue and then the event
queueInput_ReceiveCompleted is raised:
private void queueInput_ReceiveCompleted(object sender, ReceiveCompletedEventArgs e)
{
// Do some stuff with data received
Invoke(new ThreadStart(ThirdStepCompleted)); // HERE IT SOMETIMES CRASHES (NOT ALWAYS).
}
private void ThirdStepCompleted()
{
// Do some other stuff
Thread th = new Thread(new ThreadStart(BeginFourthStep));
th.Start();
}
and so on... with the rest of the steps.
The problem is:
Sometimes (not always), program crashes at queueInput_ReceiveCompleted handler method when performing the Invoke. The error says:
System.InvalidOperationException: Cannot call Invoke or InvokeAsync on
a control until the window handle has been created.
I know, it is a complex and difficult code to understand, but it was designed as is....

wpf InvalidOperationException when using parameters in constructor [duplicate]

I am using http://www.codeproject.com/KB/IP/Facebook_API.aspx
I am trying to call the XAML which is created using WPF. But it gives me an error:
The calling thread must be STA, because many UI components require this.
I don't know what to do. I am trying to do this:
FacebookApplication.FacebookFriendsList ffl = new FacebookFriendsList();
But it is giving me that error.
I added a background worker:
static BackgroundWorker bw = new BackgroundWorker();
static void Main(string[] args)
{
bw.DoWork += bw_DoWork;
bw.RunWorkerAsync("Message to worker");
Console.ReadLine();
}
static void bw_DoWork(object sender, DoWorkEventArgs e)
{
// This is called on the worker thread
FacebookApplication.FacebookFriendsList ffl = new FacebookFriendsList();
Console.WriteLine(e.Argument); // Writes "Message to worker"
// Perform time-consuming task...
}
Try to invoke your code from the dispatcher:
Application.Current.Dispatcher.Invoke((Action)delegate{
// your code
});
If you make the call from the main thread, you must add the STAThread attribute to the Main method, as stated in the previous answer.
If you use a separate thread, it needs to be in a STA (single-threaded apartment), which is not the case for background worker threads. You have to create the thread yourself, like this:
Thread t = new Thread(ThreadProc);
t.SetApartmentState(ApartmentState.STA);
t.Start();
with ThreadProc being a delegate of type ThreadStart.
You can also try this
// create a thread
Thread newWindowThread = new Thread(new ThreadStart(() =>
{
// create and show the window
FaxImageLoad obj = new FaxImageLoad(destination);
obj.Show();
// start the Dispatcher processing
System.Windows.Threading.Dispatcher.Run();
}));
// set the apartment state
newWindowThread.SetApartmentState(ApartmentState.STA);
// make the thread a background thread
newWindowThread.IsBackground = true;
// start the thread
newWindowThread.Start();
I suspect that you are getting a callback to a UI component from a background thread. I recommend that you make that call using a BackgroundWorker as this is UI thread aware.
For the BackgroundWorker, the main program should be marked as [STAThread].
Just mark your program Main method with the [STAThread] attribute and the error goes away! it's magic :)
Example:
class Program {
[STAThread]
static void Main(string[] args) {
// My code here
}
}
In my case, I wanted to launch a WPF window from a console app. Simply setting the Main method with [STAThread] didn't work.
A combination of Timores' and Mohammad's answer worked for me:
private static void StaThreadWrapper(Action action)
{
var t = new Thread(o =>
{
action();
System.Windows.Threading.Dispatcher.Run();
});
t.SetApartmentState(ApartmentState.STA);
t.Start();
}
Example usage:
StaThreadWrapper(() =>
{
var mainWindow = new MainWindow();
mainWindow.Show();
});
For me, this error occurred because of a null parameter being passed. Checking the variable values fixed my issue without having to change the code. I used BackgroundWorker.
If the Application.Current is null for example by unit test, you can try this:
System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke( YOUR action )
Another situation if you may meet, choosing which Window to new and show.
Don't make the choice in App.xaml.cs' App() or OnStartup(), instead, make the choice in Startup event.
// App.xaml.cs
private App()
{
Window window = CheckSession() ? new InstallWindow() : (Window)new LoginWindow();
window.Show(); // bad 1
}
protected override void OnStartup(StartupEventArgs e)
{
Window window = CheckSession() ? new InstallWindow() : (Window)new LoginWindow();
window.Show(); // bad 2
base.OnStartup(e);
}
Below should be good
// App.xaml.cs
private App()
{
Startup += Application_Startup;
}
private void Application_Startup(object sender, StartupEventArgs e)
{
Window window = CheckSession() ? new InstallWindow() : (Window)new LoginWindow();
window.Show(); // good
}
Also remember to remove the StartupUri from App.xaml
<!--App.xaml-->
<Application StartupUri="MainWindow">
<!--remove StartupUri-->
</Application>
OR add the event here is OK too.
<!--App.xaml-->
<Application Startup="Application_Startup">
</Application>
// App.xaml.cs
private App()
{
}
private void Application_Startup(object sender, StartupEventArgs e)
{
Window window = CheckSession() ? new InstallWindow() : (Window)new LoginWindow();
window.Show(); // good
}
If you call a new window UI statement in an existing thread, it throws an error. Instead of that create a new thread inside the main thread and write the window UI statement in the new child thread.

How do I get the name of the thread that the exception occurred on?

I am handling thread exceptions but I want to get the name of the Thread that the exception occurred on. It appears that when the thread exception fires the event stays on the main thread although I think the exception could have occurred on a different thread.
static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
ShowFaultDialog(e.Exception, "(Application) Thread Exception [" + System.Threading.Thread.CurrentThread.Name + "]");
}
In static void Main():
Thread.CurrentThread.Name = "Main Thread";
VS 2010 shows the main thread as having a 'Name' of "Main Thread" but actually the thread name is null.
If you mean handling of Application.ThreadException event: it fires only on exceptions, that was thrown from WinForms threads. Usually, there's one WinForms thread in application: the main thread.
UPDATE.
Here's sample that demonstrating Application.ThreadException and AppDomain.UnhandledException behavior difference:
1) Program class:
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
Application.Run(new Form1());
}
static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Debug.WriteLine(Thread.CurrentThread.Name);
}
static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{
Debug.WriteLine(Thread.CurrentThread.Name);
}
}
2) Main form (a form with two buttons) code-behind:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
throw new InvalidOperationException();
}
private void button2_Click(object sender, EventArgs e)
{
new Thread(() => { throw new InvalidOperationException(); })
{
Name = "Worker Thread"
}.Start();
}
}
When you are clicking on button1, you're throwing exception from WinForms thread. So, this exception will be handled at Application_ThreadException by default.
When you are clicking on button2, you're throwing exception from worker thread, which is not a WinForms thread. Application.ThreadException isn't fired in this case, instead AppDomain.UnhandledException event is fired (and CurrentDomain_UnhandledException is called, producing 'Worker Thread' line in output window).
Use an incrememnted numerical variable (such as byte) to give each thread it's own name eg
string threadname = "Thread" + threadnumber
And then use the catch statement to notify you like so:
ShowFaultDialog(e.exception, threadname)
That way you'll be able to tell which thread it is, in theory.
As I understand from MSDN the Application_ThreadException event allows Windows Forms applications to handle unhandled exceptions that occur in Windows Forms threads and when you reach this event you are in your main UI thread. So it will print always the same.
Have you checked the Exception.TargetSite property? This property gives you back the method name and signature where the exception occurred.

I get an error about using a different thread?

When I click my ActionButton, there is a timer that starts and after 3 seconds, it must fire a methode to change the current ContentPage to the another page.
But i get a message : The calling thread cannot access this object because a different thread owns it. I dont understand what i am doing wrong. But if i put the ChangeContent() method in the click_event, it works, but in the _tm_elapsed it doenst work?
using smartHome2011.FramePages;
using System.Timers;
public partial class AuthenticationPage : UserControl
{
private MainWindow _main;
private Storyboard _storyboard;
private Timer _tm = new Timer();
private HomeScreen _homeScreen = new HomeScreen();
public AuthenticationPage(MainWindow mainP)
{
this.InitializeComponent();
_main = mainP;
}
private void ActionButton_Click(object sender, System.EventArgs eventArgs)
{
_main.TakePicture();
identifyBox.Source = _main.source.Clone();
scanningLabel.Visibility = Visibility.Visible;
_storyboard = (Storyboard) FindResource("scanningSB");
//_storyboard.Begin();
Start();
}
private void Start()
{
_tm = new Timer(3000);
_tm.Elapsed += new ElapsedEventHandler(_tm_Elapsed);
_tm.Enabled = true;
}
private void _tm_Elapsed(object sender, ElapsedEventArgs e)
{
((Timer) sender).Enabled = false;
ChangeContent();
//MessageBox.Show("ok");
}
private void ChangeContent()
{
_main.ContentPage.Children.Clear();
_main.ContentPage.Children.Add(_homeScreen);
}
}
Description
You have to use Invoke to ensure that the UI Thread (the thread who has created your Control) will execute that.
1. If you are doing Windows Forms then do this
Sample
private void ChangeContent()
{
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(ChangeContent));
return;
}
_main.ContentPage.Children.Clear();
_main.ContentPage.Children.Add(_homeScreen);
}
2. If you are doing WPF then do this
private void _tm_Elapsed(object sender, ElapsedEventArgs e)
{
((Timer) sender).Enabled = false;
this.Dispatcher.Invoke(new Action(ChangeContent), null);
//MessageBox.Show("ok");
}
More Information
Windows Forms
MSDN - Control.Invoke Method
MSDN - Control.InvokeRequired Property
WPF
MSDN - Dispatcher.Invoke Method
MSDN - Dispatcher Class
The logic executed in the Elapsed event of the Timer is run on a separate thread from the rest of your code. This thread cannot access objects on the main/GUI thread.
This thread should help you find out how to do it: How to update the GUI from another thread in C#?
I suspect you are using a System.Threading.Timer. You can avoid cross thread operation by just using a Windows.Forms timer:
http://msdn.microsoft.com/en-us/library/system.windows.forms.timer.aspx
That timer uses regular messages and the event occours on the same thread of the UI.
The event to use is no more called "Elapsed", but "Tick" read the doc here: http://msdn.microsoft.com/en-us/library/system.windows.forms.timer.tick.aspx

ThreadExceptionEventHandler and invoking delegates

If I assign a ThreadExceptionEventHandler to Application.ThreadException, why when I invoke a delegate method using a control on the main application thread are any exceptions thrown by that delegate not triggering the event handler?
i.e.
static void Main()
{
...
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
Application.Run(new Form1());
}
static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
Console.Error.Write("A thread exception occurred!");
}
...
private void Form1_Load(object sender, EventArgs e)
{
Thread syncThread = new Thread(new ThreadStart(this.ThrowException));
syncThread.Start();
}
private void ThrowException()
{
button1.Invoke(new MethodInvoker(delegate
{
// Not handled by ThreadExceptionEventHandler?
throw new Exception();
}));
}
The context on this is that I have a background thread started from a form which is throwing an unhandled exception which terminates the application. I know this thread is going to be unreliable since it is network connectivity reliant and so subject to being terminated at any point, but I'm just interested as to why this scenario doesn't play out as I expect?
Use AppDomain.CurrentDomain.UnhandledException instead , to catch exceptions that occur in threads not created and owned by Windows Forms
You should definetly see MSDN article to clarify this issue

Categories