Multiple windows services in a single project = mystery - c#

I'm having a bizarre issue that I haven't seen before and I'm thinking it MUST be something simple that I'm not seeing in my code.
I have a project with 2 windows services defined. One I've called DataSyncService, the other SubscriptionService. Both are added to the same project installer. Both use a timer control from System.Timers.
If I start both services together, they seem to work fine. The timers elapse at the appropriate time and everything looks okay. However, if I start either service individually, leaving the other stopped, everything goes haywire. The timer elapses constantly and on the wrong service. In other words, if I start the DataSyncService, the SubscriptionService timer elapses over and over. ...which is obviously strange.
The setup is similar to what I've done in the past so I'm really stumped. I even tried deleting both service and starting over but it doesn't seem to make a difference. At this point, I'm thinking I've made a simple error in the way I'm defining the services and my brain just won't let me see it. It must be creating some sort of threading issue that causes one service to race when the other is stopped. Here the code....
From Program.cs:
static void Main()
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new DataSyncService(),
new SubscriptionService()
};
ServiceBase.Run(ServicesToRun);
}
From ProjectInstaller.designer.cs:
private void InitializeComponent()
{
this.serviceProcessInstaller1 = new System.ServiceProcess.ServiceProcessInstaller();
this.dataSyncInstaller = new System.ServiceProcess.ServiceInstaller();
this.subscriptionInstaller = new System.ServiceProcess.ServiceInstaller();
//
// serviceProcessInstaller1
//
this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
this.serviceProcessInstaller1.Password = null;
this.serviceProcessInstaller1.Username = null;
//
// dataSyncInstaller
//
this.dataSyncInstaller.DisplayName = "Data Sync Service";
this.dataSyncInstaller.ServiceName = "DataSyncService";
this.dataSyncInstaller.StartType = System.ServiceProcess.ServiceStartMode.Automatic;
//
// subscriptionInstaller
//
this.subscriptionInstaller.DisplayName = "Subscription Service";
this.subscriptionInstaller.ServiceName = "SubscriptionService";
this.subscriptionInstaller.StartType = System.ServiceProcess.ServiceStartMode.Automatic;
//
// ProjectInstaller
//
this.Installers.AddRange(new System.Configuration.Install.Installer[] {
this.serviceProcessInstaller1,
this.dataSyncInstaller,
this.subscriptionInstaller});
}
private System.ServiceProcess.ServiceProcessInstaller serviceProcessInstaller1;
private System.ServiceProcess.ServiceInstaller dataSyncInstaller;
private System.ServiceProcess.ServiceInstaller subscriptionInstaller;
From DataSyncService.cs:
public static readonly int _defaultInterval = 43200000;
//log4net.ILog log;
public DataSyncService()
{
InitializeComponent();
//log = LogFactory.Instance.GetLogger(this);
}
protected override void OnStart(string[] args)
{
timer1.Interval = _defaultInterval; //GetInterval();
timer1.Enabled = true;
EventLog.WriteEntry("MyProj", "Data Sync Service Started", EventLogEntryType.Information);
//log.Info("Data Sync Service Started");
}
private void timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
EventLog.WriteEntry("MyProj", "Data Sync Timer Elapsed.", EventLogEntryType.Information);
}
private void InitializeComponent()
{
this.timer1 = new System.Timers.Timer();
((System.ComponentModel.ISupportInitialize)(this.timer1)).BeginInit();
//
// timer1
//
this.timer1.Enabled = true;
this.timer1.Elapsed += new System.Timers.ElapsedEventHandler(this.timer1_Elapsed);
//
// DataSyncService
//
this.ServiceName = "DataSyncService";
((System.ComponentModel.ISupportInitialize)(this.timer1)).EndInit();
}
From SubscriptionService:
public static readonly int _defaultInterval = 300000;
//log4net.ILog log;
public SubscriptionService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
timer1.Interval = _defaultInterval; //GetInterval();
timer1.Enabled = true;
EventLog.WriteEntry("MyProj", "Subscription Service Started", EventLogEntryType.Information);
//log.Info("Subscription Service Started");
}
private void timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
EventLog.WriteEntry("MyProj", "Subscription Service Time Elapsed", EventLogEntryType.Information);
}
private void InitializeComponent() //in designer
{
this.timer1 = new System.Timers.Timer();
((System.ComponentModel.ISupportInitialize)(this.timer1)).BeginInit();
//
// timer1
//
this.timer1.Enabled = true;
this.timer1.Elapsed += new System.Timers.ElapsedEventHandler(this.timer1_Elapsed);
//
// SubscriptionService
//
this.ServiceName = "SubscriptionService";
((System.ComponentModel.ISupportInitialize)(this.timer1)).EndInit();
}
Again, the problem is that the timer1_elapsed handler runs constantly when only one of the services is started. And it's the handler on the OPPOSITE service.
Anybody see anything?

