C# 如何使Win32_进程查询更快?

C# 如何使Win32_进程查询更快?,c#,windows,wmi,C#,Windows,Wmi,我使用以下函数将流程信息提取到数据表中: public DataTable getProcesses() { DataTable dt = new DataTable(); dt.Columns.Add("ID"); dt.Columns.Add("Name"); dt.Columns.Add("Path"); dt.Columns.Add("User");

我使用以下函数将流程信息提取到数据表中:

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建议的一些更改后,仍然得到了相同的运行时,我进行了一些调试,以查看实际花费了这么长时间的原因。结果是具体的一行:
intreturnval=Convert.ToInt32(proc.InvokeMethod(“GetOwner”,argList)),它将为我获取“拥有”每个进程的用户

这条线路大约需要60毫秒,而所有其他线路需要1-2毫秒。超过200次迭代(这是我拥有的进程的数量,我只能想象一个更大的列表需要多少时间),总共大约需要12-13秒(就这一行而言),总共需要15-20秒


既然我已经“挑出”了这个问题,我如何优化这个函数呢?

我个人一直想摆脱使用
DataTable
。您可以使用
列表
或集合来解决您可能遇到的许多潜在问题

话虽如此,您可能希望签出库以获取
列表上的WMI信息。您可以通过以下方式实现您正在尝试的目标:

        WMIHelper helper = new WMIHelper("root\\CimV2");

        string pid = "-";
        string name = "-";
        string path = "-";
        string priort = "-";
        string user = "-";

        var processes = helper.Query("Select * From Win32_Process").ToList();

        foreach (var p in processes)
        {
            pid = p.ProcessID;
            name = p.Name;
            path = p.ExecutablePath ?? String.Empty;
            priort = p.Priority ?? String.Empty;
        }
上述代码适用于
动态
对象,不需要编写任何模型。就是那个小代码。如果需要使用方法,则可以声明模型并使用强类型对象:

1) 定义您的模型:

[WMIClass(Name = "Win32_Process", Namespace = "root\\CimV2")]
public class Process
{
    public int Handle { get; set; }
    public string Name { get; set; }
    public int ProcessID { get; set; }
    public string ExecutablePath { get; set; }
    public int Priority { get; set; }

    /// <summary>
    /// Date the process begins executing.
    /// </summary>
    public DateTime CreationDate { get; set; }

    public dynamic GetOwnerSid()
    {
        return WMIMethod.ExecuteMethod(this);
    }

    public ProcessOwner GetOwner()
    {
        return WMIMethod.ExecuteMethod<ProcessOwner>(this);
    }

    public int AttachDebugger()
    {
        return WMIMethod.ExecuteMethod<int>(this);
    }
}

public class ProcessOwner
{
    public string Domain { get; set; }
    public int ReturnValue { get; set; }
    public string User { get; set; }
}

(ORMi将为您解决这一问题,因为它总是查询模型上设置的属性。)

我个人一直想摆脱使用
数据表的习惯。您可以使用
列表
或集合来解决您可能遇到的许多潜在问题

话虽如此,您可能希望签出库以获取
列表上的WMI信息。您可以通过以下方式实现您正在尝试的目标:

        WMIHelper helper = new WMIHelper("root\\CimV2");

        string pid = "-";
        string name = "-";
        string path = "-";
        string priort = "-";
        string user = "-";

        var processes = helper.Query("Select * From Win32_Process").ToList();

        foreach (var p in processes)
        {
            pid = p.ProcessID;
            name = p.Name;
            path = p.ExecutablePath ?? String.Empty;
            priort = p.Priority ?? String.Empty;
        }
上述代码适用于
动态
对象,不需要编写任何模型。就是那个小代码。如果需要使用方法,则可以声明模型并使用强类型对象:

1) 定义您的模型:

[WMIClass(Name = "Win32_Process", Namespace = "root\\CimV2")]
public class Process
{
    public int Handle { get; set; }
    public string Name { get; set; }
    public int ProcessID { get; set; }
    public string ExecutablePath { get; set; }
    public int Priority { get; set; }

    /// <summary>
    /// Date the process begins executing.
    /// </summary>
    public DateTime CreationDate { get; set; }

    public dynamic GetOwnerSid()
    {
        return WMIMethod.ExecuteMethod(this);
    }

    public ProcessOwner GetOwner()
    {
        return WMIMethod.ExecuteMethod<ProcessOwner>(this);
    }

    public int AttachDebugger()
    {
        return WMIMethod.ExecuteMethod<int>(this);
    }
}

public class ProcessOwner
{
    public string Domain { get; set; }
    public int ReturnValue { get; set; }
    public string User { get; set; }
}

(ORMi将为您解决这个问题,因为它总是查询在模型上设置的属性。)

