A couple of weeks ago, I attended a seminar on PRISM at Microsoft's office. Although I have not been working with GUI for more than two years now, I am always enthusiastic to hear about interesting architectural principles, and PRISM is indeed an interesting one. One of the key concepts of the PRISM architecture is the
EventAggregator component whose responsibility is to enable different application modules to communicate without knowing each other in a publish/subscribe paradigm. It's not entirely a new concept as the earlier CAB (SCSF) also had a similar implementation, and there are also some other Open Source implementations of the EventAggregator pattern like EventBroker and MemBus.
So, you must be asking yourself, why do we need another implementation. Well, the answer lies in the little details of the implementation and on one important feature - the cross process event distribution.
The project I am involved in is a large command and control system which has a rich GUI smart client system running on four screens and divided into three processes. Therefore, in order to fully utilize the
EventAggregator principle, I needed some kind of IPC (Inter Process Communication) enabled library for sending events between GUI instances on a single machine.
The other thing is that some of these existing implementations oblige you to inherit from some base class when you define your events, while the other is limited only to
EventHandler<TEventArgs> event handlers or use an Observer pattern by implementing generic interfaces on subscriber objects.
What I wanted is a simple POCO event definition which I can easily define, serialize when needed for the cross process communication, and publish directly on the
So the requirements are as follows:
- Subscription may be strong or weak (using the
- Events are POCO objects.
- Event handlers may be static, public, and nonpublic.
- All event handlers limited to one and single parameter.
- Event handlers may be invoked on the publisher thread, worker thread, or UI thread (a/sync).
- IPC mechanism must be one to many without any configuration.
- Event handlers may be invoked with the derived types of the events (i.e., polymorphic invocation).
- Subscribers may use attributes to mark event handlers, or subscribe directly to some delegate.
With all these in mind, I produced the following interface:
EventRouter implementation should be singleton as you should use only one instance throughout your process space; however, I didn't implement it as a singleton because most of the
EventRouter usage will be done using some IoC container (I am using Castle Windsor), and there it can be easily configured to singleton lifetime:
IWindsorContainer container = new WindsorContainer();
IEventRouter eventRouter = container.Resolve<IEventRouter>();
The Windows platform offers a wide range of IPC technologies and APIs. Having in mind the requirement of one to many distribution and no configuration, I decided to go with the Windows Messages API, in particular,
SendMessage. While browsing on the web, I found this great project called XDMessaging which does almost exactly what I needed: it uses the
SendMessageTimeout API to send the
WM_COPYDATA message, and uses
EnumWindows to find all the top-level windows registered to receive this message. The only mismatch with this implementation is that it uses binary serialized strings to pass data across processes.
So I copy/pasted the core of this library functionality, and replaced its transport medium to binary serialized objects. The implementation itself is pretty simple: each instance creates a top-level zero size window, and uses its window procedure to find
public class WindowsManager : NativeWindow, IPCoordinator
const string ChannelName = "MainChannel";
List<IntPtr> m_windowHandles = new List<IntPtr>();
object m_lock = new object();
CreateParams p = new CreateParams();
p.Width = 0;
p.Height = 0;
p.X = 0;
p.Y = 0;
p.Caption = Guid.NewGuid().ToString();
p.Parent = IntPtr.Zero;
Native.SetProp(base.Handle, ChannelName, (int)base.Handle);
protected override void WndProc(ref Message m)
if (m.Msg == Native.WM_COPYDATA)
using (DataGram dataGram = DataGram.FromPointer(m.LParam))
if (NotifyEvent != null)
public void BroadcastMessage(object data)
IntPtr outPtr = IntPtr.Zero;
IEnumerable<IntPtr> windows = GetWindowHandles();
using (DataGram dg = new DataGram(data))
Native.COPYDATASTRUCT dataStruct = dg.ToStruct();
foreach (var handle in windows)
IntPtr.Zero, ref dataStruct,
1000, out outPtr);
public IEnumerable<IntPtr> GetWindowHandles()
private int OnWindowEnum(IntPtr hWnd, IntPtr lParam)
if (hWnd != base.Handle && Native.GetProp(hWnd, ChannelName) != 0)
#region IPCoordinator Members
public void Broadcast(object notifictaion)
ThreadPool.QueueUserWorkItem(state => BroadcastMessage(notifictaion));
public event NotificationEventHandler NotifyEvent;
The message broadcasting executed on a thread pool thread since the
SendMessageTimeout API uses a timeout of 1 second for each window, so it may block (rarely) for N seconds where N is the number of
You can apply any other IPC API by implementing the
IPCoordinator interface and passing it to the
Since most event consumer handlers would be inside some UI resource, it is necessary to synchronize thread execution in order to avoid the nasty Cross-Thread exception. The current
EventRouter implementation contains thread synchronization for both Windows Forms applications by using
System.Threading.SynchronizationContext, and WPF applications by using
I prefer using the
Dispatcher object since it is created always and never returns
null like the
SynchronizationContext.Current method. The only limitations are that it can only be used with .NET 3.0 or higher and must be constructed on the UI thread.
You can implement you own UI sync by implementing the
Exceptions thrown by the event handlers do not stop the event propagation, and if the
IEventRouter.Logger property is initialized, will be logged according to the application configuration.
Using the code
Subscribing for event notifications
You can subscribe to events using the
[EventConsumer(ThreadingOption = ThreadingOption.UIAsync)]
private void OnNewOrder(OrderAdded notif)
and then call the
IEventRouter.Register(object subscriber) method.
If you want to explicitly subscribe to some notification, use one of the
IEventRouter.Register(Action<T> action) overloads:
, true, ThreadingOption.DefaultThread, null);
Sending events to specific subscriber/s
router.SendTo(new Notification(), "MySpecialOne");
Using polymorphic invocation
EnablePolymorphicInvokation property is enabled (its disabled by default), your event handlers will be invoked also on event derived types:
public class CustomerRemoved
public int CustomerID;
public class VipCustomerRemoved : CustomerRemoved
private void OnCustomerChange(CustomerRemoved notif)
In this case, the
OnCustomerChange method will be called when both
VipCustomerRemoved events are published.
By default, all subscriptions are implemented using weak references, so when the subscriber object goes out of scope, it will be reclaimed. Nevertheless, you can explicitly unregister the subscriber object either by its reference or by its subscription ID, if it is present. Please note that subscription ID is used like a single or group subscription rather than a unique key.
Some implementation details
The registration of a subscriber object based on reflecting on method calls and finding the
public void Register(object subscriber)
Type sType = subscriber.GetType();
MethodInfo methods = sType.GetMethods(BindingFlags.Public |
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static);
foreach (var method in methods)
EventConsumer attr =
if (attr == null)
ParameterInfo pInfo = method.GetParameters();
if (pInfo.Count() != 1)
throw new InvalidOperationException("Method must have a single parameter");
Type notifyType = pInfo.ParameterType;
SubscriptionInfo info = new SubscriptionInfo();
info.ID = attr.SubscriptionID;
info.Method = method;
info.Target = subscriber;
info.ThreadOption = attr.ThreadingOption;
info.UseStrongReference = attr.UseStrongReference;
The subscriptions are stored in a
MultiMap object which is actually a dictionary with an event type as a key and a list of subscriptions as a value.
MultiMap is a small extension to a
HashList class from the excellent NGenerics project.
All the actions performed on the
MultiMap instance (subscriptions, invocations, and un-subscriptions) are inside a
lock statement, so the
EventRouter implementation is thread safe.
IEventRouter.Register(Action<T> action) with an anonymous method, it may be reclaimed right after the first method invocation since there are no strong references to it. So to be on the safe side, either use strong reference for anonymous methods or don't use them at all