AppDomain.UnhandledException handler doesn't fire in unit test - c#

In the code snippet below, why wouldn't the attached handler (of AppDomain.CurrentDomain.UnhandledException event) fire up when an exception is thrown in the unit test?
I'm using NUnit 2.5.10 with TestDriven.NET 3.0 on VS2010.
[TestFixture]
public class MyTests {
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) {
Console.WriteLine("Gotcha!");
}
[Test]
public void ExceptionTest1() {
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
throw new Exception("ExceptionInTest");
}
}
Output: (No gotchas)
------ Test started: Assembly: WcfQueue.Test.dll ------
Test 'xxxxx.Test.MyTests.ExceptionTest1' failed: System.Exception : ExceptionInTest
ProgramTests.cs(83,0): at xxxxx.Test.MyTests.ExceptionTest1()
0 passed, 1 failed, 0 skipped, took 1.98 seconds (NUnit 2.5.5).
Update: The purpose of this question is NOT to test .Net framework or NUnit. I just want to find out the reason why, in a unit test, the handler wouldn't fire.

An exception will percolate up the call stack until you reach a try/catch block that can handle that exception, an AppDomain boundary, or the top of the stack (in that order of priorities).
If you're executing within the same AppDomain that NUnit gives you, NUnit will catch your exception. This preempts the AppDomain boundary stuff, which would have called your event.
So your test needs to create a new AppDomain and execute its code there (including the setup, which adds the event handler for AppDomain.UnhandledException). Everything should work as expected there.

I guess, the event isn't fired because exception is handled. By test framework, to generate report.

As far as I know the unhandled exception event is only triggered in the default AppDomain. I think NUnit uses to execute the tests a different AppDomain which is the reason why your event does not get triggered.

Related

Can C# WinForm static void Main NOT catching Exception?

