Control Your Windows Services with System.ServiceProcess.ServiceController

I was trying to do some work in a VirualPC image today, and each time I tried to load the VPC image, I got a "there is not enough memory" message.  This happens on a pretty frequent basis.  Instead of lowering the memory required for the VPC, I wanted to stop any services that are running and consuming memory. 

It would have been nice to enumerate through the processes and stop them all:

foreach(ServiceController sc1 in ServiceController.GetServices())
{
if(sc1.CanStop && sc1.Status == ServiceControllerStatus.Running)
{
sc1.Stop();
}
}

However, that would be a bad thing as there are many services running that I need.  Instead, I decided to use a configuration file to hold the specific names of the services that I wanted to stop.  But wait... when I close the VPC image, I will likely need those services again...

The result utility is here, I threw it together pretty quickly.  I am using recursion to make sure all the services we depend on are started when we want to start, and all of the services that depend on us are stopped before we stop.

using System;
using System.ServiceProcess;
using System.Configuration;
using System.Collections.Specialized;

namespace ServiceControl
{
class ServiceUtil
{
[STAThread]
static void Main(string[] args)
{
//Get the keys from the section in the config file.
NameValueCollection nv = (NameValueCollection)ConfigurationSettings.GetConfig("ServiceStopper");
foreach(string key in nv.AllKeys)
{
//Each key name is the name of the service in SCM
ServiceProcessor s = new ServiceProcessor(key);
if(args[0].ToUpper() == "START")
s.Process(ServiceProcessor.ServiceControlCommand.Start);
else
s.Process(ServiceProcessor.ServiceControlCommand.Stop);
}
}
}

 internal class ServiceProcessor
{
#region nested types
public enum ServiceControlCommand
{
Start,
Stop
}
#endregion

  private string _serviceName;

#region ctors
public ServiceProcessor(string serviceName)
{
_serviceName = serviceName;
}
#endregion

  #region methods
public void Process(ServiceControlCommand cmd)
{
ServiceController sc = new ServiceController(_serviceName);
ProcessServiceController(sc,cmd, 1);
}

  private void ProcessServiceController(ServiceController sc, ServiceControlCommand cmd, int indent)
{
if(cmd == ServiceControlCommand.Start)
{
//Start all the services we depend on
foreach(ServiceController child in sc.ServicesDependedOn)
{
ProcessServiceController(child, cmd, indent + 1);
}
}
else
{
//Process all the services that depend on us
foreach(ServiceController child in sc.DependentServices)
{
ProcessServiceController(child, cmd, indent + 1);
}
}

string pad = new string(' ',indent);
Console.Write(pad);
Console.Write(sc.ServiceName + ": ");

   //Process the current service
if(cmd == ServiceControlCommand.Start )
{
StartService(sc);
}
else
{
StopService(sc);
}
}

private void StopService(ServiceController sc)
{
//If it is running and we can stop it...
if (sc.CanStop && sc.Status == ServiceControllerStatus.Running)
{
Console.Write("Stopping... ");
try
{
sc.Stop();
//Wait until status is Stopped, or time out after
//10 seconds have elapsed
TimeSpan t = new TimeSpan(0,0,0,20);
sc.WaitForStatus(ServiceControllerStatus.Stopped,t);
}
catch (Exception oops)
{
Console.WriteLine( oops.Message );
}
}
Console.WriteLine(sc.Status);
}

  private void StartService(ServiceController sc)
{
//We only want to start those services that are stopped
if (sc.Status == ServiceControllerStatus.Stopped )
{
Console.Write("Starting... ");
try
{
sc.Start();
//Wait until status is Running, or time out after
//10 seconds have elapsed
TimeSpan t = new TimeSpan(0,0,0,20);
sc.WaitForStatus(ServiceControllerStatus.Running ,t);
}
catch (Exception oops)
{
Console.Write(sc.Status + ": ");
Console.Write( oops.Message);
}
}
Console.WriteLine(sc.Status);
}
#endregion
}
}

The configuration file for the console application:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="ServiceStopper" type="System.Configuration.NameValueSectionHandler"/>
</configSections>
<ServiceStopper>
<add key="MSMQ" value="Microsoft Message Queueing" />
<add key="SMTPSVC" value="SMTP Service" />
<add key="W3SVC" value="World Wide Web Publishing Service" />
<add key="Irmon" value="Infrared Monitor" />
<add key="IISADMIN" value="IIS Admin" />
<add key="helpsvc" value="Help and Support" />
<add key="ERsvc" value="Error Reporting Service" />
</ServiceStopper>
</configuration>