Prevent necessity of InvokeRequired in event handler - c#

I've been writing an API that facilitates communication with a serial port. I'm doing some refactoring and general cleanup and was wondering if there's a way to avoid the following issue.
The main class in the API has the capability to constantly read from the port and raise an event containing a value when the read bytes match a particular regex. The process of reading and parsing occurs on another thread. The event contains the value as an argument (string) and because it's being raised from another thread, a client attempting to directly assign the value to, say, the Text property of a control causes a cross-thread exception unless the handler has the proper Invoke code.
I understand why this happens, and when I put the proper invocation code in my test client's event handler, all is well; my question is whether or not there's anything I can do in the API code itself such that clients don't have to worry about it.
Essentially, I'd like to turn this:
void PortAdapter_ValueChanged(Command command, string value)
{
if (this.InvokeRequired)
{
Invoke(new MethodInvoker(() =>
{
receivedTextBox.Text = value;
}));
}
else
{
receivedTextBox.Text = value;
}
}
into simply this:
void PortAdapter_ValueChanged(Command command, string value)
{
receivedTextBox.Text = value;
}

Well there is a common pattern for that used many places in .Net framework itself. For example BackgroundWorker uses this model.
For that you'll take a SynchronizationContext as a parameter for your API, in this case I assume it is PortAdapter.
When raising an event, you raise the event in given SynchronizationContext using SynchronizationContext.Post or SynchronizationContext.Send. Former is asynchronous and latter is synchronous.
So, when client code creating a instance of your PortAdapter, it passes WindowsFormsSynchronizationContext instance as parameter. Which means that PortAdapter will raise the event in given synchronization context and that also means that you don't need a InvokeRequired or Invoke calls.
public class PortAdapter
{
public event EventHandler SomethingHappened;
private readonly SynchronizationContext context;
public PortAdapter(SynchronizationContext context)
{
this.context = context ?? new SynchronizationContext();//If no context use thread pool
}
private void DoSomethingInteresting()
{
//Do something
EventHandler handler = SomethingHappened;
if (handler != null)
{
//Raise the event in client's context so that client doesn't needs Invoke
context.Post(x => handler(this, EventArgs.Empty), null);
}
}
}
Client code:
PortAdapter adpater = new PortAdapter(SynchronizationContext.Current);
...
It is very important to create instance of PortAdapter in UI thread, otherwise SynchronizationContext.Current will be null and hence events will be still raised in ThreadPool thread.
More about SynchronizationContext here.

TBH, the approach with checking for InvokeRequired is fine and flexible.
But if you like, you can have all events in your application UI-safe. For this either all classes have to have invocation control registered
public class SomeClassWithEvent
{
private static Control _invoke = null;
public static void SetInvoke(Control control)
{
_invoke = control;
}
public event Action SomeEvent;
public OnSomeEvent()
{
// this event will be invoked in UI thread
if (_invoke != null && _invoke.IsHandleCreated && SomeEvent != null)
_invoke.BeginInvoke(SomeEvent);
}
}
// somewhere you have to register
SomeClassWithEvent.SetInvoke(mainWindow);
// and mayhaps unregister
SomeClassWithEvent.SetInvoke(null);
or have that invocation control exposed, to example:
// application class
public static class App
{
// will be set by main window and will be used even risers to invoke event
public static MainWindow {get; set;}
}
You will have difficulties if event occur when no handle is created or control registered.

You can trigger the event in the UI Thread, this way the event handler (if any) will already be in the UI thread.
public class PortAdapter
{
public event EventHandler<string> ValueChanged;
protected virtual void OnValueChanged(string e)
{
var handler = ValueChanged;
if (handler != null)
{
RunInUiThread(() => handler(this, e));
}
}
private void RunInUiThread(Action action)
{
if (InvokeRequired)
{
Invoke(action);
}
else
{
action.Invoke();
}
}
}
However this is not good design because you don't know if an handler will perform UI interaction.

Related

Listen for event and invoke callback, based on specification?

