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

12

目前我是通过以下方式进行检查:

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

它可以帮助调试并且可通过可执行文件运行服务。但是现在假设该服务需要与用户桌面进行交互,因此我必须在属性中启用“允许服务与桌面交互”。这当然会破坏此检查方式。是否还有其他方法?


3
与桌面交互的功能已从Vista和Win7中移除。这基本上会解决你的问题,如果不能立刻解决,那也会很快解决。 - Hans Passant
可能重复:https://dev59.com/aEbRa4cB1Zd3GeqPxytc - P Daddy
请查看对于[这个类似的问题][2]的答案(我推荐我的 <sup><sub>;-)</sub></sup>)。 [2]: https://dev59.com/aEbRa4cB1Zd3GeqPxytc - P Daddy
@nobugz 这项服务应该监控一些用户GUI程序,并在它们关闭/崩溃时保持运行(一个信息亭/终端)。看起来这真的不是一个好的方法... - mikoro
8个回答

9

你确定吗?(提供源URL将更完美!) - Spi
@Spi .NET Core 2.2:https://github.com/dotnet/corefx/blob/release/2.2/src/System.Runtime.Extensions/src/System/Environment.cs#L152 - George Chakhidze
@Spi .NET Core 3: https://github.com/dotnet/corefx/blob/release/3.1/src/Common/src/CoreLib/System/Environment.cs#L129 - George Chakhidze
1
@Spi 目前,我更倾向于使用这段代码:https://github.com/aspnet/Extensions/blob/master/src/Hosting/WindowsServices/src/WindowsServiceHelpers.cs - George Chakhidze
1
提醒一下,您的“最佳方法”链接已失效。 - Spencer

7
这并不是完美的方法,但你可以尝试这样做:
public static bool IsService()
{
    ServiceController sc = new ServiceController("MyApplication");
    return sc.Status == ServiceControllerStatus.StartPending;
}

这个方法的理念是,如果你在服务启动时运行此方法,则它将始终处于等待状态。如果根本没有安装该服务,则该方法始终返回false。仅当服务正在启动并且有人同时尝试将其作为应用程序启动时,才会失败(这种情况非常罕见)。
我不太喜欢这个答案,但我认为这可能是你能做到的最好的。从实际角度来看,允许同一应用程序以服务或应用程序模式运行的想法并不是一个很好的主意 - 从长远来看,如果将所有通用功能抽象成类库并创建一个单独的服务应用程序,将更容易管理。但是,如果出于某种原因,你真的需要同时具有两者的优势,你可能可以将上面的IsService方法与Environment.UserInteractive相结合,以获得几乎正确的答案。

这似乎运行良好,比Environment.UserInteractive更可靠。 - mikoro

7
在 .NET Core 中,你可以使用 WindowsServiceHelpers.IsWindowsService() 静态帮助方法来确定应用程序是否作为 Windows 服务运行,该方法可在 Microsoft.Extensions.Hosting.WindowsServices NuGet 包中获得。 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
}

太棒了,这至少是针对 .Net Core 的正确答案。谢谢! - Chris Bordeman
请注意,如果您有启动进程“B”的进程“A”,并且在进程“B”执行IsWindowsService()之前终止了进程“A”,则此方法可能会让您陷入麻烦。这时将从非托管代码引发InvalidOperationException异常。 - Jacob

6
该被接受的答案存在问题,因为检查未安装服务的状态会抛出错误。我使用的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

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

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

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

2

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

另外,您需要知道交互式桌面已在 Windows Vista 及以上版本中 被禁用。如果您正在运行需要与用户交互的 Windows 服务,则现在推荐的方法是将前端应用程序与 Windows 服务分离,并使用类似于 WCF 的东西进行通信。

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


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

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