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...
Related
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.
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.
This popped up, when I was trying to find why the OnModeChanging handler wasn't being called when I called the ChangeMode event of my formview.
On the formview's ChangeMode method MSDN page , it is stated that it:
switches the FormView control to the specified data-entry mode
but also that:
the ModeChanged and ModeChanging events are not raised when this method is called
And in the ModeChanged and ModeChanging events pages, it says that they occur:
when the FormView control switches between edit, insert, and read-only mode
after/before the mode changes, respectively.
Can you explain it to me: when are the ModeChanged/ing events raised?
And, is there a way to force these events to be raised?
I think I know why now. I've found an answer in other forum, and though I didn't find the code of FormView, I've found a DetailsView implementation and I think in this case it might be similar.
Basically what I've understood of it, is that the ModeChanged/ing events are raised when command buttons are clicked (Cancel, Edit, Insert, New and Update), i.e. when one doesn't have direct control over these events, and when we use the ChangeMode method, we know that the mode has changed (or will be changed) and it would make no sense of raising an event..
DetailsView ChangeMode:
public void ChangeMode(DetailsViewMode newMode) {
Mode = newMode;
}
DetailsView command handlers:
private void HandleCancel() {
bool isBoundToDataSourceControl = IsBoundUsingDataSourceID;
DetailsViewModeEventArgs e = new DetailsViewModeEventArgs(DefaultMode, true);
OnModeChanging(e);
if (e.Cancel) {
return;
}
if (isBoundToDataSourceControl) {
Mode = e.NewMode;
OnModeChanged(EventArgs.Empty);
}
RequiresDataBinding = true;
}
private void HandleEdit() {
if (PageIndex < 0) {
return;
}
DetailsViewModeEventArgs e = new DetailsViewModeEventArgs(DetailsViewMode.Edit, false);
OnModeChanging(e);
if (e.Cancel) {
return;
}
if (IsBoundUsingDataSourceID) {
Mode = e.NewMode;
OnModeChanged(EventArgs.Empty);
}
RequiresDataBinding = true;
}
private bool HandleInsertCallback(int affectedRows, Exception ex) {
DetailsViewInsertedEventArgs dea = new DetailsViewInsertedEventArgs(affectedRows, ex);
dea.SetValues(_insertValues);
OnItemInserted(dea);
_insertValues = null;
if (ex != null && !dea.ExceptionHandled) {
if (PageIsValidAfterModelException()) {
return false;
}
dea.KeepInInsertMode = true;
}
if (!dea.KeepInInsertMode) {
DetailsViewModeEventArgs eMode = new DetailsViewModeEventArgs(DefaultMode, false);
OnModeChanging(eMode);
if (!eMode.Cancel) {
Mode = eMode.NewMode;
OnModeChanged(EventArgs.Empty);
RequiresDataBinding = true;
}
}
return true;
}
private void HandleNew() {
DetailsViewModeEventArgs e = new DetailsViewModeEventArgs(DetailsViewMode.Insert, false);
OnModeChanging(e);
if (e.Cancel) {
return;
}
if (IsBoundUsingDataSourceID) {
Mode = e.NewMode;
OnModeChanged(EventArgs.Empty);
}
RequiresDataBinding = true;
}
private bool HandleUpdateCallback(int affectedRows, Exception ex) {
DetailsViewUpdatedEventArgs dea = new DetailsViewUpdatedEventArgs(affectedRows, ex);
dea.SetOldValues(_updateOldValues);
dea.SetNewValues(_updateNewValues);
dea.SetKeys(_updateKeys);
OnItemUpdated(dea);
_updateKeys = null;
_updateOldValues = null;
_updateNewValues = null;
if (ex != null && !dea.ExceptionHandled) {
if (PageIsValidAfterModelException()) {
return false;
}
dea.KeepInEditMode = true;
}
if (!dea.KeepInEditMode) {
DetailsViewModeEventArgs eMode = new DetailsViewModeEventArgs(DefaultMode, false);
OnModeChanging(eMode);
if (!eMode.Cancel) {
Mode = eMode.NewMode;
OnModeChanged(EventArgs.Empty);
RequiresDataBinding = true;
}
}
return true;
}
With ChangeMode you are choosing that the control switch to one of it's modes.
When it starts to performing this task, the ModeChanging event is raised (to indicate that it's in progress) (optionally do something here).
Once that task is completed, it raises the ModeChanged event (to indicate that it's done) (optionally do something here).
[Updated]
I see your point. How could you consume the events if they don't get raised.
I'm going to guess at it, that they don't get raised initially because of nothing to do, just perform the changing of the mode.
In either case I guess, it's more of a state change than the raising of events.
[Updated]
I think what we are both saying is that if no one has subscribed to the event (i.e., no one is listening for it), there's no point in raising it.
Hey guys, I just wanted to verify what I'm doing is correct. It came to our attention that a Windows Service had a pretty serious memory leak. I was able to track it down to how Workflow was being called. I reworked it a bit to stop the memory leak, but I wanted to validate that the code is doing what I think it is. Note I do not know the first thing about Workflow, so I'm coming to you.
Basically, the code was executing the Workflow on a thread, but was not removing the handler to WorkflowRuntime.Terminated. I am trying to ensure that the Workflow is executed asynchronously. Here are the relevant portions of code:
Checking to ensure there is only once instance of the WorkflowRuntime:
private static void _CheckRuntimeInstance()
{
lock (_padlock)
{
if (_wfRuntime == null)
{
_wfRuntime = new WorkflowRuntime();
ManualWorkflowSchedulerService schedulerService = new ManualWorkflowSchedulerService();
_wfRuntime.AddService(schedulerService);
_wfRuntime.StartRuntime();
}
}
}
Inside a static method, creating the specific WorkflowInstance to run:
_CheckRuntimeInstance();
// create the instance
WorkflowInstance instance = _wfRuntime.CreateWorkflow(typeof(WorkflowType),parameters);
instance.Start();
Guid instanceId = instance.InstanceId;
ThreadPool.QueueUserWorkItem(CallbackMethod, instanceId);
Thread callback method:
private static void DeviceLocationAssignmentCallback(Object state)
{
Guid instanceId = (Guid)state;
EventHandler<WorkflowTerminatedEventArgs> workflowTerminatedHandler = null;
EventHandler<WorkflowCompletedEventArgs> workflowCompletedHandler = null;
workflowTerminatedHandler = delegate(object sender, WorkflowTerminatedEventArgs e)
{
if (instanceId == e.WorkflowInstance.InstanceId)
{
// Remove event registration.
_wfRuntime.WorkflowTerminated -= workflowTerminatedHandler;
_wfRuntime.WorkflowCompleted -= workflowCompletedHandler;
if (e.Exception != null)
{
// Log error.
}
}
};
_wfRuntime.WorkflowTerminated += workflowTerminatedHandler;
workflowCompletedHandler = delegate(object sender, WorkflowCompletedEventArgs e)
{
if (instanceId == e.WorkflowInstance.InstanceId)
{
// Remove event registrations.
_wfRuntime.WorkflowTerminated -= workflowTerminatedHandler;
_wfRuntime.WorkflowCompleted -= workflowCompletedHandler;
}
};
_wfRuntime.WorkflowCompleted += workflowCompletedHandler;
_wfRuntime.GetService<ManualWorkflowSchedulerService>().RunWorkflow(instanceId);
}
EDIT: Changed the title of the post to get more views.
Basic premise:
I have a Room which publishes an event when an Avatar "enters" to all Avatars within the Room. When an Avatar leaves the Room I want it to remove all subscriptions for that room.
How can I best unsubscribe the Avatar from all events in the room before I add the Avatar to a new Room and subscribe to the new Room's events?
The code goes something like this:
class Room
{
public event EventHandler<EnterRoomEventArgs> AvatarEntersRoom;
public event EvnetHandler<LeaveRoomEventArgs> AvatarLeavesRoom;
public event EventHandler<AnotherOfManyEventArgs> AnotherOfManayAvatarEvents;
public void AddPlayer(Avatar theAvatar)
{
AvatarEntersRoom(this, new EnterRoomEventArgs());
AvatarEntersRoom += new EventHandler<EnterRoomEventArgs>(theAvatar.HandleAvatarEntersRoom);
AvatarLeavesRoom += new EventHandler<EnterRoomEventArgs>(theAvatar.HandleAvatarEntersRoom);
AnotherOfManayAvatarEvents += new EventHandler<EnterRoomEventArgs>(theAvatar.HandleAvatarEntersRoom);
}
}
class Avatar
{
public void HandleAvatarEntersRoom(object sender, EnterRoomEventArgs e)
{
Log.Write("avatar has entered the room");
}
public void HandleAvatarLeaveRoom(object sender, LeaveRoomEventArgs e)
{
Log.Write("avatar has left room");
}
public void HandleAnotherOfManayAvatarEvents(object sender, AnotherOfManyEventArgs e)
{
Log.Write("another avatar event has occurred");
}
}
Each delegate has a method named GetInvocationList() that returns all the actual delegates that have been registered. So, assuming the delegate Type (or event) is named say MyDelegate, and the handler instance variable is named myDlgHandler, you can write:
Delegate[] clientList = myDlgHandler.GetInvocationList();
foreach (var d in clientList)
myDlgHandler -= (d as MyDelegate);
to cover the case where it might be null,
if(myDlgHandler != null)
foreach (var d in myDlgHandler.GetInvocationList())
myDlgHandler -= (d as MyDelegate);
Probably the simplest way to accomplish this would be to store all of your subscribed events for an avatar in an ArrayList of delegates to the events.
When the avatar leaves the room, simply loop through the list of delegates performing a standard remove (-=).
Is there anything wrong with a standard remove?
public void RemovePlayer(Avatar theAvatar) {
AvatarEntersRoom -= new EventHandler<EnterRoomEventArgs>(theAvatar.HandleAvatarEntersRoom);
}
EDIT
Based on your update it appears that you want code that will remove a particular object from all events on a particular class. There is no realistic way to accomplish this goal. It's often a bit verbose but the best way is to individually add/remove a particular object method combo from every event.
The only way to get close to this functionality is to use reflection. You could reflectively grab all events on your class and then do some magic to find all instances of a class within the event chain. This will only be a partial solution though because it will ignore such things as a lambda expression event handlers.
you can run on all the event subscribers with:
_Event.GetInvocationList()
and remove each event handler.
Delegate[] subscribers = myEvent.GetInvocationList();
for(int i = 0; i < subscribers.Length; i++)
{
myEvent -= subscribers[i] as yourDelegateType;
}
What I'd like to do is in debug (do not think that this is good performance wise for release and one should catch it during development) throw exceptions when a class's events are not unsubscribed, this is the method that I use:
#if DEBUG
private void CheckEventHasNoSubscribers(Delegate eventDelegate)
{
if (eventDelegate != null)
if (eventDelegate.GetInvocationList().Length != 0)
{
var subscriberCount = eventDelegate.GetInvocationList().Length;
// determine the consumers of this event
var subscribers = new StringBuilder();
foreach (var del in eventDelegate.GetInvocationList())
subscribers.AppendLine((subscribers.Length != 0 ? ", " : "") + del.Target);
// throw an exception listing all current subscription that would hinder GC on them!
throw new Exception(
$"Event:{eventDelegate.Method.Name} still has {subscriberCount} subscribers, with the following targets [{subscribers}]");
}
}
#endif
The in my Dispose of the item that owns the delegate, or any other location where you're workflow supposed to release the object I would call it like this.
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (_orderCacheLock != null)
_orderCacheLock.Dispose();
if(_SettingTradeTimeOut!=null)
_SettingTradeTimeOut.Dispose();
_orderCacheLock = null;
#if DEBUG
CheckEventHasNoSubscribers(OnIsProfitable);
CheckEventHasNoSubscribers(OnPropertyChanged);
#endif
disposedValue = true;
}
}
It's then super easy to find the subscribers to these "orphaned" events and fix the code
ps:
An Extension of this "practice pattern" looks like this.
public static void CheckEventHasNoSubscribers(this Delegate eventDelegate)
{
if (eventDelegate != null)
if (eventDelegate.GetInvocationList().Length != 0)
{
var subscriberCount = eventDelegate.GetInvocationList().Length;
// determine the consumers of this event
var subscribers = new StringBuilder();
foreach (var del in eventDelegate.GetInvocationList())
subscribers.AppendLine((subscribers.Length != 0 ? ", " : "") + del.Target);
// point to the missing un-subscribed events
throw new Exception( $"Event:{eventDelegate.Method.Name} still has {subscriberCount} subscribers, with the following targets [{subscribers}]");
}
}