Handling fatal error in a Windows Service - c#

I created a windows service and installer that watches a collection of files for changes and copies any file that changes to a destination directory specified in the WatchlistConfig.xml file.
I have a couple issues with the service:
1. It has stopped running on one occasion. (unacceptable)
2. We sometimes have to attempt to start the service several times before it "takes".
I believe issue #1 is probably due to not handling fatal errors in the application. I found a bit of code that I tried to incorporate into the Main() method, but is written for a console app (Application is not a recognized class) and thus is commented out for now. Any idea which is the right class for implementing this in a service?
Issue #2 is most likely a timeout I'm guessing. The watchlist is currently comprised of 9 different files on different machines on the network. Connecting to these sources is not immediate (not all on a single domain). Is there a way to set a different timeout value for service startup?
Here's the relevant code. Additional classes on request.
Thanks in advance.
Edit: mistakenly posted the Main() from the test harness (console) which I use to debug. I've left it in place and add the Program class from the WinSvc Project
//Console Test harness
class Program
{
[STAThread]
static void Main(string[] args)
{
//AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
//Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
//Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
//Application.EnableVisualStyles();
//Application.SetCompatibleTextRenderingDefault(false);
//Application.Run(new Form1());
TimedWatchList twl = new TimedWatchList(new PSU_Config(Helpers.GetConfigFile()));
Console.WriteLine("Press \'q\' to quit the sample.");
while (Console.Read() != 'q') ;
}
static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
HandleException((Exception)e.ExceptionObject);
}
static void HandleException(Exception e)
{
//Handle/Log Exception Here
}
static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
Logger.Loggit(e.Exception.Message);
}
}
//Actual Service
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new Psu()
};
ServiceBase.Run(ServicesToRun);
}
}
public partial class Psu : ServiceBase
{
public Psu()
{
InitializeComponent();
TimedWatchList twl = new TimedWatchList(new PSU_Config(Helpers.GetConfigFile()));
}
protected override void OnStart(string[] args)
{
}
protected override void OnStop()
{
}
}
public class TimedWatchList
{
public static PSU_Config Config { get; set; }
List<WatchFile> WatchList = new List<WatchFile>();
public TimedWatchList(PSU_Config config)
{
Config = config;
if (Config.PrintDebugMsgs) Logger.Loggit("Attempting to create TimedWatchList object");
WatchList = WatchListFactory.GetWatchList(Helpers.GetWatchListFile());
if (Config.PrintDebugMsgs) Logger.Loggit("TimedWatchList created");
Timer _timer = new Timer();
_timer.Interval += Config.Interval;
_timer.Enabled = true;
// register OnTimedEvent() to fire on each "tick"
_timer.Elapsed += OnTimedEvent;
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
foreach (WatchFile file in WatchList)
{
file.PostOnUpdate();
}
}
}//TimedWatchList class
internal class WatchFile
// represents a file that is being watched
{
#region Props
public FileInfo SourceFile { get; set; }
public DirectoryInfo TargetPath { get; set; }
#endregion //Props
#region CTOR
public WatchFile() { }
public WatchFile(string fileName, string sourcePath, string destPath)
{
SourceFile = new FileInfo(Path.Combine(sourcePath, fileName));
TargetPath = new DirectoryInfo(destPath);
}
public WatchFile(FileInfo sourceFile, DirectoryInfo targetDirectory)
{
SourceFile = sourceFile;
TargetPath = targetDirectory;
}
#endregion //CTOR
public void PostOnUpdate()
{
//if (TimedWatchList.Config.PrintDebugMsgs) Logger.Loggit("WatchFile Post Event called for: " + SourceFile.Name);
//if (TimedWatchList.Config.PrintDebugMsgs) Logger.Loggit("Stored LastModified datetime: " + LastModified);
string targetPath = String.Format(#"{0}\{1}", TargetPath.FullName, SourceFile.Name);
{
try
{
//ensure directory exists
if (!Directory.Exists(TargetPath.FullName)) Directory.CreateDirectory(TargetPath.FullName);
//ensure file version is current
if (!File.Exists(targetPath) || (File.GetLastWriteTime(targetPath) != File.GetLastWriteTime(SourceFile.FullName)))
{
Logger.Loggit(String.Empty);
Logger.Loggit("Attempting to copy: " + SourceFile + " (" + File.GetLastWriteTime(SourceFile.FullName) + ")");
SourceFile.CopyTo(targetPath, true);
Logger.Loggit("\tCopy posted.\tLastModified: " + File.GetLastWriteTime(targetPath));
}
}
catch (IOException ioex)
{
Logger.Loggit("Error: " + ioex.Message);
}
catch (Exception ex)
{
Logger.Loggit("Error: " + ex.Message);
}
}
}
}// WatchFile class

There's really no need to guess; as a service you should be logging your errors to the system event log. Set a top level handler (as you've done), but don't expect to be able to handle it.
If the error was unhandled you're not going to be able to do anything about it there. Log it and exit. Catch the errors you can handle as soon as possible, test and design your code to not break otherwise.
You can set your service to restart automatically after a crash, but that should be a last resort. Bust out your debugger and figure out exactly where the errors are occurring and why. I see a lot of "it's probably [something]" and "it may be [something else]" statements here. Again, there is no good reason to guess; you have tools at your disposal which will help you figure out exactly what is going on.

