Get Method, Class and LineNumber from StatckTrace in Application_UnhandledException event - c#

I am developing Windows Phone 7 Silverlight Application. I want to do Application Level error handling instead of writing try...catch... in all methods. I need to extract Method Name, Class Name and Line Number where the actual error occurred. Below is the demo code. In Application_UnhandledException event, I am expecting Method = "GenerateError" and Class = "ExceptionTesting". Also, I want to get LineNumber where the actual error occurred (this is not shown in code).
Code to generate Error:
public partial class ExceptionTesting : PhoneApplicationPage
{
// Generate Error to Test Exception Handling
private void GenerateError()
{
Int16 i = Convert.ToInt16("test");
}
}
Code that Handles Application Level Exception:
// Code to execute on Unhandled Exceptions
private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
{
StackTrace st = new StackTrace();
var query = st.GetFrames() // get the frames
.Select(frame => new
{
Method = frame.GetMethod(),
Class = frame.GetMethod().DeclaringType
});
foreach (var q in query)
{
if (q.Method.Name.Contains("GenerateError"))
{
MessageBox.Show("Class: " + q.Class + ", Method: " + q.Method);
}
}
if (System.Diagnostics.Debugger.IsAttached)
{
// An unhandled exception has occurred; break into the debugger
System.Diagnostics.Debugger.Break();
}
}

The Application_UnhandledException method is not called from your method where the exception happens, so new StrackTrace() will not be meaningful, as you have discovered.
To get the stack trace for the place where the exception occurred, use e.Exception.StackTrace.
Note that the real exception may be wrapped inside another exception, possibly several layers deep (e.Exception.InnerException).

You could also use BugSense to get this information.
Disclaimer: I am one of the cofounders

Related

Any workaround to get outer exceptions from an exception thrown inside an Invoked method in WinForms?

In Windows Forms when an exception is thrown from a call to Control.Invoke or Control.BeginInvoke and it is unhandled by any catch block, it can be handled by a control- or application-level handler via the Windows.Forms.Application.ThreadException event. Its handler takes an EventArgs with a property Exception, which is the exception thrown. However, under the hood, Windows.Forms.Control (undesirably) strips off all but the most inner exception, per this answer.
Found this blog post on it as well, but it doesn't suggest any workaround.
(This currently is causing me to get a trivial stack trace in my error logs--a stack trace that tells me the innermost details of , but there is no calling code mentioned and no way to figure out the code location of such an error.)
Is there a workaround? Is there any way to get all the outer exceptions in my application event handler (which is logging unexpected exceptions for troubleshooting)?
The only suggestion I have found seems to be to catch the exception inside the invoked code and stuff some of its info into Exception.Data, perhaps in a new exception--but if I knew the outer code that caused the exception, I could just fix the bug rather than logging it. Instead, how could I do this globally without wrapping a try-catch around every candidate block of code?
This is admittedly a hack, but it's the best solution I was able to come up with which supports both global exception handling in WinForms and all exceptions, even with inner exceptions.
In the Program.cs:
internal static class Program
{
[STAThread]
static void Main()
{
ApplicationConfiguration.Initialize();
AppDomain.CurrentDomain.FirstChanceException += CurrentDomain_FirstChanceException;
Application.ThreadException += Application_ThreadException;
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException, true);
Application.Run(new MyMainForm());
}
private static void CurrentDomain_FirstChanceException(object sender, FirstChanceExceptionEventArgs e)
{
_outermostExceptionCache.AddException(e.Exception);
}
private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{
Exception exception = null;
if (e?.Exception != null)
exception = _outermostExceptionCache.GetOutermostException(e.Exception);
// Handle exception
}
private static OutermostExceptionCache _outermostExceptionCache = new();
}
And for that you'll need the OutermostExceptionCache class:
public class OutermostExceptionCache
{
public void AddException(Exception ex)
{
if ((ex != null) && (ex is not TargetInvocationException))
{
Exception innermostException = GetInnermostException(ex);
lock (_syncRoot)
{
RemoveOldEntries();
_cache[innermostException] = new CacheEntry(ex);
}
}
}
public Exception GetOutermostException(Exception ex)
{
Exception innermostException = GetInnermostException(ex);
Exception outermostException = null;
lock (_syncRoot)
{
if (_cache.TryGetValue(innermostException, out CacheEntry entry))
{
outermostException = entry.Exception;
_cache.Remove(innermostException);
}
else
{
outermostException = ex;
}
}
return outermostException;
}
private void RemoveOldEntries()
{
DateTime now = DateTime.Now;
foreach (KeyValuePair<Exception, CacheEntry> pair in _cache)
{
TimeSpan timeSinceAdded = now - pair.Value.AddedTime;
if (timeSinceAdded.TotalMinutes > 3)
_cache.Remove(pair.Key);
}
}
private Exception GetInnermostException(Exception ex)
{
return ex.GetBaseException() ?? ex;
}
private readonly object _syncRoot = new();
private readonly Dictionary<Exception, CacheEntry> _cache = new();
private class CacheEntry
{
public CacheEntry(Exception ex)
{
Exception = ex;
AddedTime = DateTime.Now;
}
public Exception Exception { get; }
public DateTime AddedTime { get; }
}
}
The way this works is by watching every exception, as it is thrown, before the runtime even bubbles the exception up to the nearest catch block. Each time an exception is thrown, it is added to a cache, indexed by the innermost (i.e. base) exception. Therefore, when an exception is caught and a new exception is thrown, with the original one as its inner exception, the cache is updated with that outer exception. Then, when Application.ThreadException event handler is provided with the unwrapped, innermost, exception, the handler can look up the outermost one from the cache.
Note: Since even locally-caught exceptions will get added to the cache (and therefore never removed via a call to GetOutermostException), it timestamps each one and automatically ditches any that are older than 3 minutes. That's an arbitrary timeout which can be adjusted as needed. If you make the timeout too short, it could cause problems with debugging since it can cause the exception handling to revert to handling only the innermost exception if you pause the process too long in the debugger (after the exception is thrown but before it is handled).

