在不使用异常处理的情况下检查特定计算机上是否存在某个服务

56

我不知道是否有更好的方法来做这件事,所以才会提出这个问题。 我可以使用以下代码检查特定机器上是否存在某个服务:

bool DoesServiceExist(string serviceName, string machineName)
{
    ServiceController controller = null;
    try
    {
        controller = new ServiceController(serviceName, machineName);
        controller.Status;
        return true;
    }
    catch(InvalidOperationException)
    {
        return false;
    }
    finally
    {
         if (controller != null)
         {
             controller.Dispose();
         }
    }
}

但这似乎是一种低效的解决方法(因为需要使用异常处理)。是否有更好的方法来检查服务是否存在?请注意 - 我最近切换到了 .Net 4.0,如果有人知道在 4.0 中有更好的解决方案,那将是可接受的。

编辑: 这是一个示例 C# 控制台应用程序,用于测试我的示例代码以及 GetServices 代码示例的性能。在我的测试中,当服务不存在时,GetServices 更具性能优势,但当服务存在时速度会慢两倍:

    static void Main(string[] args)
    {
        string serviceName = string.Empty;
        string machineName = string.Empty;

        var sw = new Stopwatch();
        sw.Reset();
        sw.Start();
        for (int i = 0; i < 1000; i++)
        {
            ServiceExistsException(serviceName, machineName);
        }
        sw.Stop();
        Console.WriteLine("Elapsed time: " + sw.ElapsedMilliseconds.ToString());
        sw.Reset();
        sw.Start();
        for (int i = 0; i < 1000; i++)
        {
            ServiceExistsGetList(serviceName, machineName);
        }
        sw.Stop();
        Console.WriteLine("Elapsed time: " + sw.ElapsedMilliseconds.ToString());

        Console.WriteLine("Done");
        Console.ReadLine();
    }

    static bool ServiceExistsException(string serviceName, string machineName)
    {
        ServiceController controller = null;
        try
        {
            controller = new ServiceController(serviceName, machineName);
            string name = controller.DisplayName;
            return true;
        }
        catch (InvalidOperationException)
        {
            return false;
        }
        finally
        {
            if (controller != null)
            {
                controller.Dispose();
            }
        }
    }

    static bool ServiceExistsGetList(string serviceName, string machineName)
    {
        ServiceController[] services = null;
        try
        {
            services = ServiceController.GetServices(machineName);
            var service = services.FirstOrDefault(s => s.ServiceName == serviceName);
            return service != null;
        }
        finally
        {
            if (services != null)
            {
                foreach (ServiceController controller in services)
                {
                    controller.Dispose();
                }
            }
        }
    }
}
3个回答

98

您可以使用ServiceController.GetServices()方法获取机器上的所有服务,然后查找其中是否存在您正在寻找的服务名称:

bool DoesServiceExist(string serviceName, string machineName)
{
    ServiceController[] services = ServiceController.GetServices(machineName);
    var service = services.FirstOrDefault(s => s.ServiceName == serviceName);
    return service != null;
}

FirstOrDefault()扩展方法(来自System.Linq)将返回具有给定名称的第一个服务,如果没有匹配,则返回null


针对您的速度问题:

对于单个方法调用,两种方法的差异微不足道,无论是否找到服务。只有当您调用此方法数千次时才会成为问题-在这种情况下,获取服务列表并记住它。


3
@pstrjds - 你应该查一下“performative”的含义,这样至少在适当或传统的情境下,你就能使用它了。 - CRice
3
@CRice - 你是正确的,我以前从没查过这个词,只是在讨论性能时听到过它的使用。我刚才查了一下,哇,它有一个完全不同的含义。我真希望这里的上下文可以显示出我的意图,并且不会导致任何误解理解我所提出的问题。下次我一定会先在英语堆栈交换发布所有问题,以确保我有正确的语法和拼写内容。 - pstrjds
你可能想考虑使用s.ServiceName.Equals(serviceName, StringComparison.CurrentCultureIgnoreCase)替换==运算符。 - ZivS
1
@kayleeFrye_onDeck 完全没有危险。它们是等价的。 - adrianbanks
2
请注意,通过GetServices获取所有服务列表需要SC_MANAGER_*权限,而查询特定服务则不需要。在某些环境中,您可能无法访问枚举服务列表的权限。 - Tim Sparkles
显示剩余6条评论

20

和adrianbanks一样的方法,但代码稍微更加紧凑。如果你正在使用LINQ,你可以使用任何语句返回你想要的内容。此外,如果你在本地计算机上进行检查,就不需要提供计算机名称。

bool DoesServiceExist(string serviceName)
{
   return ServiceController.GetServices().Any(serviceController => serviceController.ServiceName.Equals(serviceName));
}

1
你没有处理 ServiceController,这肯定是不好的实践吧? - Robula
ServiceController.GetServices()是一个静态方法,因此没有实例需要处理。在底层,Any()会调用dispose。https://dev59.com/rFwZ5IYBdhLWcg3wC8b4 - JimmyV
@JimmyV,你的链接详细说明了迭代器何时被释放,而不是其中的元素。 GetServices返回一个IDisposable对象(ServiceController对象)的IEnumerable,上述代码中没有任何自动处理这些对象的释放。 - Matt Burnell

4
在Mike的回答基础上构建。与Dictionary.TryGetValue相同的概念。
    /// <summary>
    /// Gets a <see cref="ServiceController"/> given the specified <see cref="pServiceName"/>.
    /// </summary>
    /// <param name="pServiceName">The name of the service.</param>
    /// <param name="pService">The <see cref="ServiceController"/> associated with the name.</param>
    /// <returns>
    /// <see cref="bool.True"/> if the <see cref="ServiceController"/> exists; otherwise <see cref="bool.False"/>.
    /// </returns>
    private static bool TryGetService(string pServiceName, out ServiceController pService)
    {
        pService = ServiceController.GetServices()
            .FirstOrDefault(serviceController => serviceController.ServiceName == pServiceName);

        return pService != null;
    }

不错。这是我的“一行代码”:private static bool TryGetService(string name, out ServiceController service) => (service = ServiceController.GetServices().FirstOrDefault(s => s.ServiceName == name)) != null; - Philippe Paré

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