如何在C#中检查Windows服务是否已安装

88
我编写了一个Windows服务,它公开了一个WCF服务,供安装在同一台机器上的GUI使用。当我运行GUI时,如果无法连接到服务,则需要知道是因为服务应用程序尚未安装还是因为服务未运行。如果是前者,我将要安装它(如此处所述);如果是后者,我将要启动它。
问题是:如何检测服务是否已安装,然后在检测到已安装后如何启动它?
6个回答

159

使用:

// 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);

1
使用以下代码更好:using (var sc = ServiceController.GetServices().FirstOrDefault(s => s.ServiceName == "myservice")) - Alexandru Dicu
7
那种方式更好在哪里?如果.GetServices()返回100个ServiceController对象,你处理了其中一个而忽略了其他99个,这真的有明显的改进吗?我自己不这样认为。 - Allon Guralnek
可能遍历数百个服务并不是一个好主意。最好只使用带有名称参数的ServiceController构造函数。 - Thomas Kjørnes

42
您也可以使用以下内容...
using System.ServiceProcess; 
... 
var serviceExists = ServiceController.GetServices().Any(s => s.ServiceName == serviceName);

3
在我看来,这是检查服务是否存在最优雅的方式,只需一行代码,利用 Linq 的强大功能。顺便说一下,.Any() 返回一个布尔值,这正是当你询问一个是/否问题时所需要的 :-) - Alex X.
3
如果您需要在远程计算机上检查服务,请使用GetServices(string) - ShooShoSha

10

实际上,像这样循环:

foreach (ServiceController SC in ServiceController.GetServices())

如果您的应用程序所在的账户没有查看服务属性的权限,可能会抛出“访问被拒绝”的异常。另一方面,即使没有这样名称的服务存在,您也可以安全地执行此操作:

ServiceController SC = new ServiceController("AnyServiceName");

如果服务不存在,访问其属性将导致InvalidOperationException。因此,以下是一种安全检查服务是否已安装的方法:

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();
}

谢谢!最后 { SC.Close(); } - Cel
6
为什么不使用 using 语句将整个内容包装起来呢?这样可以避免使用 finally{SC.Close()},因为 using 语句会自动处理资源释放的问题。 使用 using 语句包装 ServiceController 对象 ("MyServiceName") 即可。 - bill

4

我认为这是这个问题的最佳答案。没有必要添加额外的处理来验证服务是否存在,因为如果不存在,它会抛出异常。你只需要捕获它即可。如果你在整个方法中使用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       
 }     
}

2
这并不是一个很好的答案。(1) 通过异常来管理代码是非常糟糕的做法 - 效率低下且缓慢,(2) 而被接受的答案则是整洁、简明并完美地回答了要求。在你自己的回答之前,你有看过它吗? - Shaul Behr
显然你不像被接受的答案那样懂得阅读,因为他明确询问了如何启动服务,而这并未包含在原始答案中。 - bill
显然,你不知道如何正确编写代码。正如@Shaul Behr所说,你的方法是一种糟糕的实践,因为它是低效和缓慢的。而且,自夸自己的答案可能会让情况变得更糟:在SO上(以及全世界),自我吹嘘从来都不被认为是好行为。 - Yoda
1
显然我不知道哪个更糟糕...你试图用不正确的语法来表达自己,看起来像是你知道在说什么,还是你没有意识到你刚刚评论了一个2014年的帖子....哈哈。 - bill
1
这是唯一一个考虑到如果有人在检查服务是否存在和与其交互之间删除服务会发生什么的答案。 - Mike Caron
1
只是补充一下 - 在我看来,异常处理仍然比枚举所有服务更快,也比不释放它们更好。此外,人们可以通过检查所有已安装的服务来获取异常信息。 - n0ne

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;
    }

2

对于非Linq,您可以像这样遍历数组:

using System.ServiceProcess;

bool serviceExists = false
foreach (ServiceController sc in ServiceController.GetServices())
{
    if (sc.ServiceName == "myServiceName")
    {
         //service is found
         serviceExists = true;
         break;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接