如何让 Win32_Process 查询更快?

我有以下函数将进程信息提取到 DataTable 中:


public DataTable getProcesses()

        {

            DataTable dt = new DataTable();


            dt.Columns.Add("ID");

            dt.Columns.Add("Name");

            dt.Columns.Add("Path");

            dt.Columns.Add("User");

            dt.Columns.Add("Priority");


            string pid = "-";

            string name = "-";

            string path = "-";

            string priort = "-";

            string user = "-";


            string query = "Select * From Win32_Process";

            ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);

            ManagementObjectCollection processList = searcher.Get();


            foreach (ManagementObject proc in processList)

            {

                pid = proc["ProcessID"].ToString();

                name = proc["Name"].ToString();


                if (proc["ExecutablePath"] != null)

                {

                    path = proc["ExecutablePath"].ToString();

                    priort = proc["Priority"].ToString();

                }

                string[] argList = new string[2];

                int returnVal = Convert.ToInt32(proc.InvokeMethod("GetOwner", argList));

                if (returnVal == 0)

                {

                    // return DOMAIN\user

                    user = argList[1] + "\\" + argList[0];

                }


                dt.Rows.Add(pid, name, path, user, priort);

            }


            return dt;

        }

这最终会奏效并给我想要的回报,但执行需要 20-30 秒。


我想知道是否有任何方法可以优化功能,或者特别是查询,这很可能是“延迟”的来源。


编辑


在做了评论中建议的一些事情之后,平均时间已经下降到 15-20 秒,但这仍然太长了。最多4-5秒是可以忍受的。我仍然没有更改查询,有什么办法可以让它更快吗?


编辑 2


在应用了@NicoRiff 建议的一些更改后,仍然获得相同的运行时,我进行了一些调试以查看实际花费了这么长时间。原来它是特定的一行:int returnVal = Convert.ToInt32(proc.InvokeMethod("GetOwner", argList));,这将使我成为“拥有”每个进程的用户。


这条线大约需要 60 毫秒,而所有其他线需要 1-2 毫秒。超过 200 次迭代(这是我拥有的进程数,我只能想象使用更大的列表所需的时间),总共需要大约 12-13 秒(仅针对那一行),给出 15-20总和。


既然我已经“挑出”了问题,我该如何优化该功能?


拉丁的传说
浏览 211回答 2
2回答

有只小跳蛙

从技术上讲,这并不能回答您的问题,因为它不使用 Win32_Process 查询。但是,使用 Powershell,它确实会在很短的时间内(~1.5 秒对~25 秒)产生相同的结果。您需要在 X64 模式下运行它以询问 64 位进程,并且需要提升权限才能让 Powershell 返回用户名。请注意,这是我第一次从 C# 调用 Powershell 脚本,可能有更好的方法来做。它还需要一些错误捕获以使其更健壮(不适合按原样生产)。using System.Management.Automation;using System.Management.Automation.Runspaces;private DataTable getProcesses()&nbsp;{&nbsp; &nbsp; // Create the datatable and columns&nbsp; &nbsp; DataTable dt = new DataTable();&nbsp; &nbsp; dt.Columns.Add("ID");&nbsp; &nbsp; dt.Columns.Add("Name");&nbsp; &nbsp; dt.Columns.Add("Path");&nbsp; &nbsp; dt.Columns.Add("User");&nbsp; &nbsp; dt.Columns.Add("Priority");&nbsp; &nbsp; dt.Columns.Add("BasePriority");&nbsp;&nbsp; &nbsp; string script = $"get-process -IncludeUserName | select id, processname, path, username, priorityclass";&nbsp; &nbsp; List<string[]> psOutput = new List<string[]>();&nbsp; &nbsp; // Invoke Powershell cmdlet and get output&nbsp; &nbsp; using (PowerShell ps = PowerShell.Create())&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; ps.AddScript(script);&nbsp; &nbsp; &nbsp; &nbsp; var output = ps.Invoke();&nbsp; &nbsp; &nbsp; &nbsp; if(ps.Streams.Error.Count > 0)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; throw new Exception($"Error running script:{Environment.NewLine}{string.Join(Environment.NewLine, ps.Streams.Error.Select(e => e.ToString()))}");&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; // clean and split the output&nbsp; &nbsp; &nbsp; &nbsp; psOutput.AddRange(output.Select(i => i.ToString().Replace("; ", ";").TrimStart("@{".ToCharArray()).TrimEnd('}').Split(';')));&nbsp; &nbsp; }&nbsp; &nbsp; // populate the DataTable&nbsp; &nbsp; psOutput&nbsp; &nbsp; &nbsp; &nbsp; .AsParallel()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// this does not really help when not calling Process.GetProcessById&nbsp; &nbsp; &nbsp; &nbsp; .Select(p => p.Select(f => f.Split("=")).ToDictionary(k => k.FirstOrDefault(), v => v.LastOrDefault()))&nbsp; &nbsp; &nbsp; &nbsp; .Select(kv => new object[] {&nbsp; &nbsp; // "flatten" the dictionaries into object[]'s that will become the datarows&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; kv["Id"]&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; , kv["ProcessName"]&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; , kv["Path"]&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; , kv["UserName"]&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; , kv["PriorityClass"]&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; , Process.GetProcessById(int.Parse(kv["Id"])).BasePriority&nbsp; // if you need the numerical base priority - takes quite a bit longer though (Not sure how to get this via Powershell)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; )&nbsp; &nbsp; &nbsp; &nbsp; .ToList()&nbsp; &nbsp; &nbsp; &nbsp; .ForEach(r => dt.Rows.Add(r));&nbsp; // add each object[] to the datatable&nbsp; &nbsp; // return the datatable&nbsp;&nbsp; &nbsp; return dt;}