I have a WinForm application written in C# where I put a try-catch block in the Program.cs, in the program entry, the static void Main method, right in the beginning of the application like this:
using System;
using System.IO;
using System.Windows.Forms;
namespace T5ShortestTime {
static class Program {
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main() {
try {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new T5ShortestTimeForm());
} catch (Exception e) {
string errordir = Path.Combine(Application.StartupPath, "errorlog");
string errorlog = Path.Combine(errordir, DateTime.Now.ToString("yyyyMMdd_HHmmss_fff") + ".txt");
if (!Directory.Exists(errordir))
Directory.CreateDirectory(errordir);
File.WriteAllText(errorlog, e.ToString());
}
}
}
}
As you can see, the Application is put in a try-catch block and in the catch block, the only thing it does is to create an error log file.
Now, so far so good. My application is running well and if I encounter a crash, the last Exception should be captured by the try-catch block and stored in the error log file.
However, as I run my program for a while, I get an unhandled exception (null reference). What surprise me is that the exception does not create an error log file.
Now, this post shows that it is possibly caused by ThreadException or HandleProcessCorruptedStateExceptions (the two most upvoted answers), but my case shows a simple null reference exception:
Problem signature:
Problem Event Name: CLR20r3
Problem Signature 01: T5ShortestTime.exe
Problem Signature 02: 2.8.3.1
Problem Signature 03: 5743e646
Problem Signature 04: T5ShortestTime
Problem Signature 05: 2.8.3.1
Problem Signature 06: 5743e646
Problem Signature 07: 182
Problem Signature 08: 1b
Problem Signature 09: System.NullReferenceException
OS Version: 6.3.9600.2.0.0.272.7
Locale ID: 1033
Additional Information 1: bb91
Additional Information 2: bb91a371df830534902ec94577ebb4a3
Additional Information 3: aba1
Additional Information 4: aba1ed7202d796d19b974eec93d89ec2
Read our privacy statement online:
http://go.microsoft.com/fwlink/?linkid=280262
If the online privacy statement is not available, please read our privacy statement offline:
C:\Windows\system32\en-US\erofflps.txt
Why would that be?
the last Exception should be captured by the try-catch block
That is not going to happen. Except in one case, when you run your program with a debugger attached. So you surely got lulled into believing it would work, everybody always starts out running their program with F5 for a while.
Application.Run() has a back-stop in its code that raises events, try/catch-em-all that raises the Application.ThreadException event when an event handler throws an unhandled exception. That back-stop is really, really necessary, especially on the x64 version of Windows 7. Very Bad Things happen when there is no exception handler. That back-stop is however not in place when you run with the debugger, that makes unhandled exceptions too difficult to debug.
So when you debug then your catch clause will run. Making unhandled exceptions too difficult to debug. When you run without a debugger then your catch clause will not run and your program will crash, just as you described. Making unhandled exception too difficult to debug.
So don't do it this way. How Application.Run() deals with unhandled exceptions is configured with the Application.SetUnhandledExceptionMode() method. You'll like this version better:
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
if (!System.Diagnostics.Debugger.IsAttached) {
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
AppDomain.CurrentDomain.UnhandledException += LogException;
}
Application.Run(new Form1());
}
private static void LogException(object sender, UnhandledExceptionEventArgs e) {
string errordir = Path.Combine(Application.StartupPath, "errorlog");
string errorlog = Path.Combine(errordir, DateTime.Now.ToString("yyyyMMdd_HHmmss_fff") + ".txt");
if (!Directory.Exists(errordir))
Directory.CreateDirectory(errordir);
File.WriteAllText(errorlog, e.ToString());
AppDomain.CurrentDomain.UnhandledException -= LogException;
MessageBox.Show("Error details recorded in " + errorlog, "Unexpected error");
Environment.Exit(1);
}
With this code in place, you can debug unhandled exceptions without any problems. The Debugger.IsAttached test ensures that the debugger will always stop when an event handler falls over. Without a debugger, it then disables the Application.ThreadException event (it is quite useless) and favors listening to all exceptions. Including the ones raised in worker threads.
You ought to give an alert to the user so the window doesn't just disappear without any trace. I was going to recommend MessageBox but noticed that this bug is currently back again on Windows 10. Sigh.
ThreadException is not an exception type like (NullReferenceException). It is that:
This event allows your Windows Forms application to handle otherwise
unhandled exceptions that occur in Windows Forms threads
This means that it handles exceptions in threads other than the Main Thread.
So, you need to subscribe to : AppDomain.CurrentDomain.UnhandledException also in order to handle the exceptions in your Main Thread (Regardless of the type of the exception e.g. NullReference, IndexOutOfRange, etc..).
Ok, in the end I implement Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException) as exampled by Hans Passant for VB.Net in this post. Here I put my own code + error logging for C#:
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
if (!System.Diagnostics.Debugger.IsAttached) {
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
AppDomain.CurrentDomain.UnhandledException += LogUnhandledExceptions;
}
Application.Run(new T5ShortestTimeForm());
}
private static void LogUnhandledExceptions(object sender, UnhandledExceptionEventArgs e) {
Exception ex = (Exception)e.ExceptionObject;
string errordir = Path.Combine(Application.StartupPath, "errorlog");
string errorlog = Path.Combine(errordir, DateTime.Now.ToString("yyyyMMdd_HHmmss_fff") + ".txt");
if (!Directory.Exists(errordir))
Directory.CreateDirectory(errordir);
File.WriteAllText(errorlog, ex.ToString());
Environment.Exit(System.Runtime.InteropServices.Marshal.GetHRForException(ex));
}
Also, it seems like the source of confusion here is that there are actually two propagated Exceptions occur:
The first one was any Exception from the application itself:
System.Exception: Exception of type 'System.Exception' was thrown.
at T5ShortestTime.T5ShortestTimeForm..ctor() in C:\Test.cs:line 45
at T5ShortestTime.Program.Main() in C:\Test.cs:line 19
at ...
And the second one occurs during the Dispose of the Form components, which creates another exception, and it is the null reference exception:
System.NullReferenceException: Object reference not set to an instance of an object.
at T5ShortestTime.T5ShortestTimeForm.Dispose(Boolean disposing)
at System.ComponentModel.Component.Finalize()
So, when I test the exception in my app, the NullReferenceException comes the last, in the Dispose.
I only manage to capture this after I set the UnhandledExceptionMode to ThrowException above.

MS UnitTestFramework retrieve and log exceptions c#

