Published
Wednesday, February 20, 2008 9:54 AM
by
mtaulty
Just sharing a piece of code. I'm sure this isn't quite perfect but someone wanted a Windows Service which would run up some processes with the option to restart them if they exited.
I think the Windows Task Scheduler will do some of this but I don't think it does all of it.
The service is driven by its configuration file which is simply;
<configuration>
<configSections>
<section name="processLauncherConfig"
type="ProcLauncher.ProcessLauncherConfig, ProcLauncher"/>
</configSections>
<processLauncherConfig xmlns="urn:proclauncher-com">
<processes>
<process executable="consoleapplication11.exe"
restart="true"
killOnStop="true">
<arguments>
<argument value="one"/>
<argument value="two"/>
</arguments>
</process>
</processes>
</processLauncherConfig>
</configuration>
With that in place, I've just got a bit of service code;
using System;
using System.Configuration;
using System.Diagnostics;
using System.ServiceProcess;
using System.Text;
using System.Collections.Generic;
using System.Threading;
namespace ProcLauncher
{
public partial class ProcService : ServiceBase
{
public ProcService()
{
InitializeComponent();
dictionaryLock = new object();
}
protected override void OnStart(string[] args)
{
EventLog.WriteEntry(Messages.MsgServiceStart, EventLogEntryType.Information);
if (!LoadConfig())
{
EventLog.WriteEntry(Messages.MsgConfigNotFound, EventLogEntryType.Error);
this.Stop();
}
// Move to the threadpool so we can return quickly.
ThreadPool.QueueUserWorkItem(StartProcesses);
}
void StartProcesses(object state)
{
lock (dictionaryLock)
{
processesStarted = new Dictionary<int, process>();
}
foreach (process proc in loadedConfig.processes)
{
StartProcess(proc);
}
}
private void StartProcess(process proc)
{
ProcessStartInfo psi = new ProcessStartInfo()
{
FileName = proc.executable,
Arguments = BuildProcessArguments(proc)
};
try
{
Process p = Process.Start(psi);
lock (dictionaryLock)
{
processesStarted.Add(p.Id, proc);
}
if (proc.restart)
{
p.EnableRaisingEvents = true;
p.Exited += OnProcessExited;
}
else
{
p.Dispose();
}
}
catch
{
}
}
private static string BuildProcessArguments(process proc)
{
string args = null;
if ((proc.arguments != null) && (proc.arguments.Count > 0))
{
StringBuilder sb = new StringBuilder(proc.arguments[0].value);
for (int i = 1; i < proc.arguments.Count; i++)
{
sb.AppendFormat(" {0}", proc.arguments[i].value);
}
args = sb.ToString();
}
return (args);
}
void OnProcessExited(object sender, EventArgs e)
{
Process p = (Process)sender;
process proc = null;
lock (dictionaryLock)
{
proc = processesStarted[p.Id];
processesStarted.Remove(p.Id);
}
p.Dispose();
StartProcess(proc);
}
bool LoadConfig()
{
loadedConfig = (ProcessLauncherConfig)ConfigurationManager.GetSection(
configSection);
return (loadedConfig != null);
}
protected override void OnStop()
{
Dictionary<int, process> copy = null;
lock (dictionaryLock)
{
copy = processesStarted;
processesStarted = null;
}
foreach (KeyValuePair<int, process> entry in copy)
{
if (entry.Value.killOnStop)
{
using (Process p = Process.GetProcessById(entry.Key))
{
p.Kill();
}
}
}
EventLog.WriteEntry(Messages.MsgServiceStop, EventLogEntryType.Information);
}
private ProcessLauncherConfig loadedConfig;
private Dictionary<int, process> processesStarted;
private object dictionaryLock;
private static string configSection = "processLauncherConfig";
}
}
I didn't spend a huge amount of time on it so there might be a few bugs in there but it's a starting point.
I've put the project file up here for download. Note that I use the Configuration Section Designer for the config file stuff here so if you haven't got that installed you'd want to just make sure that the MyConfigCode.cs file was in the project as that's all you actually need to read the config file.