Getting Access is denied exception when calling CompositionCapabilities.GetForCurrentView

I am getting the following exception when I call CompositionCapabilities.GetForCurrentView.
System.UnauthorizedAccessException: 'Access is denied. (Exception from
HRESULT: 0x80070005 (E_ACCESSDENIED))'
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
// Exception happens here.
var capabilities = CompositionCapabilities.GetForCurrentView();
}
The weird thing is the code compiles OK so I assume the API is available. Do I need to declare any capabilities in Package.appxmanifest?
You don't need to declare anything. The method is simply called too early.
So instead of calling it in the constructor, call it right after the Window is created -
protected override void OnWindowCreated(WindowCreatedEventArgs args)
{
if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 4))
{
var capabilities = CompositionCapabilities.GetForCurrentView();
var areEffectsSupported = capabilities.AreEffectsSupported();
var areEffectsFast = capabilities.AreEffectsFast();
}
base.OnWindowCreated(args);
}
Note you will want to add a check to see if that API is supported too, like in the code above.

C# - Create new exception without losing stack trace and inner exceptions?

I have a method that provides an Exception object and I want to log an error if anything happens in the error handler itself. Here is some pseudocode:
public override void OnError(Exception originalException)
{
try
{
// Do work...
}
catch (Exception e)
{
// How do I create this exception without losing the stack
// trace of e and preserving any inner exceptions that were
// in e at the same time including the details of
// originalException?
Exception newException = new Exception(e.Message, originalException);
Logger.Error("An error occurred", newException);
}
}
Basically, I am trying to combine originalException and e above into one Exception message to pass to a logger object. I suppose one option would be to create 2 separate log messages but it's not ideal.
You could use an AggregateException to wrap multiple exceptions:
public override void OnError(Exception originalException)
{
try
{
// Do work...
}
catch (Exception e)
{
var exs = new AggregateException(originalException, e);
Logger.Error("An error occurred", exs);
}
}
EDIT: If Logger doesn't record contents of the InnerException property (or InnerExceptions in this case) then seems the only option is multiple calls to Logger.Error.

Exception Unhandled by User Code

I have a member of a library being declared on the main page of my app:
private OpticalReaderLib.OpticalReaderTask _task = new OpticalReaderLib.OpticalReaderTask();
It works fine until I want to navigate back to this page at a different time. It brings up the error "An exception of type 'System.Exception' occurred in OpticalReaderLib.DLL but was not handled in user code".
Does anyone know why this is happening?
Thanks.
System.Exception is the base class for all Exceptions, so this is a very generic error message.
You could try logging more detail (e.g. exception.Message or exception.InnerException) about the exception that is thrown as part of your investigation. (via a try-catch statement).
It looks like you're initialising a field, where is this code being executed?
Update due to comment
As a temporary solution to discover the exception error.
private OpticalReaderLib.OpticalReaderTask _tempTask;
private OpticalReaderLib.OpticalReaderTask _task
{
get
{
//code to debug the error that is occuring
try
{
if (_tempTask == null)
_tempTask = new OpticalReaderLib.OpticalReaderTask();
else
return _tempTask;
}
catch (Exception exception)
{
//Log the exception detail here
}
}
}
protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e)
{
if (_task != null)
{
_task.Completed -= OpticalReaderTask_Completed;
_task.Dispose();
_task = null;
}
base.OnBackKeyPress(e);
}

Pass the exception from my class to my form

How can I get my exception from my class to show as a windows error ?
This is my class :
public class Editcap
{
private string _newFileName;
public void convertFileToLibpcap(string filePath)
{
FileInfo fileInfo = new FileInfo(filePath);
_newFileName = fileInfo.FullName.Replace(fileInfo.Extension, "_new") + ".pcap";
invokeProcess(WiresharkProcesses.Editcap, string.Format("{2}{0}{2} -F libpcap {2}{1}{2}", fileInfo.FullName, _newFileName, "\""));
deleteFile(filePath);
}
private void deleteFile(string filePath)
{
try
{
File.Delete(filePath);
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}
public string getNewFileName()
{
return _newFileName;
}
}
From my form:
The function creates new wireshark file with different extension and should delete the old file. If it fails to delete, I want to show the message error in pop up window
Editcap editpcap = new Editcap();
editpcap.convertFileToLibpcap(file.FullName);
You don't need to do anything, the exception will bubble up all on it's own; it's how they work.
Just add a Try/Catch around your calling code:
try
{
Editcap editpcap = new Editcap();
editpcap.convertFileToLibpcap(file.FullName);
}
catch(Exception e)
{
MessageBox.Show("There was an error deleting the file.");
}
Note that there is no need (and in fact some harm) to catching and then re-throwing a new exception in your deleteFile method. You're changing the type of the exception from the more specific and informative IOException to the less expressive Exception, and you're also taking out a lot of the stack trace information. It would be better to just remove that try/catch entirely from deleteFile. It also means you can change the calling code to catch IOException instead of the global Exception. By doing this you can potentially have different catch blocks for different exceptions, allowing you to treat them differently (perhaps crash with one type, log an error for a different one, show a message to the user for something else, etc.).

Categories

Resources