获取Windows服务的PID

15

有人能帮我了解如何获取Windows服务的PID吗?
我需要获取PID以便运行以下命令:

Process.Start(new ProcessStartInfo 
    {
        Filename = "cmd.exe",
        CreateNoWindow = true,
        UseShellExecute = false,
        Arguments = string.Format("/c taskkill /pid {0} /f", pidnumber)
    });

下次请不要忽略代码格式。适当格式化的代码有助于人们更好地理解您的意图。 - Fedor
即使任务管理器在“进程”选项卡中没有显示您的服务,它仍然具有某种可执行文件。 - M C
3个回答

32
其他答案忽略的事实是,一个进程也可以托管多个自治服务。每个托管几个服务的svchost.exe进程的多个实例就是最好的例子。
因此,通常情况下,试图通过终止其托管进程来杀死任意服务是绝对不安全的(我假设这是你尝试做的事情,因为你提到了taskkill.exe)。这可能会导致关闭几个不相关的服务。
如果您知道服务的进程仅托管您关心的服务,则可以选择@MC在他/她的回答中建议的策略。

或者,您还可以使用ServiceController类打开对服务的句柄,然后通过ServiceHandle属性使用它来P/Invoke QueryServiceStatusEx函数以查找您想要了解的进程ID。

如果您需要更多详细信息,您应该澄清您实际尝试实现的是什么。从您的问题中不清楚。

更新这里有一些我从现有项目中提取出来的代码,假设您有一个ServiceController实例。__如上所述,请小心使用!__

[StructLayout(LayoutKind.Sequential)]
internal sealed class SERVICE_STATUS_PROCESS
{
    [MarshalAs(UnmanagedType.U4)]
    public uint dwServiceType;
    [MarshalAs(UnmanagedType.U4)]
    public uint dwCurrentState;
    [MarshalAs(UnmanagedType.U4)]
    public uint dwControlsAccepted;
    [MarshalAs(UnmanagedType.U4)]
    public uint dwWin32ExitCode;
    [MarshalAs(UnmanagedType.U4)]
    public uint dwServiceSpecificExitCode;
    [MarshalAs(UnmanagedType.U4)]
    public uint dwCheckPoint;
    [MarshalAs(UnmanagedType.U4)]
    public uint dwWaitHint;
    [MarshalAs(UnmanagedType.U4)]
    public uint dwProcessId;
    [MarshalAs(UnmanagedType.U4)]
    public uint dwServiceFlags;
}

internal const int ERROR_INSUFFICIENT_BUFFER = 0x7a;
internal const int SC_STATUS_PROCESS_INFO = 0;

[DllImport("advapi32.dll", SetLastError = true)]
internal static extern bool QueryServiceStatusEx(SafeHandle hService, int infoLevel, IntPtr lpBuffer, uint cbBufSize, out uint pcbBytesNeeded);

public static int GetServiceProcessId(this ServiceController sc)
{
    if (sc == null)
        throw new ArgumentNullException("sc");

    IntPtr zero = IntPtr.Zero;

    try
    {
        UInt32 dwBytesNeeded;
        // Call once to figure the size of the output buffer.
        QueryServiceStatusEx(sc.ServiceHandle, SC_STATUS_PROCESS_INFO, zero, 0, out dwBytesNeeded);
        if (Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER)
        {
            // Allocate required buffer and call again.
            zero = Marshal.AllocHGlobal((int)dwBytesNeeded);

            if (QueryServiceStatusEx(sc.ServiceHandle, SC_STATUS_PROCESS_INFO, zero, dwBytesNeeded, out dwBytesNeeded))
            {
                var ssp = new SERVICE_STATUS_PROCESS();
                Marshal.PtrToStructure(zero, ssp);
                return (int)ssp.dwProcessId;
            }
        }
    }
    finally
    {
        if (zero != IntPtr.Zero)
        {
            Marshal.FreeHGlobal(zero);
        }
    }
    return -1;
}

1
使用服务控制器,正如此答案所建议的那样,也是我的选择。 - Felice Pollano
2
这是这里最好的答案。 - Jeff Paquette
1
调用 QueryServiceStatusEx 需要管理员权限吗? - Cocowalla
这要看情况。一般来说是可以的,但你也可以设置服务权限(针对特定服务),允许非管理员用户访问(查询这些服务的状态)(参考链接)。 - Christian.K
对我来说,Marshal.GetLastWin32Error() 返回了零,但 dwBytesNeeded 被设置了。所以我必须检查 ERROR_INSUFFICIENT_BUFFER 或零。 - ergohack

3

查看类似问题的答案:

找到 Windows 服务运行的进程名称

使用 WMI 查询,您可以 -

查找与单个 exe 相关的所有服务(单个 exe 可以托管多个服务):

select Name from Win32_Service where ProcessId = 588

或者,为了回答这个问题,您可以获取正在运行服务的进程的 PID:

select ProcessId from Win32_Service where Name = 'wuauserv'


2
假设您已知道服务所使用的可执行文件的名称并且确切地知道只有一个:
int procID = Process.GetProcessesByName("yourservice")[0].Id;

方法Process.GetProcessesByName("yourservice")返回一个具有指定名称的进程数组,因此,如果您不知道有多少个"yourservice.exe"同时运行,则可能需要使用foreach循环。


2
你应该提到可以有多个结果。 - Felice Pollano
为了验证我是否拥有正确的PID,我会在循环中检查文件路径,代码如下:if (processes[i].MainModule.FileName == myServiceExecutableFilePath) - Smitty-Werben-Jager-Manjenson
可能有多个具有相同名称的EXE文件,其中并不是你感兴趣的服务的服务进程。此外,如果您使用它来在无法正常停止服务时终止服务进程,那么如果进程在错误的时间死亡,它可能会失败并引发异常。 - Florian Winter

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