如何确定是否在Windows服务内启动?

时间:2010-03-07 17:32:17

标签: c# windows-services

目前我正在以下列方式检查它:

if (Environment.UserInteractive)
    Application.Run(new ServiceControllerForm(service));
else
    ServiceBase.Run(windowsService);

它可以帮助调试一点,也可以使用可执行文件运行服务。但现在假设该服务需要与用户桌面进行交互,因此我必须在属性中启用“允许服务与桌面交互”。这当然打破了这种检查方式。还有另一种方式吗?

8 个答案:

答案 0 :(得分:6)

这不完美,但你可能会做这样的事情:

public static bool IsService()
{
    ServiceController sc = new ServiceController("MyApplication");
    return sc.Status == ServiceControllerStatus.StartPending;
}

这个想法是,如果你在服务仍在启动时运行它,那么它将始终处于挂起状态。如果根本没有安装该服务,则该方法将始终返回false。它只会在服务正在启动的非常不可能的极端情况下失败有人试图同时将其作为应用程序启动。

我不喜欢这个答案,但我认为这可能是你能做的最好的。实际上允许相同的应用程序在服务或应用程序模式下运行不是一个好主意 - 从长远来看,如果将所有常用功能抽象到类库中并且只是创建一个单独的服务应用。但如果由于某种原因你真的真的需要你的蛋糕并且也吃它,你可以将上面的IsService方法与Environment.UserInteractive结合起来以获得正确答案几乎所有的时间。

答案 1 :(得分:4)

接受答案的问题是检查未安装的服务的状态将会抛出。我正在使用的IsService方法如下所示:

    private bool IsService(string name)
    {
        if (!Environment.UserInteractive) return true;
        System.ServiceProcess.ServiceController sc = new System.ServiceProcess.ServiceController(name);
        try
        {
            return sc.Status == System.ServiceProcess.ServiceControllerStatus.StartPending;
        }
        catch(InvalidOperationException)
        {
            return false;
        }
    }

哪个应该比检查Environment.UserInteractive

更可靠

答案 2 :(得分:3)

在 .NET Core 中,您可以使用 WindowsServiceHelpers.IsWindowsService() NuGet 包中提供的 Microsoft.Extensions.Hosting.WindowsServices 静态帮助程序方法确定应用程序是否作为 Windows 服务运行。

Install-Package Microsoft.Extensions.Hosting.WindowsServices

if (WindowsServiceHelpers.IsWindowsService())
{
    // The application is running as a Windows Service
}
else
{
    // The application is not running as a Windows Service
}

答案 3 :(得分:2)

为什么不使用命令行开关?

// Note that you have to add the params argument, 
// which isn't usually present in windows services
private static void Main(params string[] parameters)
{
    ....

    if (parameters.Length > 0)
    {
        if (parameters[0].ToLower() == "/console")
        {
            Application.Run(new ServiceControllerForm(service));  
        {
        else
        {
            ServiceBase.Run(windowsService);
        }
    }
}

答案 4 :(得分:2)

这是我们在 this 答案的帮助下使用的。

bool IsStartupAsService(string serviceName)
{
    return ServiceController
        .GetServices()
        .Where(s => s.ServiceName == serviceName)
        .Any(s => s.Status == ServiceControllerStatus.StartPending);
}

答案 5 :(得分:1)

不使用Environment.UserInteractive属性,而是修改服务的启动方法以检查“-console”命令行参数。如果参数存在,则作为普通应用程序运行。如果没有,请作为服务运行。它不像属性检查那样自动化,但可以很容易地为桌面添加一个快捷方式,为您添加“-console”命令行参数。

另外,您需要注意与桌面的互动disabled in Windows Vista and beyond。如果您正在运行需要与用户交互的Windows服务,那么现在批准的方法是将前端应用程序与Windows服务分开,并让它们使用WCF等进行通信。

如果您需要调试Windows服务(无论是作为服务还是作为应用程序运行),请在启动方法中调用System.Diagnostics.Debugging.Break()。这将强制提示您进入调试会话。我使用这种技术一直调试我的Windows服务。

答案 6 :(得分:1)

还必须注意,foo\dbar始终在 .NET Core 中返回Environment.UserInteractive,即使它作为Windows服务运行。

目前,最好的方法似乎是this one from ASP.NET Core

来源: .NET Core 2.2 .NET Core 3.1

答案 7 :(得分:0)

您可以检查该进程或其任何父进程是否被列为服务:

       var process = System.Diagnostics.Process.GetCurrentProcess();
       var parent = process.Parent();
       var procIsService = process?.IsService;
       var parentIsService = parent?.IsService;
       ...

public static class Extensions
{
    private static string FindIndexedProcessName(int pid)
    {
        var processName = Process.GetProcessById(pid).ProcessName;
        var processesByName = Process.GetProcessesByName(processName);
        string processIndexdName = null;

        for (var index = 0; index < processesByName.Length; index++)
        {
            processIndexdName = index == 0 ? processName : processName + "#" + index;
            var processId = new PerformanceCounter("Process", "ID Process", processIndexdName);
            if ((int)processId.NextValue() == pid)
            {
                return processIndexdName;
            }
        }

        return processIndexdName;
    }

    private static Process FindPidFromIndexedProcessName(string indexedProcessName)
    {
        var parentId = new PerformanceCounter("Process", "Creating Process ID", indexedProcessName);
        return Process.GetProcessById((int)parentId.NextValue());
    }

    public static Process Parent(this Process process)
    {
        return FindPidFromIndexedProcessName(FindIndexedProcessName(process.Id));
    }

    public static bool IsService(this Process process)
    {
        using (ManagementObjectSearcher Searcher = new ManagementObjectSearcher(
        "SELECT * FROM Win32_Service WHERE ProcessId =" + "\"" + process.Id + "\""))
        {
            foreach (ManagementObject service in Searcher.Get())
                return true;
        }
        return false;
    }
}