In the Service.Designer.cs files InitializeComponent() methods, I'm missing
this.CanShutdown = true;
...and I shouldn't be enabling the timers there since I do it in the OnStart handlers.
So it should be something like:
private void InitializeComponent()
{
this.timer1 = new System.Timers.Timer();
((System.ComponentModel.ISupportInitialize)(this.timer1)).BeginInit();
//
// timer1
//
this.timer1.Elapsed += new System.Timers.ElapsedEventHandler(this.timer1_Elapsed);
//
// DataSyncService
//
this.ServiceName = "DataSyncService";
this.CanShutdown = true;
((System.ComponentModel.ISupportInitialize)(this.timer1)).EndInit();
}

Related

FileSystemWatcher hangs after a few days

I have a Windows service, that watches a folder for newly created files and copies them to another folder. This folder has high traffic and the events are queued.
The service runs fine but after a few days, it hangs for many hours not doing any job. Later it just starts working again and continues the job, and the accumulated events are then processed. Meanwhile, the service is not crashing.
What went wrong?
The service starts like this:
protected override void OnStart(string[] args)
{
Log.AddLog("starting up");
FolderWatcher folderWatcher = new FolderWatcher();
folderWatcher.Start();
}
This is my file system watcher class:
public class FolderWatcher
{
private FileSystemWatcher watcher;
public void Start()
{
Dispatcher changeDispatcher = null;
ManualResetEvent changeDispatcherStarted = new ManualResetEvent(false);
Action changeThreadHandler = () =>
{
changeDispatcher = Dispatcher.CurrentDispatcher;
changeDispatcherStarted.Set();
Dispatcher.Run();
};
new Thread(() => changeThreadHandler()) { IsBackground = true }.Start();
changeDispatcherStarted.WaitOne();
watcher = new FileSystemWatcher(sourceFilesPath);
watcher.Filter = "*.xml";
watcher.IncludeSubdirectories = true;
watcher.InternalBufferSize = 65536;
watcher.EnableRaisingEvents = true;
watcher.Created += (sender, e) => changeDispatcher.BeginInvoke(new Action(() => OnCreated(sender, e)));
}
public static void OnCreated(Object sender, FileSystemEventArgs e)
{
//process the files here
}
}
thanks

My Windows Service failing to get started after a reboot

