SystemEvents is a class that allows users to receive events that are system-wide. These events range from system time changing (due to daylight savings or time sync or manual time changes) to SessionEnding to LowBattery, to FontsInstalled events, etc.
There are many issues with this class
1- It does not work in a Windows Service, unless the service interacts with the desktop (which is an XP feature only). Vista and Windows 7 has an awkward support to say the least for services that interact with the windows desktop. As far as I'm concerned, it is not supported.
2- If you call any SystemEvent.XYZ += OnXYZ;, this ends up creating a hidden window. So what's the problem? if you have any WCF objects that register themselves (using WCF's ServiceHost) in the same thread as the one that called SystemEvents, then all subsequent calls to the WCF services will be serialized on that thread. Most likely that thread is the main UI thread for your application. This results in nasty deadlocks that are very difficult to debug and isolate.
So what's the solution?
I tried various methods including the Microsoft's suggested way to handle this issue http://msdn.microsoft.com/en-us/library/microsoft.win32.systemevents_events.aspx but SystemEvents seems to have a mind of its own and you will fix #1 above but you will still end up with #2.
I decided to write my own System Events class as it is simple and straight forward.
I was mainly focused on the TimeChanged event but you can add other events as needed
public class SystemEventsEx : NativeWindow
{
public event EventHandler TimeChanged = null;
public SystemEventsEx()
{
CreateParams cp = new CreateParams();
cp.Caption = "WindowsMessageTarget";
cp.Width = 0;
cp.Height = 0;
cp.X = 0;
cp.Y = 0;
cp.Parent = IntPtr.Zero;
CreateHandle(cp);
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WindowsMessages.WmTimeChanged)
{
TimeChanged((object)null, (EventArgs)null);
}
base.WndProc(ref m);
}
}
Make a stsatic class that can host the SystemEvents instance and allow subscriptions to systm events as follows:
public static class MyApplication
{
private static SystemEventsEx _systemEvents = null;
public static SystemEventsEx SystemEvents
{ get { return _systemEvents; } }
public static EnableSystemEvents()
{
Thread t = new Thread(OnEnableSystemEvents);
t.IsBackground = true;
t.Start();
}
private static void OnEnableSystemEvents()
{
_systemEvents = new SystemEventsEx();
Application.Run();
}
}
In you main.cs
you can do the following:
[STAThread]
public static void Main(string[] args)
{
. . .
MyApplication.EnableSystemEvents();
. . .
}
There are many issues with this class
1- It does not work in a Windows Service, unless the service interacts with the desktop (which is an XP feature only). Vista and Windows 7 has an awkward support to say the least for services that interact with the windows desktop. As far as I'm concerned, it is not supported.
2- If you call any SystemEvent.XYZ += OnXYZ;, this ends up creating a hidden window. So what's the problem? if you have any WCF objects that register themselves (using WCF's ServiceHost) in the same thread as the one that called SystemEvents, then all subsequent calls to the WCF services will be serialized on that thread. Most likely that thread is the main UI thread for your application. This results in nasty deadlocks that are very difficult to debug and isolate.
So what's the solution?
I tried various methods including the Microsoft's suggested way to handle this issue http://msdn.microsoft.com/en-us/library/microsoft.win32.systemevents_events.aspx but SystemEvents seems to have a mind of its own and you will fix #1 above but you will still end up with #2.
I decided to write my own System Events class as it is simple and straight forward.
I was mainly focused on the TimeChanged event but you can add other events as needed
public class SystemEventsEx : NativeWindow
{
public event EventHandler TimeChanged = null;
public SystemEventsEx()
{
CreateParams cp = new CreateParams();
cp.Caption = "WindowsMessageTarget";
cp.Width = 0;
cp.Height = 0;
cp.X = 0;
cp.Y = 0;
cp.Parent = IntPtr.Zero;
CreateHandle(cp);
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WindowsMessages.WmTimeChanged)
{
TimeChanged((object)null, (EventArgs)null);
}
base.WndProc(ref m);
}
}
Make a stsatic class that can host the SystemEvents instance and allow subscriptions to systm events as follows:
public static class MyApplication
{
private static SystemEventsEx _systemEvents = null;
public static SystemEventsEx SystemEvents
{ get { return _systemEvents; } }
public static EnableSystemEvents()
{
Thread t = new Thread(OnEnableSystemEvents);
t.IsBackground = true;
t.Start();
}
private static void OnEnableSystemEvents()
{
_systemEvents = new SystemEventsEx();
Application.Run();
}
}
In you main.cs
you can do the following:
[STAThread]
public static void Main(string[] args)
{
. . .
MyApplication.EnableSystemEvents();
. . .
}
Comments
Post a Comment