从技术上讲,这并不能回答您的问题,因为它不使用Win32_进程查询。 但是,使用Powershell,它确实可以在很短的时间内(1.5秒~25秒)产生相同的结果

您需要在X64模式下运行此命令来查询64位进程,并且需要提高Powershell的权限才能返回用户名

请注意,这是我第一次从C#调用Powershell脚本,可能有更好的方法。 它还需要一些错误捕获,以使其更加健壮(不适合生产)

使用System.Management.Automation;
使用System.Management.Automation.Runspaces;
私有数据表getProcesses()
{
//创建数据表和列
DataTable dt=新的DataTable();
dt.列。添加(“ID”);
dt.列。添加(“名称”);
添加(“路径”);
dt.列。添加(“用户”);
dt.列。添加(“优先级”);
dt.列。添加(“基本优先级”);
字符串脚本=$“获取进程-IncludeUserName |选择id、进程名、路径、用户名、priorityclass”;
List psOutput=新列表();
//调用Powershell cmdlet并获取输出
使用(PowerShell ps=PowerShell.Create())
{
ps.AddScript(脚本);
var输出=ps.Invoke();
如果(ps.Streams.Error.Count>0)
{
抛出新异常($“运行脚本时出错:{Environment.NewLine}{string.Join(Environment.NewLine,ps.Streams.Error.Select(e=>e.ToString()))}”);
}
//清理并拆分输出
psOutput.AddRange(output.Select(i=>i.ToString().Replace(“;”,“;”).TrimStart(@{.tocharray()).TrimEnd('}').Split(';'));
}
//填充数据表
P输出
.AsParallel()//当不调用Process.GetProcessById时,这实际上没有帮助
.Select(p=>p.Select(f=>f.Split(“=”).ToDictionary(k=>k.FirstOrDefault(),v=>v.LastOrDefault())
.Select(kv=>newobject[]{//“展平”字典到将成为数据行的object[]中
kv[“Id”]
,kv[“进程名称”]
,kv[“路径”]
,kv[“用户名”]
,kv[“优先等级”]
,Process.GetProcessById(int.Parse(kv[“Id”])).BasePriority//如果需要数字基优先级,则需要花费相当长的时间(不确定如何通过Powershell获取此值)
}
)
托利斯先生()
.ForEach(r=>dt.Rows.Add(r));//将每个对象[]添加到数据表中
//返回数据表
返回dt;
}

从技术上讲,这并不能回答您的问题,因为它不使用Win32\u进程查询。 但是,使用Powershell,它确实可以在很短的时间内(1.5秒~25秒)产生相同的结果

您需要在X64模式下运行此命令来查询64位进程,并且需要提高Powershell的权限才能返回用户名

请注意,这是我第一次从C#调用Powershell脚本,可能有更好的方法。 它还需要一些错误捕获,以使其更加健壮(不适合生产)
using System.Management.Automation;
using System.Management.Automation.Runspaces;

private DataTable getProcesses() 
{
    // Create the datatable and columns
    DataTable dt = new DataTable();
    dt.Columns.Add("ID");
    dt.Columns.Add("Name");
    dt.Columns.Add("Path");
    dt.Columns.Add("User");
    dt.Columns.Add("Priority");
    dt.Columns.Add("BasePriority"); 

    string script = $"get-process -IncludeUserName | select id, processname, path, username, priorityclass";

    List<string[]> psOutput = new List<string[]>();

    // Invoke Powershell cmdlet and get output
    using (PowerShell ps = PowerShell.Create())
    {
        ps.AddScript(script);
        var output = ps.Invoke();
        if(ps.Streams.Error.Count > 0)
        {
            throw new Exception($"Error running script:{Environment.NewLine}{string.Join(Environment.NewLine, ps.Streams.Error.Select(e => e.ToString()))}");
        }

        // clean and split the output
        psOutput.AddRange(output.Select(i => i.ToString().Replace("; ", ";").TrimStart("@{".ToCharArray()).TrimEnd('}').Split(';')));
    }

    // populate the DataTable
    psOutput
        .AsParallel()           // this does not really help when not calling Process.GetProcessById
        .Select(p => p.Select(f => f.Split("=")).ToDictionary(k => k.FirstOrDefault(), v => v.LastOrDefault()))
        .Select(kv => new object[] {    // "flatten" the dictionaries into object[]'s that will become the datarows
                        kv["Id"]
                        , kv["ProcessName"]
                        , kv["Path"]
                        , kv["UserName"]
                        , kv["PriorityClass"]
                        , Process.GetProcessById(int.Parse(kv["Id"])).BasePriority  // if you need the numerical base priority - takes quite a bit longer though (Not sure how to get this via Powershell)
                    }
            )
        .ToList()
        .ForEach(r => dt.Rows.Add(r));  // add each object[] to the datatable

    // return the datatable 
    return dt;
}