I can successfully Start/Stop service Manually but it fails to start on a system restart although startup type is set to 'Automatic'. On some terminal, when rebooted, service fail to start with error failed to connect DB
29052019 17:25:27 ERROR Logs.TransactionLogManager - Exception Thrown in getNewSession()
29052019 17:25:27 ERROR Logs.TransactionLogManager - Unable To Open Database Session
29052019 17:25:27 ERROR Logs.TransactionLogManager - Unable to complete network request to host "localhost".
at FirebirdSql.Data.FirebirdClient.FbConnectionInternal.Connect()
at FirebirdSql.Data.FirebirdClient.FbConnectionPoolManager.Pool.CreateNewConnectionIfPossibleImpl(FbConnectionString connectionString)
at FirebirdSql.Data.FirebirdClient.FbConnectionPoolManager.Pool.GetConnection(FbConnection owner)
at FirebirdSql.Data.FirebirdClient.FbConnection.Open()
at NHibernate.Connection.DriverConnectionProvider.GetConnection()
at NHibernate.Tool.hbm2ddl.SuppliedConnectionProviderConnectionHelper.Prepare()
at NHibernate.Tool.hbm2ddl.SchemaMetadataUpdater.GetReservedWords(Dialect dialect, IConnectionHelper connectionHelper)
at NHibernate.Tool.hbm2ddl.SchemaMetadataUpdater.Update(ISessionFactory sessionFactory)
at NHibernate.Impl.SessionFactoryImpl..ctor(Configuration cfg, IMapping mapping, Settings settings, EventListeners listeners)
at NHibernate.Cfg.Configuration.BuildSessionFactory()
at StevensCommon.Database.FluentNHibernateManager.OpenNewSession()
at StevensCommon.DAOs.StevensDAO.GetNewSession()
On development environment whilst debugging I'm not even getting any error. Assuming that on my machine the firebird services does start up before my service, still it fails to start the service on a reboot. And nothing comes up in the log.
I've added some log points within the code and they all gets logged when I Stop/Start the service manually. But nothing shows up in the log when system is rebooted and server never gets started.
Tried adding Firebird Service as a dependencies in the Project/Service Installer, set the DelayedAutoStart = true. Whatever solution I read online I've tried it but service is not starting on a reboot.
Following I'm adding starting/entry point snippet of the service.
Program.cs
static class Program
{
static void Main(string[] args)
{
TransactionLogManager.WriteServiceLog("FLAG - Before DAO Call", null);
// Check to see if we have a licence
if (LicenceFileDAO.GetLicence() != null && LicenceManager.IsLicenceActive())
{
TransactionLogManager.WriteServiceLog("FLAG - Inside DAO Block", null);
//Run the IS Service
ServiceBase[] ServicesToRun = new ServiceBase[] { new MyService() };
ServiceBase.Run(ServicesToRun);
TransactionLogManager.WriteServiceLog("FLAG - End of DAO Call", null);
}
else
{
TransactionLogManager.WriteServiceLog("No Valid Licence Found - Either A Licence Has Not Been Activated Or It Has Expired. Please Contact Support", null);
}
}
}
MyService.cs
public partial class MyService : ServiceBase
{
protected static Timer importTimer = null;
protected static Timer exportTimer = null;
protected static Timer licenceTimer = null;
protected static Timer requestTimer = null;
protected static Timer uploadTimer = null;
protected static Timer handshakeTimer = null;
protected static Timer scheduledReportTimer = null;
protected static Timer alertTimer = null;
public static Boolean ImportRunning = false;
public static Boolean ExportRunning = false;
public static Boolean RequestRunning = false;
public static Boolean UploadRunning = false;
public static Boolean HandshakeRunning = false;
public static Boolean ScheduledReportRunning = false;
public static Boolean AlertReportRunning = false;
public static int? zynkWorkflowProcessId = null;
public MyService()
{
TransactionLogManager.WriteServiceLog("FLAG - 1", null);
InitializeComponent();
InitializeServiceTimers();
try
{
MessengerService.Start();
TransactionLogManager.WriteServiceLog("Messaging Service Started", null);
}
catch (Exception e)
{
TransactionLogManager.WriteServiceLog(e.Message, e.StackTrace);
}
}
private void InitializeComponent()
{
this.eventLog1 = new System.Diagnostics.EventLog();
((System.ComponentModel.ISupportInitialize)(this.eventLog1)).BeginInit();
//
// MyService
//
this.ServiceName = "MyService";
((System.ComponentModel.ISupportInitialize)(this.eventLog1)).EndInit();
}
private void InitializeServiceTimers()
{
if (!System.Diagnostics.EventLog.SourceExists("Stevens Integration Service"))
{
System.Diagnostics.EventLog.CreateEventSource(
"Stevens Integration Service", "ServiceLog");
}
eventLog1.Source = "Stevens Integration Service";
eventLog1.Log = "ServiceLog";
TransactionLogManager.WriteServiceLog("FLAG - 2", null);
importTimer = new Timer(IntegrationServiceSettings.GetImportInterval() * 1000);
exportTimer = new Timer(IntegrationServiceSettings.GetExportInterval() * 1000);
licenceTimer = new Timer(86400 * 1000); // Daily
requestTimer = new Timer(IntegrationServiceSettings.GetRequestInterval() * 1000);
scheduledReportTimer = new Timer(30000);
alertTimer = new Timer(20000);
importTimer.Elapsed += new ElapsedEventHandler(ImportTimerElapsed);
exportTimer.Elapsed += new ElapsedEventHandler(ExportTimerElapsed);
licenceTimer.Elapsed += new ElapsedEventHandler(LicenceTimerElapsed);
requestTimer.Elapsed += new ElapsedEventHandler(RequestTimerElapsed);
scheduledReportTimer.Elapsed += new ElapsedEventHandler(ScheduledReportTimerElapsed);
alertTimer.Elapsed += new ElapsedEventHandler(AlertTimerElapsed);
TransactionLogManager.WriteServiceLog("FLAG - 3", null);
StartTimers();
TransactionLogManager.WriteServiceLog("FLAG - 4", null);
}
protected override void OnStart(string[] args)
{
eventLog1.WriteEntry("Stevens Integration Service Starting...");
TransactionLogManager.WriteServiceLog("FLAG - OnStart() Started", null);
if (StevensDAO.TestConnection())
{
eventLog1.WriteEntry("Configure Settings...");
IntegrationServiceSettings.InitialiseAll();
eventLog1.WriteEntry("Done.");
}
TransactionLogManager.WriteServiceLog("FLAG - OnStart() Finished", null);
TransactionLogManager.WriteServiceLogInfo("Started");
eventLog1.WriteEntry("Started.");
}
}
ProjectInstaller - That Includes System.ServiceProcess.ServiceProcessInstaller and System.ServiceProcess.ServiceInstaller
private System.ServiceProcess.ServiceProcessInstaller serviceProcessInstaller1;
private System.ServiceProcess.ServiceInstaller serviceInstaller1;
private MyService myService ;
private void InitializeComponent()
{
this.serviceProcessInstaller1 = new System.ServiceProcess.ServiceProcessInstaller();
this.serviceInstaller1 = new System.ServiceProcess.ServiceInstaller();
this.myService = new IntegrationService.MyService();
// serviceProcessInstaller1
this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
this.serviceProcessInstaller1.Password = null;
this.serviceProcessInstaller1.Username = null;
// serviceInstaller1
this.serviceInstaller1.Description = "My Integration Service";
this.serviceInstaller1.DisplayName = "MyService";
this.serviceInstaller1.ServiceName = "MyService";
//this.serviceInstaller1.ServicesDependedOn = new string[] { "Firebird Guardian - DefaultInstance", "Firebird Server - DefaultInstance" };
//this.serviceInstaller1.DelayedAutoStart = true;
this.serviceInstaller1.StartType = System.ServiceProcess.ServiceStartMode.Automatic;
// myService
this.myService.ExitCode = 0;
this.myService.ServiceName = "MyService";
// ProjectInstaller
this.Installers.AddRange(new System.Configuration.Install.Installer[] {
this.serviceProcessInstaller1,
this.serviceInstaller1});
}
LogPoints - On manual Start/Stop I do get following log points, but when I reboot the system nothing comes up
30052019 16:19:35 ERROR Logs.TransactionLogManager - FLAG - Before DAO Call
30052019 16:19:35 ERROR Logs.TransactionLogManager - FLAG - Inside DAO Block
30052019 16:19:35 ERROR Logs.TransactionLogManager - FLAG - 1
30052019 16:19:35 ERROR Logs.TransactionLogManager - FLAG - 2
30052019 16:19:35 ERROR Logs.TransactionLogManager - FLAG - 3
30052019 16:19:35 ERROR Logs.TransactionLogManager - FLAG - 4
30052019 16:19:35 ERROR Logs.TransactionLogManager - Messaging Service Started
30052019 16:19:35 ERROR Logs.TransactionLogManager - FLAG - OnStart() Started
30052019 16:19:35 ERROR Logs.TransactionLogManager - Client 1 is now connected!
30052019 16:19:36 ERROR Logs.TransactionLogManager - FLAG - OnStart() Finished