I am currently building out a custom task manager and I'm wondering if it's possible to tell the task manager to listen for a specific event (OnSomething below), and then invoke a callback method when the task raises that event. However, mentally I can't see how it's possible to listen for an event that doesn't exist at the base class level. For example, I have a base class that contains basic information about the task called CustomTask:
public abstract class CustomTask {
public bool IsRunning { get; private set; } = false;
public void Start() {
IsRunning = true;
DoSomething();
IsRunning = false;
}
protected abstract void DoSomething();
}
For the sake of SO readers, I've simplified the definition, but you get the gist of it. It contains basic details, a few methods for starting and canceling, provides basic state management (simplified IsRunning here), etc.
I then have custom tasks that derive from CustomTask, in this case, let's focus on a sample task called CustomTaskA. It contains a definition for an event called OnSomething, which someone, somewhere may want to listen for:
public sealed class CustomTaskA : CustomTask {
protected override void DoSomething() => RaiseOnSomething(this, new EventArgs());
public event EventHandler<EventArgs> OnSomething;
private void RaiseOnSomething(object sender, EventArgs e) => OnSomething?.Invoke(sender, e);
}
Now, the CustomTaskManager registers tasks, tracks them via Guid, manages them and more, but for simplicity:
public sealed class CustomTaskManager {
// Singleton setup.
private static CustomTaskManager _instance = new CustomTaskManager();
public static CustomTaskManager Instance {
get {
// Simplified for SO.
if (_instance == null)
_instance = new CustomTaskManager();
return;
}
}
// Collection of tasks.
private Dictionary<Guid, CustomTask> _tasks = new Dictionary<Guid, CustomTask>();
// Register and start a task.
public bool TryRegisterAndStartTask(CustomTask task, out Guid taskId) {
taskId = Guid.Empty;
try {
// Register task.
taskId = Guid.NewGuid();
_tasks.Add(taskId, task);
// Listen for events.
// Start task.
task.Start();
} catch (Exception e) {
// Log exception.
}
return false;
}
}
When registering and starting a task, I'd like to tell the task manager I want to listen for OnSomething, and if OnSomething is invoked, I want the task manager to call a method OnSomethingWasRaised. For example:
TaskManager.Instance.TryRegisterAndStartTask(task, out Guid taskId, task.OnSomething, OnSomethingWasRaised);
private static void OnSomethingWasRaised(object sender, EventArgs e) {
Console.WriteLine("Woohoo!");
}
I know the specifying and invoking a callback method is entirely possible, and listening for events is plausible with reflection.
Is there a way (with or without using reflection) to listen for a specified event defined on a derived object and then invoke a specified callback method?
NOTE: Please excuse any syntactical errors as I hand-typed the snippets to keep them minimal.
Problem with (proposed) approach like this:
TryRegisterAndStartTask(task, out Guid taskId, task.OnSomething, OnSomethingWasRaised);
is that you cannot pass event as argument, or store it in variable, because event is just a set of two methods (add and remove), just like property is a set of two methods get and set.
You can of course change event to "raw" delegate:
public EventHandler<EventArgs> OnSomething;
This one you can pass by reference:
public bool TryRegisterAndStartTask(CustomTask task, ref EventHandler<EventArgs> del, EventHandler<EventArgs> sub, out Guid taskId) {
taskId = Guid.Empty;
// subscribe
del += sub;
...
}
CustomTaskManager.Instance.TryRegisterAndStartTask(task, ref task.OnSomething, OnSomethingWasRaised, out var taskId);
But that's usually not a good idea, since you are losing private scope of events - with events one can only add\remove delegates, with raw delegate anyone can do anything, like invoking or setting to null.
If regular event stays - that means reflection is the only way to achieve your goal, and even worse - you'll have to reference to the event you want to subscribe to by string name, not by an actual reference, though you can use nameof(task.OnSomething). Then, you are losing compile time validation of subscription delegate type. Say you want to subscribe to event Action Something but passing Func<string> delegate there. It will compile fine with reflection approach, and fail only at runtime.
Still if you insist that will look something like this:
public bool TryRegisterAndStartTask(CustomTask task, string eventName, Delegate sub, out Guid taskId) {
taskId = Guid.Empty;
// subscribe
var ev = task.GetType().GetEvent(eventName, BindingFlags.Public | BindingFlags.Instance);
var addMethod = ev.GetAddMethod(); // this can be null or private by the way
addMethod.Invoke(task, new [] {sub});
...
}
And called like this:
var task = new CustomTaskA();
EventHandler<EventArgs> handler = OnSomethingWasRaised;
CustomTaskManager.Instance.TryRegisterAndStartTask(task, nameof(task.OnSomething), handler, out var taskId);
Ugly, unsafe, and not worth it in your scenario, in my opinion.

Will several bindings of static/global Event handler to Events in a class, lead to a thread/memory leak?