You might want to simply wrap your function in a try / catch block to see what you might find.
try
{
MainAppFunctionality();
}
catch (Exception e)
{
//Not sure what you are going to do here, it's probably too late
}
I suggest you log to the Windows Event Log at various points in your application as a start so you can start to narrow down the location of the error.
I'm also not sure why you are using Console.Read() from a Windows Service context. As of Vista, there isn't a way for the service to interact with the desktop.

Related

WPF C# Check if needed files exist on app startup

I want to check if .dll, .png and .exe files exist before first app windows launches, but problem is I cant no matter how I try it just throws error in event viewer, instead my message.
My IsResourceExist Method:
private static bool IsResourceExist(string fileName)
{
var process = Process.GetCurrentProcess();
string path = process.MainModule.FileName.Replace("\\" + process.ProcessName + ".exe", "");
try
{
if (!File.Exists(Path.Combine(path, fileName)))
{
MessageBox.Show("Unable to load " + fileName + " library\nReinstall application and try again", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return false;
}
return true;
}
catch
{
return false;
}
}
Simple method nothing fancy, in normal situation(when file actually exist works fine)
private static bool CheckLibrarys()
{
if (!IsResourceExist("MyLib.dll")) return false;
//Other resources checking same way
return true;
}
This method checks all apps needed resources, also works on normal situation(when all files exist)
This I think very first code line called by app, works when files exist
public App()
{
if (!CheckLibrarys()) Environment.Exit(0);
}
When I delete MyLib.dll file in event viewer it throws:
Description: The process was terminated due to an unhandled exception.
Exception Info: System.IO.FileNotFoundException at
myapp.App.CheckLibrarys() at myapp.App..ctor() at
myapp.App.Main()
Like for real is this somekind of .Net Framework joke? What I'm missing here?
EDIT 1:
Same situation even with OnStartup override
protected override void OnStartup(StartupEventArgs e)
{
if (!CheckLibrarys()) Environment.Exit(0);
base.OnStartup(e);
}
EDIT 2 extending #bic answer and still app does not launch and does not throw any error that mylib is misssing
private static bool CheckLibrarys()
{
if (!IsResourceExist("MyLib.dll")) { return false; }
else
{
if (!MyLib.Init.ReturnOnlyTrue())
{
MessageBox.Show("Wrong loaded library, reinstall application and try again", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return false;
}
}
//Other resources checking same way
return true;
}
In my MyLib Init class ReturnOnlyTrue() method look like this:
public static bool ReturnOnlyTrue()
{
return true;
}
If the dll is referenced in the project then it cannot be missing otherwise the project references cannot be resolved. If you remove it from the project references and simply load it at runtime then you shouldnt have this problem.
Here is a nice description of how the runtime resolves references. This is ultimately where the FileNotFound exception is coming from.
https://learn.microsoft.com/en-us/dotnet/framework/deployment/how-the-runtime-locates-assemblies
In order for you to capture the error when the application starts you can add error handling as follows.
namespace SO_Wpf
{
using System;
using System.Diagnostics;
using System.IO;
using System.Windows;
using System.Windows.Threading;
public partial class App : Application
{
public App()
{
Current.DispatcherUnhandledException += this.AppDispatcherUnhandledException;
AppDomain.CurrentDomain.UnhandledException += this.AppDomainUnhandledException;
}
private void AppDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
if (e.Exception.GetType() == typeof(FileNotFoundException))
{
if (!CheckLibrarys())
{
Current.Shutdown();
}
}
else
{
MessageBox.Show(e.Exception.Message);
}
}
private void AppDomainUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
if (e.ExceptionObject.GetType() == typeof(FileNotFoundException))
{
if (!CheckLibrarys())
{
Current.Shutdown();
}
}
else
{
MessageBox.Show(e.ExceptionObject.ToString());
}
}
private static bool CheckLibrarys()
{
if (!IsResourceExist("MyLib.dll"))
{
return false;
}
//Other resources checking same way
return true;
}
private static bool IsResourceExist(string fileName)
{
var process = Process.GetCurrentProcess();
var path = process.MainModule.FileName.Replace("\\" + process.ProcessName + ".exe", "");
try
{
if (!File.Exists(Path.Combine(path, fileName)))
{
MessageBox.Show("Unable to load " + fileName + " library\nReinstall application and try again", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return false;
}
return true;
}
catch
{
return false;
}
}
}
}
e.Exception.Message will give you the message or you can change the output altogether by checking the error and if its FileNotFoundException etc. tell the user and exit.
You can override OnStartup method of App.xaml. In this you can add your custom logic.
Maybe exception is coming form somewhere else. You can add a global exception handler and can see from where it is coming form.
public partial class App : Application
{
public App()
{
DispatcherUnhandledException += App_DispatcherUnhandledException;
}
private void App_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
e.Handled = true;
MessageBox.Show(e.Exception.Message);
Environment.Exit(0);
}
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
if (!SomeCondition)
Application.Current.Shutdown();
}
}

