My Xamarin Forms application freezes whenever any exception occurs. I have created an exception handler as well to handle the exceptions but whenever I face an exception from the API end it automatically crashes the application. This only happens in Android and not in iOS.
Here's the code for the exception handler -
public async Task<bool> HandleExceptionAsync(Exception exception)
{
if (exception.GetType().Name == nameof(UnauthorizedException) ||
exception.Message.ToLowerInvariant().Contains("refresh token has expired") ||
exception.Message.ToLowerInvariant().Contains("invalid refresh token"))
{
//await loginFacade.LogoutAsync(System.Threading.CancellationToken.None);
}
else if (exception.GetType().Name == nameof(NoInternetConnectionException))
{
HandleNoInternetException();
}
else if (CheckIfLoggable(exception))
{
Log.Error(exception, string.Format(Common.Constants.LoggingFormats.ExceptionWithUserDetails, _userContext.Serialize(), exception));
}
else
{
#if !DEBUG
try
{
//await emailManager.SendFeedbackEmail(exception);
}
catch (Exception ex)
{
}
#endif
}
return true;
}
private bool CheckIfLoggable(Exception exception)
{
return exception.Message.ToLowerInvariant().Contains("not found".ToLowerInvariant()) ||
exception.Message.ToLowerInvariant().Contains("BadInternetConnectionException".ToLowerInvariant()) ||
exception.GetType() == typeof(NullReferenceException) ||
exception.GetType() == typeof(OperationCanceledException);
}
private void HandleNoInternetException()
{
if (Xamarin.Essentials.Connectivity.NetworkAccess != Xamarin.Essentials.NetworkAccess.Internet)
{
}
Xamarin.Essentials.Connectivity.ConnectivityChanged += Connectivity_ConnectivityChanged;
}
private void Connectivity_ConnectivityChanged(object sender, Xamarin.Essentials.ConnectivityChangedEventArgs e)
{
if (Xamarin.Essentials.Connectivity.NetworkAccess != Xamarin.Essentials.NetworkAccess.Internet)
{
}
else
{
}
}
I have tried logging it and then removed the buggy codes which caused the exception.
It is not possible to recover from exceptions that happen during UI drawing or events, using a global exception handler.
Use try-catch in every UI class, both in constructor, and in all event handlers (e.g. a button handler).
IMHO it’s a major PITA of Xamarin. But that is the way it is.
Even then, low level exceptions inside Xamarin code or graphics code can
crash app.
Isolate the problem manually. Print statements, breakpoints, commenting out different code. All are sometimes necessary debugging.
Related
We are trying to redirect focus from an ancestor control to a child using GettingFocus but we are seeing ArgumentExceptions getting thrown on some cases.
The cases I've noticed so far:
NewFocusedElement has ActualHeight or ActualWidth of 0
NewFocusedElement can't get focus
Is there anyway to guard args.NewFocusedElement = element; ?
We are catching exceptions as a temporary solution.
Here is the code:
private void OnGettingFocus(UIElement sender, GettingFocusEventArgs args)
{
if ((args.Direction == FocusNavigationDirection.Up || args.Direction == FocusNavigationDirection.Down)
&& (sender == this) && (DefaultFocusButton != null))
// Add extra guard here so that the try / catch is no longer needed
{
try
{
args.NewFocusedElement = DefaultFocusButton;
}
catch (ArgumentException e)
{
// This focus redirect causes an exception when the element can't receive focus
}
}
}
It appears that exceptions that occur in a property's Set method do not bubble up to the Application's ThreadException event.
We use that event along with the AppDomain.CurrentDomain.UnhandledException event to catch any unexpected mishaps that occur in the application. The exception details are written to a log so our support and development team can better evaluate the issue. Sadly it looks like this Catch All falls short in this particular case.
There are several similar questions on StackOverflow, But no answer addresses the issue of the global exception handling not catching the exception. I already know we can fix it so no exception occurs. We could add a TryCatch block to every setter. We could add the BindingComplete event to each databinding and get the exception that way. But all of that defeats the purpose of having global exception handling which works perfectly in any other case.
How do I 'globally' catch exceptions thrown in object instances
Data Binding and throwing exception in setter
Neither Application.ThreadException nor AppDomain.CurrentDomain.UnhandledException are respected
To reproduce the issue, simply create a form with a text box, bind the text box to a property and throw an exception in the property's set method. Add the ThreadException and UnhandledException events to the program.cs. Run the program and type in the text box to trigger the exception. The debugger will break on the exception, press Continue (F5) the let the exception bubble up as it would outside of the debugger. Any normal exception would end up in those events, but this one does not.
Form1.cs
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
textBox1.DataBindings.Add("Text", this, "TestValue", true, DataSourceUpdateMode.OnPropertyChanged);
}
private string _TestValue = "";
public string TestValue
{
get{return _TestValue;}
set
{
_TestValue = value;
throw new Exception("Something bad happened in here");
}
}
Program.cs
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
Application.ThreadException += ThreadExceptionHandler;
AppDomain.CurrentDomain.UnhandledException += new System.UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
Application.Run(new Form1());
}
private static void ThreadExceptionHandler(object sender, System.Threading.ThreadExceptionEventArgs args)
{
try
{
//RR.Common.ErrorLogRt.WriteError(args.Exception.StackTrace.ToString(), args.Exception.Message.ToString(), true);
MessageBox.Show(args.Exception.Message);
}
catch
{
MessageBox.Show("Error writing to exception log. This program will now terminate abnormally.");
Application.Exit();
}
}
static void CurrentDomain_UnhandledException(object sender, System.UnhandledExceptionEventArgs e)
{
try
{
if (e != null)
{
Exception ex = e.ExceptionObject as Exception;
//RR.Common.ErrorLogRt.WriteError(ex.StackTrace.ToString(), ex.Message.ToString(), true);
MessageBox.Show(ex.Message);
}
else
{
MessageBox.Show("Unhandled Error: " + e.ToString());
}
}
catch
{
MessageBox.Show("Error writing to exception log. This program will now terminate abnormally.");
Application.Exit();
}
}
static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
{
try
{
if (e != null && e.Exception != null && e.Exception.InnerException != null)
{
//The unobserved exception is always the same, The actual exception that cause it will be the inner exception.
Exception ex = e.Exception.InnerException;
MessageBox.Show(e.Exception.Message);
//RR.Common.ErrorLogRt.WriteError(ex.StackTrace.ToString(), ex.Message.ToString(), true);
}
else
{
MessageBox.Show("Unhandled Error: " + e.ToString());
}
}
catch
{
MessageBox.Show("Error writing to exception log. This program will now terminate abnormally.");
Application.Exit();
}
}
}
Remarks from Binding.FormattingEnabled Property
Setting this property to true also enables error-handling behavior and
causes the BindingComplete event to be raised. The handler of this
event can take the appropriate action, based on the success, error, or
exceptions in the binding process, by examining the
BindingCompleteState property of the BindingCompleteEventArgs
parameter.
The code involved
internal bool PushData(bool force)
{
Exception ex = null;
if (!force && this.ControlUpdateMode == ControlUpdateMode.Never)
{
return false;
}
if (this.inPushOrPull && this.formattingEnabled)
{
return false;
}
this.inPushOrPull = true;
try
{
if (this.IsBinding)
{
object value = this.bindToObject.GetValue();
object propValue = this.FormatObject(value);
this.SetPropValue(propValue);
this.modified = false;
}
else
{
this.SetPropValue(null);
}
}
catch (Exception ex2)
{
ex = ex2;
if (!this.FormattingEnabled)
{
throw;
}
}
finally
{
this.inPushOrPull = false;
}
if (this.FormattingEnabled)
{
BindingCompleteEventArgs bindingCompleteEventArgs = this.CreateBindingCompleteEventArgs(BindingCompleteContext.ControlUpdate, ex);
this.OnBindingComplete(bindingCompleteEventArgs);
return bindingCompleteEventArgs.Cancel;
}
return false;
}
As you can see, passing 4th parameter as true: DataBindings.Add("Text", this, "TestValue", true is responsible for catching the exception inside PushData and passing it to BindingComplete event. There is no other way (except AppDomain.CurrentDomain.FirstChanceException) to find the exception anywhere else than in BindingComplete if formatting is enabled.
I know a solution exists for WPF, but I could not make it work for winforms.
It seems like the exception is somehow trapped by the framework and I cant find the right trace to listen to.
What you could do though is handle first-chance exceptions (beware that this will probably make you catch way more than what you want).
This will show a message box with "Something bad happened in here" in your example:
AppDomain.CurrentDomain.FirstChanceException += OnFirstChanceException;
//...
private static void OnFirstChanceException(object sender, FirstChanceExceptionEventArgs firstChanceExceptionEventArgs)
{
if(firstChanceExceptionEventArgs.Exception is TargetInvocationException)
{
if(firstChanceExceptionEventArgs.Exception.InnerException != null)
MessageBox.Show(firstChanceExceptionEventArgs.Exception.InnerException.Message);
else
MessageBox.Show(firstChanceExceptionEventArgs.Exception.Message);
}
}
If you are curious, this is the WPF solution I was talking about:
https://web.archive.org/web/20140809204919/https://www.tech.pro/tutorial/940/wpf-snippet-detecting-binding-errors
It looks like it has been reported as a defect to Microsoft, who have closed it as 'Won't Fix':
ReflectPropertyDescriptor.SetValue does not preserve stack trace
in the Microsoft Reference Source, the code for the SetValue method (lines 1085 to 1173) contains a block with a structure like this:
try // <--- This ...
{
try
{
// Code to invoke SetMethod.
}
catch(Exception)
{
// Code to rewind.
// Code to throw inner or rethrow.
}
}
finally // <--- ... and this consume the exception before you can handle it.
{
// Code to raise change notification.
}
The outer try ... finally block is consuming the (second chance) exception that is preventing you from handling it in your code. The dbugger can still catch the (first chance) exception, but it wont get out of the SetValue method.
I am maintaining a program with customer informations. It consists of many forms that each show some relevant info from the database. This error is in a single form after doing the following
Open the customer search form
View random customer A info in the customerinfo form
open the crm form and it automatically shows customer A. Then add a file to him via draganddrop.
Close the last two forms and select random customer B and do the same.
Close the last two forms and select customer A and add a new file. Error!!!
Here is the code that fails:
private void FireFileCountChanged() {
if (FileCountChanged != null)
BeginInvoke(new DeferEvent(FireFileCountChangedDeferred), 2); // FAILS
"An unhandled exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll
Additional information: Invoke or BeginInvoke cannot be called on a control until the window handle has been created."
I tried adding the following:
private void FireFileCountChanged() {
if (FileCountChanged != null && this.Handle != null) // CHANGED AND FAILS.
BeginInvoke(new DeferEvent(FireFileCountChangedDeferred), 2);
}
But the this.handle gives:
'this.Handle' threw an exception of type 'System.ObjectDisposedException' and
"Cannot access a disposed object.\r\nObject name: 'AttachmentsControl'."
Then I added a timeout of 10 seconds as the first line in the method, but the handle is still not created. Has the handle somehow been disposed when one of the window were closed? And what can be done about this? Any help is appreciated. I'm kind of stuck.
private void FireFileCountChangedDeferred(int repostCount) {
if (FileCountChanged != null) {
if (repostCount > 0) {
//black magic is somehow involved in getting this event to fire *after* the filewatcher reports the change.
System.Threading.Thread.Sleep(10);
BeginInvoke(new DeferEvent(FireFileCountChangedDeferred), repostCount - 1);
} else
FileCountChanged(this, null);
}
}
private void CopyFiles(string[] files, bool reload) {
if (CreatePath()) {
foreach (string src in files) {
try {
string dest = MakeSafeFilename(src);
File.Copy(src, dest);
FireFileCountChanged();
} catch (Exception ex) {
//Util.Print("Copy ex: {0}", ex.Message);
ErrMsg("Error while copying:{1}{0}", ex.Message, environment.NewLine);
}
}
}
}
private void Lstv_DragDrop(object sender, DragEventArgs ea) {
if (m_CanAdd) {
string[] files = GetDraggedFiles(ea);
if (files != null)
CopyFiles(files, true);
else if (OutlookDataObject.HoldsOutlookData(ea) && CreatePath()) {
try {
OutlookDataObject.CopyDroppedFiles(ea, m_Path, OutlookFilenameCallback);
} catch (Exception ex) {
//Util.Print("Copy ex: {0}", ex.Message);
ErrMsg("Error copying from Outlook:{1}{0}", ex.Message, Environment.NewLine);
}
}
}
}
Solution
private void FireFileCountChanged() {
while (!this.IsHandleCreated) // added
System.Threading.Thread.Sleep(100); //added
if (FileCountChanged != null)
BeginInvoke(new DeferEvent(FireFileCountChangedDeferred), 2);
You need to check the IsHandleCreated property, not compare the Handle to null. Reading the Handle property is considered a UI operation itself.
private void FireFileCountChanged() {
if (FileCountChanged != null && this.IsHandleCreated)
BeginInvoke(new DeferEvent(FireFileCountChangedDeferred), 2);
}
However, based on the complex steps that you need to take to reproduce the bug, I suspect that there are some form instance re-use issues or other more complex issues at play here, and it isn't just a matter of making this call to BeginInvoke work.
I have an application where I load an image dynamically. Let's say, that image doesn't exists and we want to notify the user and then exit. In my main loop, I have exception handling which works just fine when I'm reading files with StreamReader. However, if I am throwing exception from another function, the app just crashes and in the error report I see thrown exception (IOException). To get an idea of the app:
public MainWindow()
{
try {
InitializeComponent();
Load(myFile);
} catch (IOException e) {
MessageBox.Show("Opening failure.");
Application.Current.Shutdown();
}
}
public void Load(string imgPath)
{
string tmpStr;
string[] tmp;
using (StreamReader sr = new StreamReader("myFile.txt", System.Text.Encoding.Default)) {
while ((tmpStr = sr.ReadLine()) != null) {
tmp = tmpStr.Split(' ');
...
}
}
}
private void Grid_Click(object sender, RoutedEventArgs e)
{
...
if (!File.Exists(myFile)) {
throw new IOException("File doesnt exist");
}
...
}
BUT, if I put try-catch block inside Grid_Click, it catches that exception.
The exception that is thrown in Grid-Click would not be caught by the catch statement in the MainWindow() method by design, since the method call to Grid_Click is not inside that try block.
The Grid_Click method is called when the click event is fired, which is at an asynchronous time. Only IOExceptions thrown inside the try block in MainWindow will be caught by that catch statement you have listed above.
What you call 'main loop' is just a constructor. The code does not execute under it's control.
I have a class that fires events delegating to DispatcherObject targets which is marshalling fine, though I have the problem where if a handler throws an exception in it's event code, what should I do? I don't want to prevent other listeners from handling the event. I'm looking for advice on how to handle such a problem.
Given an abstract base class with a Saved event, my pattern would be as follows:
public event EventHandler<EventArgs> Saved;
public void Save() {
try {
OnSave();
} catch (Exception) {
// What should I do here? throwing prevents subsequent handlers,
// while catching gobbles up the exception. Should this be in OnSave()?
}
}
protected virtual void OnSave() {
EventHandler<EventArgs> evt = Saved;
if (evt != null) {
var args = EventArgs.Empty;
foreach (var handler in evt.GetInvocationList()) {
var target = handler.Target as DispatcherObject;
if (target == null || target.CheckAccess()) {
var h = handler as EventHandler<EventArgs>;
if (h != null) h(this, args);
} else {
target.Dispatcher.Invoke(handler, this, args);
}
}
}
}
I've thought about building an exception that holds all exceptions like an ArrayException or something, but this doesn't seem right.
Advice on what to do here would be very much appreciated.
UPDATE: I thank both Daniel and Henrik for your answers, If I could mark both as answered I would, I've decided to go with handling the event as I really don't want it going completely unnoticed, my final solution is as follows (for others looking for the solution).
public event EventHandler<EventArgs> Saved;
public void Save() {
OnSave();
}
protected virtual void OnSave() {
EventHandler<EventArgs> evt = Saved;
if (evt != null) {
var args = EventArgs.Empty;
var handlers = evt.GetInvocationList();
var exceptions = new Queue<Exception>(handlers.Length);
foreach (var handler in handlers) {
try {
var target = handler.Target as DispatcherObject;
if (target == null || target.CheckAccess()) {
var h = handler as EventHandler<EventArgs>;
if (h != null) h(this, args);
} else {
target.Dispatcher.Invoke(handler, this, args);
}
} catch (Exception ex) {
exceptions.Enqueue(ex);
}
}
if (exceptions.Count == 1) {
var ex = exceptions.Peek();
throw new Exception(ex.Message, ex);
}
if (exceptions.Count > 0) {
throw new AggregateException(exceptions);
}
}
}
No need to create your own ArrayException. You can just use System.AggregateException for wrapping multiple exceptions.
From your code, it seems that you should wrap the invoking in the loop into the try/catch, and gobble the exception there.
I don't see how the OnSave caller could benefit from having even the single exception from all the event handlers. Since you have multiple event handles, and from you architecture sketch it seems that every one does something completely different (since each must be called), exception space is so heterogeneous so the caller won't be able to deal with it anyway.
So the question could be: how do I deal with multiple event exceptions on the higher level?
Can you try to correct and re-run OnSave? Will you have to omit calls that succeeded? And so on...