为了账号安全,请及时绑定邮箱和手机立即绑定

如何让 Win32_Process 查询更快?

如何让 Win32_Process 查询更快?

C#
拉丁的传说 2022-06-12 11:06:55
我有以下函数将进程信息提取到 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总和。既然我已经“挑出”了问题,我该如何优化该功能?
查看完整描述

2 回答

?
有只小跳蛙

TA贡献1824条经验 获得超8个赞

从技术上讲,这并不能回答您的问题,因为它不使用 Win32_Process 查询。但是,使用 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;

}


查看完整回答
反对 回复 2022-06-12
?
一只名叫tom的猫

TA贡献1906条经验 获得超3个赞

我个人一直想摆脱使用DataTable. 您可以使用List<>可以解决许多潜在问题的集合。


话虽如此,您可能想查看ORMi库以获取有关List<>. 您可以通过以下方式实现您正在尝试的目标:


        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;

        }

上面的代码适用于dynamic对象,不需要您编写任何模型。就是那个小代码。如果您需要使用方法,那么您可以声明您的模型并使用强类型对象:


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

}

2)查询WMI


        List<Process> processes = helper.Query<Process>().ToList();


        foreach (Process p in processes)

        {

            pid = p.ProcessID;

            name = p.Name;

            path = p.ExecutablePath ?? String.Empty;

            priort = p.Priority ?? String.Empty;


            dynamic d = p.GetOwnerSid();

            ProcessOwner po = p.GetOwner();

        }

即使对于这个任务,第二种方式可能看起来有点太多的工作,你会得到更干净和易于理解的代码。


注意:我已经用 ORMi 尝试过你的代码,我会在 1-2 秒内得到结果。正如其他人所说,这可能取决于您的环境。


SELECT注意 2:始终只使用语句中需要的属性。WMI a 非常昂贵SELECT *。始终指定属性。在您的情况下,它将是:


Select ProcessID, Name, ExecutablePath, Priority From Win32_Process

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


查看完整回答
反对 回复 2022-06-12
  • 2 回答
  • 0 关注
  • 210 浏览

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号