C# Windows Service Events

So I am just messing around here nothing production just proof of concept with my first ever Windows Service.
I am trying to essentially create a windows service that sits as the listener for a signalr connection. In essence, I will have a windows application and a windows service. The win service will handle connecting to the signalr hub and on signalr calls fire an event. The windows application will listen for these events and perform actions based on them.
Currently I cannot get this to work. I have never worked with events, or windows services before. In my windows application my events never hit their break points, as well I log an error of null reference exception from the
ConnectToHub()
Alright if I comment out the OnConnected() method call I log a successful connection to the hub. I have never worked with events before so is my mistake with the events?
I debated that this approach was a bit overkill. However, for me it was a proof of concept that I could find a use for a long running windows service, and adding some events into the mix.
Code for service:
public delegate void MessageRecievedEventHanlder(object sender, MessageRecievedArgs e);
public delegate void ConnectedToHubEventHandler(object sender, ConnectedArgs e);
public partial class SignalRService : ServiceBase
{
IHubProxy _hub;
HubConnection connection;
string url = #"http://localhost:8080/";
private Message LastMessage;
public static event MessageRecievedEventHanlder NewMessage;
protected virtual void OnNewMessage(MessageRecievedArgs e)
{
NewMessage(null, e);
}
public static event ConnectedToHubEventHandler Connected;
protected virtual void OnConnected(ConnectedArgs e) {
System.IO.File.WriteAllText(#"C:\Users\Bailey Miller\Desktop\FTP\Logg.txt", "Hit OnConnected " + e.Success +" " + Connected != null ? "Isn't null" : "Null event");
Connected(null, e);
}
public SignalRService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
ConnectToHub().Wait();
}
private async Task ConnectToHub()
{
try
{
//Connecting
if (connection == null)
{
connection = new HubConnection(url);
}
if (_hub == null)
{
_hub = connection.CreateHubProxy("ChatHub");
}
await connection.Start();
//Connected
//Configure all the incoming options
_hub.On<Message>("RecieveMessage", IncomingMessage);
System.IO.File.WriteAllText(#"C:\Users\Bailey Miller\Desktop\FTP\Succes.txt", "Connected");
OnConnected(new ConnectedArgs(true));
}
catch (Exception ex)
{
//Failed
//OnConnected(new ConnectedArgs(false));
System.IO.File.WriteAllText(#"C:\Users\Bailey Miller\Desktop\FTP\Fail.txt", "Failed to connect " + ex.Message.ToString());
}
}
private void IncomingMessage(Message state)
{
DateTime? lmt;
//Determine if has lastmessagetime
if (LastMessage == null) {
lmt = null;
}
else {
lmt = LastMessage.RecievedAt;
}
LastMessage = state;
//New Message
//OnNewMessage(new MessageRecievedArgs(state, lmt));
}
protected override void OnStop()
{
}
}
public class MessageRecievedArgs : EventArgs
{
public Message NewMessage { get; }
public DateTime? LastMessageTime { get; }
public MessageRecievedArgs(Message msg, DateTime? lmt) {
this.NewMessage = msg;
this.LastMessageTime = lmt;
}
}
public class ConnectedArgs : EventArgs {
public bool Success { get; }
public ConnectedArgs(bool suc) {
this.Success = suc;
}
}
My windows application as of now:
public MainWindow()
{
InitializeComponent();
SignalRService.SignalRService.NewMessage += SignalRService_NewMessage;
SignalRService.SignalRService.Connected += SignalRService_Connected;
}
private void SignalRService_Connected(object sender, SignalRService.ConnectedArgs e)
{
throw new NotImplementedException();
}
private void SignalRService_NewMessage(object sender, SignalRService.MessageRecievedArgs e)
{
throw new NotImplementedException();
}
Your question is a bit broad- you don't describe exactly what isn't working, so I am guessing that when you start your service, it says "starting..." for a long while and eventually windows service manager gives you an error saying your service didn't start in a timely fashion. The issue is that OnStart() is expected to return- you can't block the thread there with the Wait() call. My suggestion would be to spawn a new background thread here to perform the waiting, then exit. That should get you past the first hurdle.
As another aside... You can add a regular main method to a windows service project, change the project type to Console Application, and run it that way to reduce your debugging cycle time. Then when you are sure it basically works, change the project type back to Windows Service and install it.
EDIT: Now that you have a better error description, I see the real problem. The issue is that you are raising an event without checking for null first. Event fields are null until you attach a listener. So change your code as follows:
protected virtual void OnConnected(ConnectedArgs e) {
System.IO.File.WriteAllText(#"C:\Users\Bailey Miller\Desktop\FTP\Logg.txt", "Hit OnConnected " + e.Success +" " + Connected != null ? "Isn't null" : "Null event");
ConnectedToHubEventHandler connectedEvent = Connected;
if (connectedEvent != null) // This event might be null, so check first
connectedEvent(null, e);
}

EasyNetQ Windows Service messaging architecture not quite working

So I am trying to experiment (based on this EasyNetQ toturial: Quick Start - EasyNetQ) with a simple EasyNetQ messaging architecture involving a Publisher and a Subscriber and it doesn't seem to be working quite as expected. Both my Publisher and Subscriber are Windows Service projects in Visual Studio 2015 and the message being sent between them is an instance of a custom type (TextMessage), which is a simple Class Library that looks like this:
namespace Messaging.Messages
{
public class TextMessage
{
public string Text { get; set; }
}
}
My Publisher looks like this:
namespace Messaging.Publisher
{
public partial class ReportService : ServiceBase
{
private Timer timer = null;
public ReportService()
{
this.InitializeComponent();
}
protected override void OnStart(string[] args)
{
Library.WriteErrorLog("Report Publisher Service started");
using (var bus = RabbitHutch.CreateBus("host=localhost"))
{
bus.Publish(new TextMessage
{
Text = "Hello"
});
}
}
protected override void OnStop()
{
this.timer.Enabled = false;
Library.WriteErrorLog("Test window service has stopped");
}
}
}
So nothing fancy. All it does is publish one message of type TextMessage and logs to a text file "PublisherLogFile.txt":
namespace Messaging.Publisher
{
public static class Library
{
public static void WriteErrorLog(string Message)
{
StreamWriter sw = null;
try
{
sw = new StreamWriter(AppDomain.CurrentDomain.BaseDirectory + "\\PublisherLogFile.txt", true);
sw.WriteLine(DateTime.Now.ToString(CultureInfo.InvariantCulture) + ": " + Message);
sw.Flush();
sw.Close();
}
catch (Exception)
{
throw;
}
}
}
}
And the Subscriber looks like this:
namespace Messaging.Subscriber
{
public partial class ReportSubscriberService : ServiceBase
{
public ReportSubscriberService()
{
this.InitializeComponent();
}
protected override void OnStart(string[] args)
{
WriteErrorLog("Report Subscriber Service started");
using (var bus = RabbitHutch.CreateBus("host=localhost"))
{
bus.Subscribe<TextMessage>("testId", HandleTextMessage);
}
}
protected override void OnStop()
{
WriteErrorLog("Exiting Report Subscriber Service");
}
private static void HandleTextMessage(TextMessage textMessage)
{
WriteErrorLog("Got message: " + textMessage.Text);
}
private static void WriteErrorLog(string Message)
{
try
{
var sw = new StreamWriter(AppDomain.CurrentDomain.BaseDirectory + "\\SubscriberLogFile.txt", true);
sw.WriteLine(DateTime.Now.ToString(CultureInfo.InvariantCulture) + ": " + Message);
sw.Flush();
sw.Close();
}
catch (Exception)
{
throw;
}
}
}
}
Also very simple. All it does is receive messages of type TextMessage and prints out the value of their Text attribute to a log file "SubscriberLogFile.txt". The problem is that it doesn't seem to be receiving the message because it doesn't log to the text file above. It looks like the HandleTextMessage handler in my Suscriber is never called. This is the content of "SubscriberLogFile.txt":
Also, looking at the RabbitMQ management console, no connections or channels are created, just one queue:
And the RabbitMQ log:
When I first did the same experiment, but with the difference that the Publisher and Subscriber were Console Applications instead of Windows Services, things seemed to work fine. What could be the problem here?
The problem was that I was disposing the bus as soon as I opened it. Instead it has to stay open and only be disposed when the service is stopped, on the OnStop event.

Debugging and logging windows service

I am learning basics of windows service. I have created a very simple one.
using System.ServiceProcess;
namespace WindowsServiceBasic
{
public partial class OmerService : ServiceBase
{
public OmerService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
System.Diagnostics.Debugger.Launch();
WriteLog("START");
}
protected override void OnStop()
{
System.Diagnostics.Debugger.Launch();
WriteLog("STOP");
}
private void WriteLog(string durum)
{
eventLog1.WriteEntry(performanceCounter1.RawValue.ToString());
}
}
}
using System;
using System.IO;
using System.ServiceProcess;
namespace WindowsServiceBasic
{
internal static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
private static void Main()
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new OmerService()
};
ServiceBase.Run(ServicesToRun);
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
if (e != null && e.ExceptionObject != null)
{
string createText = e.ToString();
File.WriteAllText(#"c:\omerlog.txt", createText);
}
}
}
}
The first time my service (AServis) starts successfully but when I click the restart it crashes. Since my service is very simple It should have been worked properly. I try to log the error, put try catch but I could not find anything. I am trying to attach process, it debugs stop event but after stop debug suddenly finishes and start process crashes. Could you please help me what is the reason and how can I debug and log error.
Thanks in advance
I saw that it was stuck in
public OmerService()
{
InitializeComponent();
}
I could see the issue adding System.Diagnostics.Debugger.Launch(); statement.
public OmerService()
{
System.Diagnostics.Debugger.Launch();
InitializeComponent();
}
The standard trick I use in this situation is to add a call to System.Diagnostics.Debugger.Break in my start up code. Now, when you start the service as normal (through the Service Control Manager (SCM)), the call to the Break will cause Windows to launch the JIT debugger, which should prompt you to choose the debugger you wish to attach to the process (e.g., Visual Studio), which will then enable you to debug your code as normal.
Also see this: Easier way to debug a Windows service.

