无法使用 WMI 和 C# 远程终止进程

Posted

技术标签:

【中文标题】无法使用 WMI 和 C# 远程终止进程【英文标题】:Unable to remotely terminate a process using WMI and C# 【发布时间】:2013-09-24 22:44:53 【问题描述】:

我正在尝试编写一个方法,如果它无法使用 StopService 方法停止,它将通过进程 ID 终止远程系统上的服务。我尝试了两种在 ManagementObject 上调用“终止”方法的不同方法,但我得到了两个不同的错误。对我来说,能够从 Terminate 方法获取返回码也很重要。

如果我直接向要终止的进程声明 ManagementPath,则会在以下行收到错误“System.Management.ManagementException: Invalid object path”:

ManagementBaseObject processParams = processObj.InvokeMethod("Terminate", (ManagementBaseObject)null, null);

如果我得到一个 ManagementObjectCollection 并循环遍历它以查找我想要终止的进程 ID,我会在以下行收到错误“无效参数”:

ManagementBaseObject termParams = currentObj.InvokeMethod("Terminate", (ManagementBaseObject)null, null);

因此,在这两种情况下,当我尝试调用 Terminate 方法时都会出现错误,但错误会根据我到达对象的方式(直接路径或循环通过集合)而有所不同。

我不认为这与 SeDebugPrivilege 有关,因为我相信如果是的话,我会得到“访问被拒绝”或“权限不足”。

如果我尝试直接指定进程路径的代码:

public int KillServiceWMI(string serviceName, string serverName, string serverUser, string serverDomain, string serverPassword)

    try
    
        ConnectionOptions options = new ConnectionOptions();
        options.Impersonation = System.Management.ImpersonationLevel.Impersonate;
        options.Username = serverDomain + "\\" + serverUser;
        options.Password = serverPassword;

        ManagementScope scope = new ManagementScope("\\\\" + serverName + "\\root\\cimv2", options);
        Console.WriteLine("Connecting to scope");
        scope.Connect();

        Console.WriteLine("Getting ManagementPath");
        ManagementPath servicePath = new ManagementPath("Win32_Service.Name='" + serviceName + "'");
        Console.WriteLine("Getting ManagementObject");
        ManagementObject serviceObj = new ManagementObject(scope, servicePath, new ObjectGetOptions());
        Console.WriteLine("Name of service is " + serviceObj["DisplayName"].ToString());
        Console.WriteLine("Process ID of service is " + serviceObj["ProcessId"].ToString());
        ManagementPath processPath = new ManagementPath("Win32_Process.ProcessId='" + serviceObj["ProcessId"] + "'");
        ManagementObject processObj = new ManagementObject(scope, processPath, new ObjectGetOptions());
        ManagementBaseObject processParams = processObj.InvokeMethod("Terminate", (ManagementBaseObject)null, null);
        int returnCode = System.Convert.ToInt32(processParams.Properties["ReturnValue"].Value);
        return returnCode;
    
    catch (Exception connectEx)
    
        Console.WriteLine("Connecting to " + serverName + " caused an exception");
        Console.Write(connectEx);
        return 99;
    

如果我遍历一组进程的代码:

public int KillServiceWMI(string serviceName, string serverName, string serverUser, string serverDomain, string serverPassword)

    try
    
        ConnectionOptions options = new ConnectionOptions();
        options.Impersonation = System.Management.ImpersonationLevel.Impersonate;
        options.Username = serverDomain + "\\" + serverUser;
        options.Password = serverPassword;

        ManagementScope scope = new ManagementScope("\\\\" + serverName + "\\root\\cimv2", options);
        Console.WriteLine("Connecting to scope");
        scope.Connect();

        Console.WriteLine("Getting ManagementPath");
        ManagementPath servicePath = new ManagementPath("Win32_Service.Name='" + serviceName + "'");
        Console.WriteLine("Getting ManagementObject");
        ManagementObject serviceObj = new ManagementObject(scope, servicePath, new ObjectGetOptions());
        Console.WriteLine("Name of service is " + serviceObj["DisplayName"].ToString());
        Console.WriteLine("Process ID of service is " + serviceObj["ProcessId"].ToString());
        ObjectQuery serviceQuery = new ObjectQuery("SELECT * from Win32_Process WHERE ProcessID = '" + serviceObj["ProcessId"].ToString() + "'");
        ManagementObjectSearcher serviceSearcher = new ManagementObjectSearcher(scope, serviceQuery);
        ManagementObjectCollection serviceColl = serviceSearcher.Get();
        int returnCode = 0;
        foreach (ManagementObject currentObj in serviceColl)
        
            if (currentObj["ProcessId"].ToString().Equals(serviceObj["ProcessId"].ToString(), StringComparison.OrdinalIgnoreCase))
            
                Console.WriteLine("Found process " + currentObj["ProcessId"].ToString() + ". Terminating...");

                ManagementBaseObject termParams = currentObj.InvokeMethod("Terminate", (ManagementBaseObject)null, null);
                returnCode = System.Convert.ToInt32(termParams.Properties["ReturnValue"].Value);
            
        
        return returnCode;
    
    catch (Exception connectEx)
    
        Console.WriteLine("Connecting to " + vaultName + " caused an exception");
        Console.Write(connectEx);
        return 99;
    