I have a class with EventHandler bindings at the constructor, that will be instantiated thousand times within application lifecycle. The question is: Will this approach leads to memory/thread leaks?
I did this way (code below), because I need to be notified every time SomeMethod() runs, whatever instance run it. Foo class (the publisher) will be short-lived, however the handlers will live until the application closes.
I ask this because, when working with Windows Forms, each form can hold several event handlers/delegates and everything is fine because all those delegates are inside the form and will be disposed when the form closes. But how about static event handlers/delegates, that could be even on separate projects?
Will I need to write a destructor to detach those event handlers?
Should I go with Weak Event Pattern?
Restriction: I must do this with .NET 3.5. I know I could do this with TPL, setting a "Fire and Forget" Task.
Thank you in advance.
Code:
public class Foo
{
public event EventHandler SomeEvent;
public Foo()
{
SomeEvent += FooHandlers.Foo_SomeEvent1;
SomeEvent += FooHandlers.Foo_SomeEvent2;
}
public void RaiseEvents(EventHandler evt, EventArgs args)
{
var eventObj = evt;
var listeners = eventObj.GetInvocationList();
foreach (var listener in listeners)
{
var method = (EventHandler)listener;
ThreadPool.QueueUserWorkItem(callBack => method(this, args));
// Handlers will do a lot of things, so I don't want
// them blocking the Main thread
}
}
public void SomeMethod()
{
// do something here
RaiseEvents(SomeEvent, new EventArgs());
}
}
public static class FooHandlers
{
public static void Foo_SomeEvent1(object sender, EventArgs e)
{
//do something here
}
public static void Foo_SomeEvent2(object sender, EventArgs e)
{
//do something different here
}
}
Since your handlers are static methods the delegate you're adding to the event doesn't have an object instance, so there is no object instance being kept alive for the duration of the object with the event.
And even if you did use an object instance to attach the handler, it wouldn't be a problem, because the object with the event is short lived. The only time there is a problem is when the object with the event is long lived, and the object that has a handler to itself assigned is short lived, and consumes a lot of resources to keep alive.

UIAutomation - unbinding event in event handler

I am using the C wrapper of UIAutomation to listen for events.
Specifically, I'm looking for a focus event. When that focus event happens, I'm simply logging to the console, and unbinding the event.
Problem is - the program seems to stall/"die" on the automation.RemoveAllEventHandlers() call. The line below that never gets executed (doesn't print to console, breakpoint doesn't get hit.)
My guess is this is a threading issue - automationis created on a thread, but the event gets called on a different thread, and a big issue ensues. Is this the problem? If so/if not - what is and how do I fix it?
Below is the code:
public class FocusListener
{
private readonly CUIAutomation _automation;
public FocusListener()
{
_automation = new CUIAutomation();
_automation.AddFocusChangedEventHandler(null, new FocusChangeHandler(this));
Console.WriteLine("Added a focus event!");
}
public void On_WindowClicked()
{
Console.WriteLine("Window clicked!");
_automation.RemoveAllEventHandlers(); // program seems to die right here..
Console.WriteLine("Focus event removed"); // this line never gets executed..
}
}
public class FocusChangeHandler : IUIAutomationFocusChangedEventHandler
{
private readonly FocusListener _listener;
public FocusChangeHandler(FocusListener listener)
{
_listener = listener;
}
public void HandleFocusChangedEvent(IUIAutomationElement sender)
{
if (sender.CurrentControlType == UIA_ControlTypeIds.UIA_WindowControlTypeId)
{
_listener.On_WindowClicked();
}
}
}
According to: https://msdn.microsoft.com/en-us/library/windows/desktop/ee671692%28v=vs.85%29.aspx
It is safe to make UI Automation calls in a UI Automation event handler, >because the event handler is always called on a non-UI thread. However, when >subscribing to events that may originate from your client application UI, you >must make the call to IUIAutomation::AddAutomationEventHandler, or a related >method, on a non-UI thread (which should also be an MTA thread). Remove event >handlers on the same thread.

Calling Invoke/BeginInvoke from a thread