一只名叫tom的猫

我个人一直想摆脱使用DataTable. 您可以使用List<>可以解决许多潜在问题的集合。话虽如此,您可能想查看ORMi库以获取有关List<>. 您可以通过以下方式实现您正在尝试的目标:&nbsp; &nbsp; &nbsp; &nbsp; WMIHelper helper = new WMIHelper("root\\CimV2");&nbsp; &nbsp; &nbsp; &nbsp; string pid = "-";&nbsp; &nbsp; &nbsp; &nbsp; string name = "-";&nbsp; &nbsp; &nbsp; &nbsp; string path = "-";&nbsp; &nbsp; &nbsp; &nbsp; string priort = "-";&nbsp; &nbsp; &nbsp; &nbsp; string user = "-";&nbsp; &nbsp; &nbsp; &nbsp; var processes = helper.Query("Select * From Win32_Process").ToList();&nbsp; &nbsp; &nbsp; &nbsp; foreach (var p in processes)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pid = p.ProcessID;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; name = p.Name;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; path = p.ExecutablePath ?? String.Empty;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; priort = p.Priority ?? String.Empty;&nbsp; &nbsp; &nbsp; &nbsp; }上面的代码适用于dynamic对象,不需要您编写任何模型。就是那个小代码。如果您需要使用方法,那么您可以声明您的模型并使用强类型对象:1)定义你的模型:[WMIClass(Name = "Win32_Process", Namespace = "root\\CimV2")]public class Process{&nbsp; &nbsp; public int Handle { get; set; }&nbsp; &nbsp; public string Name { get; set; }&nbsp; &nbsp; public int ProcessID { get; set; }&nbsp; &nbsp; public string ExecutablePath { get; set; }&nbsp; &nbsp; public int Priority { get; set; }&nbsp; &nbsp; /// <summary>&nbsp; &nbsp; /// Date the process begins executing.&nbsp; &nbsp; /// </summary>&nbsp; &nbsp; public DateTime CreationDate { get; set; }&nbsp; &nbsp; public dynamic GetOwnerSid()&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; return WMIMethod.ExecuteMethod(this);&nbsp; &nbsp; }&nbsp; &nbsp; public ProcessOwner GetOwner()&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; return WMIMethod.ExecuteMethod<ProcessOwner>(this);&nbsp; &nbsp; }&nbsp; &nbsp; public int AttachDebugger()&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; return WMIMethod.ExecuteMethod<int>(this);&nbsp; &nbsp; }}public class ProcessOwner{&nbsp; &nbsp; public string Domain { get; set; }&nbsp; &nbsp; public int ReturnValue { get; set; }&nbsp; &nbsp; public string User { get; set; }}2)查询WMI&nbsp; &nbsp; &nbsp; &nbsp; List<Process> processes = helper.Query<Process>().ToList();&nbsp; &nbsp; &nbsp; &nbsp; foreach (Process p in processes)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pid = p.ProcessID;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; name = p.Name;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; path = p.ExecutablePath ?? String.Empty;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; priort = p.Priority ?? String.Empty;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; dynamic d = p.GetOwnerSid();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ProcessOwner po = p.GetOwner();&nbsp; &nbsp; &nbsp; &nbsp; }即使对于这个任务,第二种方式可能看起来有点太多的工作,你会得到更干净和易于理解的代码。注意:我已经用 ORMi 尝试过你的代码,我会在 1-2 秒内得到结果。正如其他人所说,这可能取决于您的环境。SELECT注意 2:始终只使用语句中需要的属性。WMI a 非常昂贵SELECT *。始终指定属性。在您的情况下,它将是:Select ProcessID, Name, ExecutablePath, Priority From Win32_Process(ORMi 也会为您解决这个问题,因为它总是会查询模型上设置的属性。)
打开App,查看更多内容
随时随地看视频慕课网APP