Fail to Keep windows service with Timer alive

I have a windows service that I made for MY server...
I need to check every minute if there is some new info in my SQL Database.
So I made a windows service that create a Timer with interval of 1 minute.
But The windows service set the timer and ending the run.
It's goes like this:
Starting Service
Setting Timer with interval
Finishing and exiting Service <-- I want to keep it alive
As you can see the Service exit and I want the Windows service to run every minute without stopping....
I can see in the Event Viewer that there are the "Service started successfully." And the "Service stopped successfully."
What should I do?
P.S : I thought Timer should work with out exit... or may I wrong?
CODE:
Windows service:
static void Main(string[] args)
{
try
{
Utils.SetConfigFile();
var ServiceToRun = new TaoTimer();
ServiceToRun.Start();
}
catch (Exception ex)
{
EventLog.WriteEntry("Application", ex.ToString(), EventLogEntryType.Error);
}
}
TaoTimer:
public partial class TaoTimer : ServiceBase
{
List<TimerModel> timerList;
public TaoTimer()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
EventLog.WriteEntry("Started");
}
public void SetTimer(TimerModel timerModel)
{
int minute = 1000 * 60;
try
{
AlertTimer at = new AlertTimer(timerModel, minute);
at.Start();
}
catch
{
}
}
protected override void OnStop()
{
EventLog.WriteEntry("Stopped");
}
protected override void OnPause()
{
EventLog.WriteEntry("Paused");
}
protected override void OnContinue()
{
EventLog.WriteEntry("Continuing");
}
protected override void OnShutdown()
{
EventLog.WriteEntry("ShutDowned");
}
public void Start()
{
SetTimerList();
}
protected void SetTimerList()//Read Config from xml and start the timer
{
XElement root = XElement.Load(#"C:\TaoTimer\Data.xml");
timerList = new List<TimerModel>(from d in root.Descendants("Timer")
select new TimerModel(
d.Element("Id").Value.ToString(),
d.Element("Name").Value.ToString(),
d.Element("InterVal").Value.ToString(),
d.Element("TimeFormat").Value.ToString(),
d.Element("Day").Value.ToString(),
d.Element("TimeStamp").Value.ToString()));
timerList.ForEach(i => SetTimer(i));
}
}
AlertTimer:
public class AlertTimer
{
static System.Timers.Timer aTimer = new System.Timers.Timer();
public AlertTimer(TimerModel timerModel, int milliseconds)
{
aTimer.Elapsed += new ElapsedEventHandler((sender, e) => OnTimedEvent(sender, e, timerModel));
aTimer.Interval = milliseconds;
}
public void Start()
{
aTimer.Enabled = true;
}
public static void OnTimedEvent(object source, ElapsedEventArgs e, TimerModel timerModel)
{
getAbsenceContacts.Start();<-- NEVER GETS HERE....
}
}
You're not actually starting your service. You're calling a method named Start, which is not part of the Windows Service class hierarchy it's just a method you've defined. Your method runs and finishes, so the service exits.
Try this:
static void Main(string[] args)
{
try
{
Utils.SetConfigFile();
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new TaoTimer()
};
ServiceBase.Run(ServicesToRun);
}
catch (Exception ex)
{
EventLog.WriteEntry("Application", ex.ToString(), EventLogEntryType.Error);
}
}
public partial class TaoTimer : ServiceBase
{
...
protected override void OnStart(string[] args)
{
SetTimerList();
EventLog.WriteEntry("Started");
}
....
}
and remove the Start method from TaoTimer entirely.
You need to store your AlertTimer instances in something that will last the lifetime of the service (e.g. in a List<AlertTimer> declared as a field inside TaoTimer.
It's only really alluded to in the documentation for Timer that timer's, in and of themselves, don't prevent themselves from being garbage collected. The example says:
// Normally, the timer is declared at the class level,
// so that it stays in scope as long as it is needed.
// If the timer is declared in a long-running method,
// KeepAlive must be used to prevent the JIT compiler
// from allowing aggressive garbage collection to occur
// before the method ends. You can experiment with this
// by commenting out the class-level declaration and
// uncommenting the declaration below; then uncomment
// the GC.KeepAlive(aTimer) at the end of the method.
//System.Timers.Timer aTimer;
Now, whilst your timer's are declared at the class level inside of your AlertTimer class, there's nothing to stop the AlertTimer instances, themselves, from being collected. And the GC only keeps things alive that are transitively reachable. Once the AlertTimer instances are collectible, so are your Timer objects.

Categories

Resources