I just started on a rather extensive automation project that uses MS's UnitTestFramework. One thing I noticed is that when there's an error in my code - not the app I test - the framework catches that error and fails the test in a nice happy way that allows for the test iteration to complete. However, I want to be able to see those exceptions & stack trace in my log4net logs, and so far I have found no way to grab them in my test cleanup (or anywhere outside of a try catch block which I have no intention of splattering in every method).
Anyone know how to get these exceptions into my logs?
You could use First-Chance Exception Notifications
via the AppDomain.FirstChanceException Event -
This event is only a notification. Handling this event does not handle
the exception or affect subsequent exception handling in any way.
After the event has been raised and event handlers have been invoked,
the common language runtime (CLR) begins to search for a handler for
the exception. FirstChanceException provides the application domain
with a first chance to examine any managed exception.
So something like this (note it is in a method marked as AssemblyInitialize, which means it runs once per test run, and the code is excluding the AssertFailedException thrown by MSTest when a test fails. You might want to exclude other exceptions as well, as otherwise there could be a lot of 'noise' in the logs.)
[TestClass]
public class Initialize
{
[AssemblyInitialize]
public static void InitializeLogging(TestContext testContext)
{
AppDomain.CurrentDomain.FirstChanceException += (source, e) =>
{
if (e.Exception is AssertFailedException == false)
LogManager.GetLogger("TestExceptions").Error(e.Exception);
};
}
}
If it feasible for you to replace the [TestMethod] attribute then you can define your own attribute MyTestMethod, say, by deriving from the default one like this:
public class MyTestMethodAttribute : TestMethodAttribute
{
public override TestResult[] Execute(ITestMethod testMethod)
{
TestResult[] testResults = base.Execute(testMethod);
foreach (var testResult in testResults.Where(e => e.Outcome == UnitTestOutcome.Failed))
testResult.LogOutput += $"Exception `{testResult.TestFailureException.GetType().Name}` with message `{testResult.TestFailureException.Message}`.";
return testResults;
}
}
The following test then produces the expected log message Exception TestFailedException with message Assert.Fail failed. Some exception text. in the standard output panel of Visual Studio's Test Explorer.
[TestClass]
public class Tests
{
[MyTestMethod]
public void Test()
=> Assert.Fail("Some exception text");
}
The approach also works when tests execute in parallel.

MsTest TestCleanup method not called when an unhandled exception is thrown

I have a test which uses an external assembly to access UI features in the application we're testing. This assembly throws an exception of its own custom type if the UI is not in the appropriate state.
I've set up a TestCleanup method that kills the application's process (while a TestInitialize starts it) so that after a test run has been completed, the UI is restarted with a clean state.
This work well under regular conditions, however, whenever an exception from the referenced assembly is thrown, it never gets to the cleanup method and jumps straight ahead to the next test. This doesn't happen with exceptions thrown from the test itself, like AssertFailedException. I even tried throwing a basic Exception from the test, and it got to the cleanup method.
If an exception is uncaught in TestInitialize, TestCleanup won't be called.
http://web.archive.org/web/20140310065725/http://connect.microsoft.com/VisualStudio/feedback/details/694337/testcleanup-method-does-not-run-when-it-should
When MSTest fails in TestInitialize, why doesn't TestCleanup get executed?
Under what circumstances are [ClassCleanup] and [TestCleanup] not run
This unfortunately diverges from the way C# handles exceptions in constructors : when this happens, the finalizer is called.
But you can directly call CleanUp method from the catch block
[TestCleanup]
public void Clenup()
{
..............
}
[TestMethod]
public void Test1()
{
try
{...................}
catch (Exception e)
{
Cleanup();
throw new Exception();
}
}
That was fixed in MsTest v2.
https://github.com/Microsoft/testfx/issues/250
Extract from the above link:
This was a conscious compat break to give unit test writers a choice to cleanup partially
initialized methods.

AppDomain.CurrentDomain.UnhandledException does not get called

I have a WCF service that has the following code in Global.asax:
protected void Application_Start(object sender, EventArgs e)
{
// Make sure that any exceptions that we don't handle at least get logged.
AppDomain.CurrentDomain.UnhandledException += LogUnhandledException;
}
private void LogUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Log.Error.LogException("UnhandledException", e.ExceptionObject as Exception);
}
The idea is to at least log all exceptions that are unhanded.
But it does not seem to ever be called. I tried doing a Divide by Zero in one of my service operations and it just stops the service after it hits the exception.
int zero = 0;
int result = 100 / zero;
The LogUnhandledException method never gets called.
I have tried this in both IIS and running in the debugger.
How can I get this event to work for a WCF service?
The unhandled exception filter for an app domain is a last-ditch attempt to allow the application to log meaningful information before it is terminated.
This event provides notification of uncaught exceptions. It allows the application to log information about the exception before the system default handler reports the exception to the user and terminates the application.
If WCF allowed an exception thrown by a service to be completely unhandled in this way it would mean that when the service is hosted in IIS the entire worker process would be terminated because a single request raised an exception - not a desirable outcome. As a result WCF doesn't leave exceptions thrown by services unhandled - this event will not be raised in this case.
If you want to log exceptions thrown by WCF services then take a look at the IErrorHandler interface instead.
Try also catching a ThreadException, e.g.
Application.ThreadException += Application_ThreadException;
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
I had the same issue in the WinForms app a while ago. UnhandledException is raised for truly unhandled exceptions that would normally terminate your entire process immediately. Typically there's a global handler that doesn't let that happen, and provides some default behavior. So you need to catch the exception before it goes to that handler. This can be done via a ThreadException.
Have you tried this?
From http://msdn.microsoft.com/en-us/library/system.appdomain.unhandledexception.aspx
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(LogUnhandledException);
I'm wondering what is difference between your call and this one, with the "+= new"... but it's worth the try.