I have a C# 2.0 application with a form that uses a class that contains a thread.
In the thread function, rather than call the event handler directly, it is invoked. The effect is that the owning form does not need to call InvokeRequired/BeginInvoke to update its controls.
public class Foo
{
private Control owner_;
Thread thread_;
public event EventHandler<EventArgs> FooEvent;
public Foo(Control owner)
{
owner_ = owner;
thread_ = new Thread(FooThread);
thread_.Start();
}
private void FooThread()
{
Thread.Sleep(1000);
for (;;)
{
// Invoke performed in the thread
owner_.Invoke((EventHandler<EventArgs>)InternalFooEvent,
new object[] { this, new EventArgs() });
Thread.Sleep(10);
}
}
private void InternalFooEvent(object sender, EventArgs e)
{
EventHandler<EventArgs> evt = FooEvent;
if (evt != null)
evt(sender, e);
}
}
public partial class Form1 : Form
{
private Foo foo_;
public Form1()
{
InitializeComponent();
foo_ = new Foo(this);
foo_.FooEvent += OnFooEvent;
}
private void OnFooEvent(object sender, EventArgs e)
{
// does not need to call InvokeRequired/BeginInvoke()
label_.Text = "hello";
}
}
This is obviously contrary to the method used by Microsoft APIs that use background threads like System.Timers.Timer and System.Io.Ports.SerialPort. Is there anything inherently wrong with this method? Is it dangerous in some way?
Thanks,
PaulH
Edit: also, what if the form did not subscribe to the event right away? Would it clog the Form's message queue with events the form wasn't interested in?
This is a threadsafe call, the method will be processed in the thread of the form.
Nothing wrong with it when looking at it from a conceptual perspective.
Timers are more elegant for such tasks, though. However, it could be that a timer with an interval of 10ms slows down the GUI, that's probably why Invoke was used.
You do not need a call to InvokeRequired, since it is clear that the Control is in an other thread. Also, BeginInvoke only needs to be called when you want to call a method asynchronously, which obviously isn't the case here.
Regarding your edit:
No, the message queue will not be clogged. No event will be fired if no handler has been registered. Take another look at your code ;)

How to raise custom event from a Static Class

I have a static class that I would like to raise an event as part of a try catch block within a static method of that class.
For example in this method I would like to raise a custom event in the catch.
public static void saveMyMessage(String message)
{
try
{
//Do Database stuff
}
catch (Exception e)
{
//Raise custom event here
}
}
Thank you.
Important: be very careful about subscribing to a static event from instances. Static-to-static is fine, but a subscription from a static event to an instance handler is a great (read: very dangerous) way to keep that instance alive forever. GC will see the link, and will not collect the instance unless you unsubscribe (or use something like a WeakReference).
The pattern for creating static events is the same as instance events, just with static:
public static event EventHandler SomeEvent;
To make life easier (re null checking), a useful trick here is to add a trivial handler:
public static event EventHandler SomeEvent = delegate {};
Then you can simply invoke it without the null-check:
SomeEvent(null, EventArgs.Empty);
Note that because delegate instances are immutable, and de-referencing is thread-safe, there is never a race condition here, and no need to lock... who-ever is subscribed when we de-reference gets invoked.
(adjust for your own event-args etc).
This trick applies equally to instance events.
Your event would also need to be static:
public class ErrorEventArgs : EventArgs
{
private Exception error;
private string message;
public ErrorEventArgs(Exception ex, string msg)
{
error = ex;
message = msg;
}
public Exception Error
{
get { return error; }
}
public string Message
{
get { return message; }
}
}
public static class Service
{
public static EventHandler<ErrorEventArgs> OnError;
public static void SaveMyMessage(String message)
{
EventHandler<ErrorEventArgs> errorEvent = OnError;
if (errorEvent != null)
{
errorEvent(null, new ErrorEventArgs(null, message));
}
}
}
And Usage:
public class Test
{
public void OnError(object sender, ErrorEventArgs args)
{
Console.WriteLine(args.Message);
}
}
Test t = new Test();
Service.OnError += t.OnError;
Service.SaveMyMessage("Test message");
Several folks have offered up code examples, just don't fire an event using code such as:
if(null != ExampleEvent)
{
ExampleEvent(/* put parameters here, for events: sender, eventArgs */);
}
as this contains a race condition between when you check the event for null and when you actually fire the event. Instead use a simple variation:
MyEvent exampleEventCopy = ExampleEvent;
if(null != exampleEventCopy)
{
exampleEventCopy(/* put parameters here, for events: sender, eventArgs */);
}
This will copy any event subscribers into the exampleEventCopy, which you can then use as a local-only version of the public event without having to worry about any race conditions (Essentially, it is possible that another thread could pre-empt you right after you have checked the public event for null and proceed to remove all subscribers from the event, causing the subsequent firing of the event to throw an exception, by using a local-only copy, you avoid the possibility of another thread removing subscribers, since there is no way they could access the local variable).
Note: VS2008, C#
Just declare an event as you normally would within the static class, but be sure to mark the event as static:
public static event EventHandler Work;
Then just subscribe to it as you normally would.
Just to add "Delegates are immutable" So, as shown in the example above the following line obtains a copy of the delegate.
EventHandler<ErrorEventArgs> errorEvent = OnError;
The way I did this is the following:
1- define a delegate (this will enable you to have customized arguments):
public delegate void CustomeEventHandler(string str);
2- define an event based on the previously defined delegate:
public static event CustomeEventHandler ReadLine;
3- create an event handler:
static void OnLineRead(string currentLine)
{
if (ReadLine != null)
ReadLine(currentLine);
}
4- raise your event using the event handler (just call it wherever you want the event to be raised).

Categories