87

I've written a Windows Service that exposes a WCF service to a GUI installed on the same machine. When I run the GUI, if I can't connect to the service, I need to know if it's because the service app hasn't been installed yet, or if it's because the service is not running. If the former, I'll want to install it (as described here); if the latter, I'll want to start it up.

Question is: how do you detect if the service is installed, and then having detected that it's installed, how do you start it up?

Community
  • 1
  • 1
Shaul Behr
  • 36,951
  • 69
  • 249
  • 387

6 Answers6

157

Use:

// add a reference to System.ServiceProcess.dll
using System.ServiceProcess;

// ...
ServiceController ctl = ServiceController.GetServices()
    .FirstOrDefault(s => s.ServiceName == "myservice");
if(ctl==null)
    Console.WriteLine("Not installed");
else    
    Console.WriteLine(ctl.Status);
Erik Philips
  • 53,428
  • 11
  • 128
  • 150
Aliostad
  • 80,612
  • 21
  • 160
  • 208
  • 1
    using (var sc = ServiceController.GetServices().FirstOrDefault(s => s.ServiceName == "myservice")) - I think this is a better approach. – Alexandru Dicu Feb 18 '13 at 09:02
  • 7
    @alexandrudicu: How is that a better approach? If `.GetServices()` returns 100 `ServiceController` objects, and you've disposed one out of the hundred while ignoring the rest, is that really appreciably better? I wouldn't say so myself. – Allon Guralnek Nov 11 '13 at 08:55
  • Possibly iterating through hundreds of services is not a good idea. Better just use the ServiceController constructor with the name parameter. – Thomas Kjørnes Feb 26 '23 at 03:06
42

You could use the following as well..

using System.ServiceProcess; 
... 
var serviceExists = ServiceController.GetServices().Any(s => s.ServiceName == serviceName);
  • 3
    IMO, this is the most elegant way to check if your service exists. Just one line of code, leveraging the power of Linq. And by the way, .Any() returns a bool which is exactly what you want when asking a yes/no question :-) – Alex X. Dec 09 '15 at 15:45
  • 3
    If you need to check services on a remote machine, use [`GetServices(string)`](https://msdn.microsoft.com/en-us/library/s21fd6th.aspx) – ShooShoSha Sep 19 '16 at 16:50
10

Actually looping like this:

foreach (ServiceController SC in ServiceController.GetServices())

may throw Access Denied exception if the account under which your application is running doesn't have rights to view service properties. On the other hand, you can safely do this even if no service with such name exist:

ServiceController SC = new ServiceController("AnyServiceName");

But accessing its properties if service doesn't exist will result in InvalidOperationException. So here's a safe way to check if a service is installed:

ServiceController SC = new ServiceController("MyServiceName");
bool ServiceIsInstalled = false;
try
{
    // actually we need to try access ANY of service properties
    // at least once to trigger an exception
    // not neccessarily its name
    string ServiceName = SC.DisplayName;
    ServiceIsInstalled = true;
}
catch (InvalidOperationException) { }
finally
{
    SC.Close();
}
ttaaoossuuuu
  • 7,786
  • 3
  • 28
  • 58
  • thanks! and would you want to finish with: finally { SC.Close(); } – Cel Feb 06 '14 at 09:14
  • 6
    Why not wrap the entire thing in using? That will remove the need for finally{SC.Close()} since a using statement will automatically dispose. using(ServiceController SC = new ServiceController("MyServiceName")) – bill Jun 12 '14 at 14:50
4

I think this is the best answer for this question. There is no need to add extra processing to verify if the service exists, since it will throw an exception if it doesn't. You just need to catch it. You also do not need to close() the connecting if you wrap the entire method in using().

using (ServiceController sc = new ServiceController(ServiceName))
{
 try
 {
  if (sc.Status != ServiceControllerStatus.Running)
  {
    sc.Start();
    sc.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 10));
    //service is now Started        
  }      
  else
    //Service was already started
 }
 catch (System.ServiceProcess.TimeoutException)
 {
  //Service was stopped but could not restart (10 second timeout)
 }
 catch (InvalidOperationException)
 {
   //This Service does not exist       
 }     
}
bill
  • 711
  • 1
  • 8
  • 19
  • 2
    Not a very good answer at all. (1) Managing code by exceptions is very bad practice - inefficient and slow, and (2) the accepted answer is neat, concise and answers the requirements perfectly. Did you look at it before diving in with your own answer? – Shaul Behr Jun 12 '14 at 15:10
  • Apparently you don't know how to read like the accepted answer, since he clearly asked how to start the service as well, which was not included in the original answer. – bill Jun 12 '14 at 16:47
  • Apparently, you don't know how to properly write code. As @Shaul Behr already stated, your approach is bad practice as it's inefficient and slow. Stating your _own_ answer is probably the best, makes it even worse: self-praise is never considered a good behavior here on SO (and probably all over the world, too). – Yoda Sep 01 '17 at 10:50
  • 1
    This is the only answer that accounts for what happens if someone deletes the service in between checking for its existence and interacting with it – Mike Caron Mar 09 '20 at 15:35
  • 1
    Just to add - this is the correct answer, IMO exceptions handling is still faster than enumerating all services and better than not disposing them. Also one can get exception inspecting all installed services anyway. – n0ne Nov 09 '21 at 06:02
2

For non-linq, you can just iterate thru the array like this:

using System.ServiceProcess;

bool serviceExists = false
foreach (ServiceController sc in ServiceController.GetServices())
{
    if (sc.ServiceName == "myServiceName")
    {
         //service is found
         serviceExists = true;
         break;
    }
}
ZTAN
  • 71
  • 4
2
 private bool ServiceExists(string serviceName)
    {
        ServiceController[] services = ServiceController.GetServices();
        var service = services.FirstOrDefault(s => string.Equals(s.ServiceName, serviceName, StringComparison.OrdinalIgnoreCase));
        return service != null;
    }