How to detect when application terminates?

This is a follow up to my initial question and I would like to present my findings and ask for corrections, ideas and insights. My findings (or rather interpretations) come from people's answers to my previous question, reading MSDN .NET 3.5 documentation and debugging .NET 3.5 code. I hope this will be of value to someone who was wondering like me how to detect when an application terminates.
Events:
System.AppDomain.CurrentDomain.ProcessExit: raised when process exits, e.g. after the default AppDomain and everything else was unloaded [Total execution time is limited to just 3 seconds!]. For WPF, use System.Windows.Application.Exit instead. For Windows Forms, run code after Application.Run(...) in main method.
System.AppDomain.CurrentDomain.DomainUnload: raised when an AppDomain other than default AppDomain unloads, e.g. when running classes with unit testing frameworks (MbUnit with TestDriven.NET).
System.AppDomain.CurrentDomain.UnhandledException: (if handled in default AppDomain:) raised for any unhandled exception in any thread, no matter what AppDomain the thread started in. This means, this can be used as the catch-all for all unhandled exceptions.
System.Windows.Application.Exit: raised when WPF application (i.e. the default AppDomain) exits gracefully. Override System.Windows.Application.OnExit to take advantage of it.
Finalizers (destructors in C#): run when garbage collector frees unmanaged resources. [Total execution time is limited!].
Order of events:
WPF application: graceful exit
System.Windows.Application.Exit
System.AppDomain.CurrentDomain.ProcessExit
Finalizers
WPF application: unhandled exception
System.AppDomain.CurrentDomain.UnhandledException
MbUnit running inside TestDriven.NET: passed test (graceful exit)
System.AppDomain.CurrentDomain.DomainUnload
Finalizers
MbUnit running inside TestDriven.NET: failed test (unhandled exceptions are handled by MbUnit)
AppDomain.CurrentDomain.DomainUnload
Finalizers
Questions:
Are my interpretations/findings correct?
Do you know of more details that I have
left out? E.g. what is the total
execution time for finalizers?
Do you know of any other events /
ideas that I be aware of?
What events are there and what order do they get raised in other applications, e.g. Windows Forms, Web Service, ASP.NET web site, etc?
Prompted by ssg31415926's question/answer (this question is a bit reversed), there's also Application.SessionEnding which is called when the when the user logs off or shuts down. It is called before the Exit event.
When Dispatcher.BeginInvokeShutdown() is called, Application.Exit will not be called.
The default timeout for a finalizer's execution is 2 seconds.
You write:
System.AppDomain.CurrentDomain.UnhandledException: (if handled in default AppDomain:) raised for any unhandled exception in any thread, no matter what AppDomain the thread started in. This means, this can be used as the catch-all for all unhandled exceptions.
I do not think that this is correct. Try the following code:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace AppDomainTestingUnhandledException
{
class Program
{
static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException +=
(sender, eventArgs) => Console.WriteLine("Something went wrong! " + args);
var ad = AppDomain.CreateDomain("Test");
var service =
(RunInAnotherDomain)
ad.CreateInstanceAndUnwrap(
typeof(RunInAnotherDomain).Assembly.FullName, typeof(RunInAnotherDomain).FullName);
try
{
service.Start();
}
catch (Exception e)
{
Console.WriteLine("Crash: " + e.Message);
}
finally
{
AppDomain.Unload(ad);
}
}
}
class RunInAnotherDomain : MarshalByRefObject
{
public void Start()
{
Task.Run(
() =>
{
Thread.Sleep(1000);
Console.WriteLine("Uh oh!");
throw new Exception("Oh no!");
});
while (true)
{
Console.WriteLine("Still running!");
Thread.Sleep(300);
}
}
}
}
As far as I can tell, the UnhandledException handler is never called, and the thread will just silently crash (or nag at you if you run it in the debugger).
Just add a new event on your main form:
private void frmMain_Load(object sender, EventArgs e)
{
Application.ApplicationExit += new EventHandler(this.WhenItStopsDoThis);
}
private void WhenItStopsDoThis(object sender, EventArgs e)
{
//Program ended. Do something here.
}

Categories