Reputation: 173
I've read a lot of the posts related to Windows services and the use of timers, but I haven't found any reason why my event handler is still being triggered. Could anyone point me in the right direction? I want to know why this is happening so I understand how to avoid this in the future.
Edit: The onError event handler is never invoked (or I would have seen the event in the event log).
Timer: System.Timers.Timer
ServiceBase: System.ServiceProcess.ServiceBase
Here is the abstract class:
public abstract class ABCService : ServiceBase
{
// Members
protected Timer invocationTimer;
protected Timer wellnessTimer;
protected FileSystemWatcher fsw;
// Constructors
protected ABCService()
{
invocationTimer = new Timer();
wellnessTimer = new Timer();
fsw = null;
invocationTimer.AutoReset = false;
invocationTimer.Interval = 30000; // 30 seconds
invocationTimer.Elapsed += new ElapsedEventHandler(invocationTimer_Elapsed);
wellnessTimer.AutoReset = false;
wellnessTimer.Elapsed += new ElapsedEventHandler(wellnessTimer_Elapsed);
}
// Methods
protected void invocationTimer_Elapsed(object o, ElapsedEventArgs args)
{
try
{
// log to event log
invocationTimer.Stop();
if ((new FileInfo(fsw.Path + "\\" + fsw.Filter)).Exists)
{
onCreated(this, new FileSystemEventArgs(WatcherChangeTypes.Created, fsw.Path, fsw.Filter));
}
}
catch (Exception x)
{
onError(this, new ErrorEventArgs(x));
}
}
protected void wellnessTimer_Elapsed(object o, ElapsedEventArgs args)
{
try
{
// log to event log
wellnessTimer.Stop();
wellnessTimer.Interval = 60000; // ms
if (fsw != null)
{
fsw.Dispose();
}
fsw = new FileSystemWatcher(ConfigurationManager.AppSettings["pathKey"], ConfigurationManager.AppSettings["filterKey"]);
invocationTimer.Start();
}
catch (Exception x)
{
onError(this, new ErrorEventArgs(x));
}
}
protected abstract void onCreated(object o, FileSystemEventArgs args);
protected virtual void onError(object o, ErrorEventArgs args)
{
// log to event log
wellnessTimer.Start();
}
protected override void OnStart(string[] args)
{
// log to event log
wellnessTimer.Interval = 5000; // 5 seconds
wellnessTimer.Start();
}
protected override void OnStop()
{
// log to event log
wellnessTimer.Stop();
}
}
Here is an instance class:
public partial class Service1 : ABCService
{
// Members
private static object locket = new object();
private static DateTime LAST_RUN_TIME = DateTime.Now.AddSeconds(-10);
// Constructors
public Service1()
{
InitializeComponent();
}
// Methods
protected override void onCreated(object o, FileSystemEventArgs args)
{
lock (locket)
{
// log to event log
if ((DateTime.Now - LAST_RUN_TIME).Seconds >= 10)
{
// do stuff
}
else
{
// log to event log
invocationTimer.Stop();
invocationTimer.Start();
}
}
}
}
Here is the autogenerated code for the partial class:
partial class Service1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
//
// Service1
//
this.ServiceName = "Service1";
}
#endregion
}
So what exactly is happening? I'm looking at my event log and I see that once every minute the wellnessTimer event handler is invoked.
Here is what I think is happening, but I'm obviously wrong:
1. Service is started via MMC
2. OnStart() method is invoked
3. wellnessTimer interval is set to 5 seconds
4. wellnessTimer start method is invoked
5. wellnessTimer_Elapsed event handler is invoked
6. wellnessTimer stop method is invoked
7. wellnessTimer interval is set to 5 minutes
8. invocationTimer start method is invoked
9. 30 seconds later, the invocationTimer_Elapsed method is invoked
10. invocationTimer stop method is invoked
At this point both timers should still exist for this instance, but should be disabled. I debugged this via attach to process in Visual Studio 2010 and marked an ID for the object (sender) being passed into the event handlers. It is the same object as the instance. Also, both timers in the Locals window had their enabled property set to false.
This makes me thing I'm using inheritance incorrectly, or something is going on with threading. I'm not the best at either of those things, but if it is due to them please let me know so I can learn.
Thanks to everyone in advance.
Edit #2: Here is some trace data...
'o' represents the object passed into the event handler
ABCService() method invoked <--
ABCService() method invoked -->
Service1() method invoked <--
Service1() method invoked -->
OnStart() method invoked <--
OnStart() method invoked -->
wellnessTimer_Elapsed() method invoked <--
((System.Timers.Timer) o).Enabled = False
((System.Timers.Timer) o).Interval = 5000
this.wellnessTimer.Enabled = False
this.wellnessTimer.Interval = 5000
wellnessTimer_Elapsed() method invoked -->
invocationTimer_Elapsed() method invoked <--
((System.Timers.Timer) o).Enabled = False
((System.Timers.Timer) o).Interval = 30000
this.invocationTimer.Enabled = False
this.invocationTimer.Interval = 30000
invocationTimer_Elapsed() method invoked -->
wellnessTimer_Elapsed() method invoked <--
((System.Timers.Timer) o).Enabled = False
((System.Timers.Timer) o).Interval = 60000
this.wellnessTimer.Enabled = False
this.wellnessTimer.Interval = 60000
wellnessTimer_Elapsed() method invoked -->
invocationTimer_Elapsed() method invoked <--
((System.Timers.Timer) o).Enabled = False
((System.Timers.Timer) o).Interval = 30000
this.invocationTimer.Enabled = False
this.invocationTimer.Interval = 30000
invocationTimer_Elapsed() method invoked -->
Upvotes: 1
Views: 15682
Reputation: 34908
Are you experiencing the problem with the code exactly as you provided, or would it be dependent on code around:
// depending on logic, these next two lines will be called together or not at all
Based on what I read from the code, the wellness timer will fire every 60 seconds when it encounters an error on the onCreated call. If this is doing file operations, the one thing that screams out is security permissions for the account the service is running as.
When catching an exception, Output it to the event log or a log file. Behaviour by exception can easily lead to confusing situations, especially where it's not so convenient to hook up a debugger.
Upvotes: 0
Reputation: 14595
From MSDN's remarks on the Timer class:
Elapsed events can occur after the Dispose or Stop method has been called or after the Enabled property has been set to false, because the signal to raise the Elapsed event is always queued for execution on a thread pool thread. One way to resolve this race condition is to set a flag that tells the event handler for the Elapsed event to ignore subsequent events.
So you can do something like this inside your event handler to prevent executing it after the timer has been disabled:
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
if (timer.Enabled)
{
Console.WriteLine("The Elapsed event was raised at {0}", e.SignalTime);
}
}
Upvotes: 1