Using recorded wave file (NAudio)

This is the code I use to record an audio file:
internal class AudioRecorder
{
public WaveIn waveSource = null;
public WaveFileWriter waveFile = null;
public string RECORDING_PATH;
public AudioRecorder(string fileName)
{
RECORDING_PATH = fileName;
}
public void Start()
{
waveSource = new WaveIn();
waveSource.WaveFormat = new WaveFormat(44100, 1);
waveSource.DeviceNumber = 0;
waveSource.DataAvailable += new EventHandler<WaveInEventArgs>(waveSource_DataAvailable);
waveSource.RecordingStopped += new EventHandler<StoppedEventArgs>(waveSource_RecordingStopped);
waveFile = new WaveFileWriter(RECORDING_PATH, waveSource.WaveFormat);
System.Timers.Timer t = new System.Timers.Timer(30000);
t.Elapsed += new ElapsedEventHandler(Stop);
waveSource.StartRecording();
t.Start();
}
private void Stop(object sender, ElapsedEventArgs args)
{
waveSource.StopRecording();
}
private void waveSource_DataAvailable(object sender, WaveInEventArgs e)
{
if (waveFile != null)
{
waveFile.Write(e.Buffer, 0, e.BytesRecorded);
waveFile.Flush();
}
}
private void waveSource_RecordingStopped(object sender, StoppedEventArgs e)
{
if (waveSource != null)
{
waveSource.Dispose();
waveSource = null;
}
if (waveFile != null)
{
waveFile.Dispose();
waveFile = null;
}
}
}
In the main method I do:
AudioRecorder r = new AudioRecorder(dialog.FileName);
r.Start();
FileInfo file = new FileInfo(r.RECORDING_PATH);
// Do somehting with the recorded audio //
The problem is that when I do r.Start() the thread does not block and keeps running. So I get a corrupt file error. When I try things like Thread.Sleep to keep the thread waiting until recording finishes, this time the AudioRecorder code does not work well (i.e. recording never finishes).
Any ideas about what should I do to correctly wait the recording to finish so that I can safely use the recorded file ?
If you want to record for 30 seconds exactly, just call StopRecording in the DataAvailable event handler once you have enough data. There is absolutely no need for a complicated threading strategy. I do exactly this in the open source .NET voice recorder application.
Dispose the WaveFileWriter in the RecordingStopped event.
If you absolutely must have a blocking call, then use WaveInEvent, and wait on an event which is set in the RecordingStopped handler, as suggested by Rene. By using WaveInEvent, you remove the need for windows message pump to be operational.
You use a ManualResetEvent to wait for the Stop event to be called, giving other threads a change to proceed.
I've only added the new bits...
internal class AudioRecorder
{
private ManualResetEvent mre = new ManualResetEvent(false);
public void Start()
{
t.Start();
while (!mre.WaitOne(200))
{
// NAudio requires the windows message pump to be operational
// this works but you better raise an event
Application.DoEvents();
}
}
private void Stop(object sender, ElapsedEventArgs args)
{
// better: raise an event from here!
waveSource.StopRecording();
}
private void waveSource_RecordingStopped(object sender, EventArgs e)
{
/// ... your code here
mre.Set(); // signal thread we're done!
}
It is good idea to avoid any multi-threaded code if it is not required and Mark's answer is explaining this perfectly.
However, if you are writing a windows application and the requirement is to record 30 seconds than it is a must not to block a main thread in waiting (for 30 seconds). The new async C# feature can be very handy here. It will allow you to keep code logic straightforward and implement waiting in a very efficient way.
I have modified your code slightly to show how the async feature can be used in this case.
Here is the Record method:
public async Task RecordFixedTime(TimeSpan span)
{
waveSource = new WaveIn {WaveFormat = new WaveFormat(44100, 1), DeviceNumber = 0};
waveSource.DataAvailable += new EventHandler<WaveInEventArgs>(waveSource_DataAvailable);
waveSource.RecordingStopped += new EventHandler<StoppedEventArgs>(waveSource_RecordingStopped);
waveFile = new WaveFileWriter(RECORDING_PATH, waveSource.WaveFormat);
waveSource.StartRecording();
await Task.Delay(span);
waveSource.StopRecording();
}
Example of using Record from click handler of WPF app:
private async void btnRecord_Click(object sender, RoutedEventArgs e)
{
try
{
btnRecord.IsEnabled = false;
var fileName = Path.GetTempFileName() + ".wav";
var recorder = new AudioRecorder(fileName);
await recorder.RecordFixedTime(TimeSpan.FromSeconds(5));
Process.Start(fileName);
}
finally
{
btnRecord.IsEnabled = true;
}
}
However, you have to watch out for timing here. Task.Delay does not guarantee that it will continue execution after the exact specified time span. You might get records slightly longer than is required.

how to check for a license every day in Windows Service [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Use DispatcherTimer with Windows Service
i want to check for a license every day in my windows Service
i try use DispatcherTimer but not working
this is what I tried to do
public OIMService()
{
InitializeComponent();
_dispatcherTimer = new DispatcherTimer();
this.ServiceName = "OIMService";
if (!System.Diagnostics.EventLog.SourceExists("OIM_Log"))
{
EventLog.CreateEventSource("OIM_Log", "OIMLog");
}
EventLog.Source = "OIM_Log";
EventLog.Log = "OIMLog";
_sc = new ServiceController("OIMService");
_helpers = new ValidationHelpers();
StartTimer();
}
private void StartTimer()
{
_dispatcherTimer.Tick += new EventHandler(DispatcherTimerTick);
_dispatcherTimer.Interval = new TimeSpan(0, 0, Convert.ToInt32(time));
_dispatcherTimer.IsEnabled = true;
_dispatcherTimer.Start();
}
private void DispatcherTimerTick(object sender, EventArgs e)
{
var helpers = new ValidationHelpers();
if (!helpers.IsValid())
this.Stop();
}
You can use a new Thread like this:
bool cancelJob = false;
ThreadStart ts = new System.Threading.ThreadStart(CheckLicence);
Thread thMain = new System.Threading.Thread(ts);
thMain.Start();
void CheckLicence()
{
//you can use cancelJob to break the thread...
while (!this.cancelJob)
{
//TODO: code to check your licence...
//sleep for 1 hour...
System.Threading.Thread.Sleep(3600000);
}
}
When you want to stop the service make sure you terminate the thread also, like this:
thMain.Abort();

how to make service act dynamically based on service running condition

hi friends i was trying to make my service act dynamically... i have set time for my service about for 2 min ,if suppose it was doin huge amount of work means it will exceeds that 2 min time limit then we need to check the service condition if work is pending means we need to run that instance until upto finish
so that i have tried this below code on googling ... i m having method were i need to cooperate in below service, can any one help me
public static void StartService(string serviceName, int timeoutMilliseconds)
{
ServiceController service = new ServiceController(serviceName);
try
{
TimeSpan timeout = TimeSpan.FromMilliseconds(timeoutMilliseconds);
service.Start();
service.WaitForStatus(ServiceControllerStatus.Running, timeout);
}
catch
{
// ...
}
}
as of now i m doing this below logic
protected override void OnStart(string[] args)
{
// my service name
Workjob("FTSCSVGenerator");
// ad 1: handle Elapsed event and CsvGenFromDatabase is method which i have to executed
timerjob.Elapsed += new ElapsedEventHandler(CsvGenFromDatabase);
// ad 2: set interval to 1 minute (= 60,000 milliseconds)
timerjob.Interval = Convert.ToDouble(DueTime);
// ////ad 3: enabling the timer
timerjob.Enabled = true;
eventLog1.WriteEntry("my service started");
}
protected override void OnStop()
{
eventLog1.WriteEntry("my service stopped");
}
private void Workjob(string servicename )
{
ServiceController servicecsv = new ServiceController(servicename);
if ((servicecsv.Status.Equals(ServiceControllerStatus.Stopped)) || (servicecsv.Status.Equals(ServiceControllerStatus.StopPending)))
{
// Start the service if the current status is stopped.
servicecsv.Start( );
}
else
{
// Stop the service if its status is not set to "Stopped".
servicecsv.Stop();
}
}
I have built services that operate in a similar manner before, my advice would be to NOT start and stop the service from external code. Instead, apply the Timer methodology within the service itself, which should always be running. On TimerElapsed, do work and then return to an idle state. Thus alleviating the need to start and stop.
Further, I would protect the "stop" of a service to not allow the stop if the service is "working"
Sample Code
Note: I employ a process I call "zeroing" with my timer. Zeroing, in my context, is the process of getting the events to fire on zero seconds of every minute. To do that, I first set the time to fire every second and I check to see if the seconds part of the current time is zero, once that occurs I switch the timer elapse to every minute. I do this to give myself some sanity while testing.
Also, my scheduling is configurable so every minute when it "ticks" i check my config to see if the process "should" execute. I do so with the following Xml Schema:
<?xml version="1.0" encoding="utf-8"?>
<ScheduleDefinition xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ScheduleInterval>1</ScheduleInterval>
<ScheduleUnits>min</ScheduleUnits>
<DailyStartTime>1753-01-01T08:00:00</DailyStartTime>
<ExcludedWeekDays>
<string>Sunday</string>
<string>Saturday</string>
</ExcludedWeekDays>
<ExcludedDates>
<string>12/25</string>
<string>02/02</string>
<string>03/17</string>
</ExcludedDates>
<DailyRunTimes>
<!-- code ommitted for size // -->
</DailyRunTimes>
</ScheduleDefinition>
Finally, this code sample is for a DataSync Services, so any references to "DataMigrationService" or "DataMigrationManager" are my own custom classes and are used as an abstraction to give me an object to control within the service.
... here's the code:
using System;
using System.Diagnostics;
using System.Reflection;
using System.ServiceProcess;
using System.Threading;
using System.Xml;
using System.Xml.Serialization;
using DataMigration.Configuration;
using DataMigration.ObjectModel;
namespace DataSyncService
{
public partial class DataSyncService : ServiceBase
{
#region Private Members
private System.Timers.Timer _timer = null;
private SimpleScheduleManager.ScheduleDefinition _definition = null;
private DataMigrationManager _manager = new DataMigrationManager();
#endregion
#region Constructor(s)
public DataSyncService()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(AssemblyResolver.Resolve);
InitializeComponent();
}
~DataSyncService()
{
_manager = null;
_definition = null;
_timer = null;
}
#endregion
#region Public Method(s)
protected override void OnStart(string[] args)
{
Assembly assembly = Assembly.GetExecutingAssembly();
_manager.ProcessMonitor.Logger.Debug("Assembly Version: ", assembly.GetName().FullName);
assembly = null;
SetScheduleFromConfigurationFile();
_timer = new System.Timers.Timer(1000);
_timer.AutoReset = true;
_timer.Enabled = true;
_timer.Elapsed += new System.Timers.ElapsedEventHandler(_timer_ZeroingProcess);
_timer.Start();
}
protected override void OnStop()
{
_timer.Stop();
_timer.Enabled = false;
_timer = null;
// block if the Process is active!
if (_manager.State == DataMigrationState.Processing)
{
// I invented my own CancellableAsyncResult (back in the day), now you can use CancellationTokenSource
CancellableAsyncResult result = _manager.RequestCancel() as CancellableAsyncResult;
while (!result.IsCompleted) { Thread.Sleep(ServiceConstants.ThreadSleepCount); }
try
{
result.EndInvoke();
}
catch (Exception ex)
{
ProcessMonitorMessage message = ProcessMonitorMessage.GetErrorOccurredInstance();
message.EventType = ProcessMonitorEventType.ProcessAlert;
message.Severity = ProcessMessageSeverity.ErrorStop;
message.SubjectLine = "Error while stopping service. ";
message.EventDescription = ex.Message;
_manager.ProcessMonitor.ReportError(message);
}
}
}
#endregion
#region Private Method(s)
private bool MigrationIsScheduledToRunNow()
{
DateTime now = DateTime.Now;
foreach (string dowString in _definition.ExcludedWeekDays)
{
if (now.DayOfWeek.ToString().Equals(dowString))
{
Trace.WriteLine("Today is " + dowString, "Excluded by Schedule definition");
return false;
}
}
foreach (string datePart in _definition.ExcludedDates)
{
string dateString = datePart + "/2008"; // 2008 is a leap year so it "allows" all 366 possible dates.
DateTime excludedDate = Convert.ToDateTime(dateString);
if (excludedDate.Day.Equals(now.Day) && excludedDate.Month.Equals(now.Month))
{
Trace.WriteLine("Today is " + datePart, "Excluded by Schedule definition");
return false;
}
}
foreach (DateTime runTime in _definition.DailyRunTimes)
{
if (runTime.Hour.Equals(now.Hour) && runTime.Minute.Equals(now.Minute))
{
Trace.WriteLine("Confirmed Scheduled RunTime: " + runTime.TimeOfDay.ToString(), "Included by Schedule definition");
return true;
}
}
return false;
}
/// <summary>
/// Load Scheduling Configuration Options from the Xml Config file.
/// </summary>
private void SetScheduleFromConfigurationFile()
{
string basePath = AppDomain.CurrentDomain.BaseDirectory;
if (basePath.EndsWith("\\")) { basePath = basePath.Substring(0, basePath.Length - 1); }
string path = string.Format("{0}\\Scheduling\\scheduledefinition.xml", basePath);
_manager.ProcessMonitor.Logger.Debug("Configuration File Path", path);
XmlSerializer serializer = new XmlSerializer(typeof(SimpleScheduleManager.ScheduleDefinition));
XmlTextReader reader = new XmlTextReader(path);
reader.WhitespaceHandling = WhitespaceHandling.None;
_definition = serializer.Deserialize(reader) as SimpleScheduleManager.ScheduleDefinition;
reader = null;
serializer = null;
}
#endregion
#region Timer Events
private void _timer_ZeroingProcess(object sender, System.Timers.ElapsedEventArgs e)
{
if (DateTime.Now.Second.Equals(0))
{
_timer.Interval = 60000;
_timer.Elapsed -= new System.Timers.ElapsedEventHandler(_timer_ZeroingProcess);
_timer.Elapsed += new System.Timers.ElapsedEventHandler(_timer_Elapsed);
_timer_Elapsed(sender, e);
}
}
private void _timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
_manager.ProcessMonitor.Logger.Info("Timer Elapsed", DateTime.Now.ToString());
if (MigrationIsScheduledToRunNow())
{
switch (_manager.State)
{
case DataMigrationState.Idle:
_manager.ProcessMonitor.Logger.Info("DataMigration Manager is idle. Begin Processing.");
_manager.BeginMigration();
break;
case DataMigrationState.Failed:
_manager.ProcessMonitor.Logger.Warn("Data Migration is in failed state, Email <NotificationRecipients> alerting them.");
break;
default:
_manager.ProcessMonitor.Logger.Warn("DataMigration Manager is still processing. Skipping this iteration.");
break;
}
}
}
#endregion
}
}

Categories