【问题讨论】:

冒着问一个愚蠢问题的风险 - 它必须是 WMI/C# 吗?你知道这可以通过 sysinternals 的 PSEXEC 来完成吗? 是的,我知道我可以使用 psexec 并以这种方式进行操作。在这种情况下,这是一种用于控制一组服务器的 Web 应用程序的方法。其中一些是微妙的,“pokey”,因此当使用 WMI 命令时,它们的服务不会停止。所以 Operations 要求我向应用程序添加一个“终止”功能,以便在尝试(并且失败)两次干净地停止服务之后,它会尝试终止该进程。 @paqogomez,我感觉您已经阅读了this Meta question,这就是促使您最近提出的编辑建议的原因。请务必阅读the highest-rated answer 开头的部分:“您应该在标题中使用标签的唯一时间是当它们与标题的会话语气相一致时”(强调我的)。跨度> @MichaelPetrotta 也许我对这个特定规则有点疯狂,但我认为我一直牢记评分最高的答案。 @paqogomez:另外,the second answer 更深入其中。不过,我理解这种诱惑。 【参考方案1】:

我最终放弃了尝试在 Win32_Process 上使用 Terminate 方法,而是使用 Create 远程调用 TaskKill.exe。因为返回信息现在隐藏在 taskkill.exe 后面,所以我必须再次获取进程列表并查找目标 pid 以确保进程实际上已终止。

ConnectionOptions options = new ConnectionOptions();
options.Impersonation = System.Management.ImpersonationLevel.Impersonate;
options.Username = serverDomain + "\\" + serverUser;
options.Password = serverPassword;

ManagementScope scope = new ManagementScope("\\\\" + serverName + "\\root\\cimv2", options);
Console.WriteLine("Connecting to scope");
scope.Connect();

Console.WriteLine("Getting ManagementPath");
ManagementPath servicePath = new ManagementPath("Win32_Service.Name='" + serviceName + "'");
Console.WriteLine("Getting ManagementObject");
ManagementObject serviceObj = new ManagementObject(scope, servicePath, new ObjectGetOptions());
Console.WriteLine("Name of service is " + serviceObj["DisplayName"].ToString());
Console.WriteLine("Process ID of service is " + serviceObj["ProcessId"].ToString());

// use processid to kill process with taskkill
ObjectGetOptions processObjGetOpt = new ObjectGetOptions();
ManagementPath processPath = new ManagementPath("Win32_Process");
ManagementClass processClass = new ManagementClass(scope, processPath, processObjGetOpt);
ManagementBaseObject processInParams = processClass.GetMethodParameters("Create");
processInParams["CommandLine"] = string.Format("cmd /c \"taskkill /f /pid 0\"", serviceObj["ProcessId"].ToString());
ManagementBaseObject outParams = processClass.InvokeMethod("Create", processInParams, null);
Console.WriteLine("Return code for taskkill: " + outParams["returnValue"]);
int returnCode = System.Convert.ToInt32(outParams["returnValue"]);

【讨论】:

以上是关于无法使用 WMI 和 C# 远程终止进程的主要内容,如果未能解决你的问题,请参考以下文章

无法在 C# 中终止进程?

使用 Wmi win32_Process 执行远程进程 - 获取进程的标准输出

j-interop 无法通过 WMI 监控 Win7

授予远程用户(非管理员)使用 WMI 和 C# 在命名空间 cimv2 中枚举 Win32_Service 服务的能力

如何获得在没有 WMI 的远程计算机上运行的进程的所有者

远程运行 wmi 进程,没有适当的域用户权限