个性化阅读
专注于IT技术分析

如何在WinForms中使用C#列出所有Windows进程及其属性(类似任务管理器)

本文概述

如果你要创建自定义任务管理器, 或者只是为你的应用程序创建一个小部件, 以显示有关Windows中进程的信息, 那么你来对地方了。

在本文中, 你将学习如何获取有关Windows中进程的信息, 以及如何轻松创建类似于应用程序的任务管理器。

1.使用诊断名称空间

为了使用应用程序中的进程, 你将需要在类顶部导入System.Diagnostics命名空间:

using System.Diagnostics;

System.Diagnostics命名空间提供了允许你与系统进程, 事件日志和性能计数器进行交互的类。

2.列出并检索所有过程的基本信息

如果你因为只需要知道如何在Windows中检索所有进程的列表而在这里, 则只需要注意以下代码段即可:

// Create an array to store the processes
Process[] processList = Process.GetProcesses();

// Loop through the array to show information of every process in your console
foreach (Process process in processList)
{
    Console.WriteLine(@"
        {0} | ID: {1} | Status {2} | Memory (private working set in Bytes) {3}", process.ProcessName, process.Id, process.Responding, process.PrivateMemorySize64);
}

GetProcesses方法将通过返回一个数组来为你解决问题, 该数组包含有关Windows上所有进程的信息。项目中先前代码的执行应生成类似于以下内容的输出:

Memory Compression | ID: 2560 | Status True | Memory (private working set)876544

conhost | ID: 9060 | Status True | Memory (private working set)5054464

MSASCuiL | ID: 13196 | Status True | Memory (private working set)3203072

svchost | ID: 1964 | Status True | Memory (private working set)3485696

chrome | ID: 18740 | Status True | Memory (private working set)97230848

chrome | ID: 2044 | Status True | Memory (private working set)26988544

VBCSCompiler | ID: 6688 | Status True | Memory (private working set)103931904

AppVShNotify | ID: 4020 | Status True | Memory (private working set)1605632

很容易吧?流程对象具有许多属性, 你可以根据需要向用户显示这些属性, 有关更多属性, 请参见MSDN。

3.创建类似任务管理器的应用程序

第一步, 你需要在表单中创建一个”列表视图”项, 以显示其信息。此列表视图项需要配置如下:

  1. 在元素的属性面板(右下角)上, 从下拉菜单中将”视图”属性设置为”详细信息”。
  2. 在同一面板上, 选择Columns属性, 然后会出现一个新窗口, 在该窗口中, 你将创建6个必填列, 我们将分别使用它们显示与任务管理器相同的信息:
任务管理器设计C#

现在, 你可以轻松在此列表视图上显示一些信息。

如果你已经了解了Process.GetProcesses方法返回的数组内对象的所有属性, 则与在Windows原始任务管理器中看到的信息相比, 它并不是那么完整。进程列表并不包含我们需要在列表中显示的所有信息, 因此, 我们需要添加对System.Management的引用以获取与该进程和描述相关的用户名。为此, 你将需要使用Visual Studio的引用管理器添加引用。为此, 请按照下列步骤操作:

  1. 右键单击项目, 添加引用

  2. 选择”程序集(框架)”选项卡, 然后搜索System.Management, 最后添加引用, 然后单击”确定”。

手动添加System.Management参考Visual Studio

我们需要添加System.Management来在WMI类中创建查询。在此处阅读有关在msdn中检索.NET中的WMI类的更多信息。将引用添加到你的项目后, 你可以在项目中导入System.Management的命名空间。除了在类的顶部导入System.Dynamic之外:

using System.Management;
using System.Dynamic;

最后, 我们可以从代码开始, 以显示有关你的应用程序上的进程的信息。

由你决定何时何地执行我们现在将解释的代码, 可以通过单击按钮或在表单的Form_Load事件中执行它。在开始渲染之前, 我们需要解决第一个问题, 即进程列表中不包含启动该进程的Windows用户的用户名和该进程的描述。如前所述, 我们需要查询一个WMI类, 即Win32_Process, 该查询将通过单个ID搜索一个进程。你可能知道Windows中的每个进程都有一个ID, 以便能够轻松识别, 因此我们可以创建以下方法, 该方法期望将要查询的进程的ID作为第一个参数, 并将返回一个Expando对象, 并带有我们需要的2个属性:描述和用户名:

/// <summary>
/// Returns an Expando object with the description and username of a process from the process ID.
/// </summary>
/// <param name="processId"></param>
/// <returns></returns>
public ExpandoObject GetProcessExtraInformation(int processId)
{
    // Query the Win32_Process
    string query = "Select * From Win32_Process Where ProcessID = " + processId;
    ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
    ManagementObjectCollection processList = searcher.Get();

    // Create a dynamic object to store some properties on it
    dynamic response = new ExpandoObject();
    response.Description = "";
    response.Username = "Unknown";

    foreach (ManagementObject obj in processList)
    {
        // Retrieve username 
        string[] argList = new string[] { string.Empty, string.Empty };
        int returnVal = Convert.ToInt32(obj.InvokeMethod("GetOwner", argList));
        if (returnVal == 0)
        {
            // return Username
            response.Username = argList[0];

            // You can return the domain too like (PCDesktop-123123\Username using instead
            //response.Username = argList[1] + "\\" + argList[0];
        }

        // Retrieve process description if exists
        if (obj["ExecutablePath"] != null)
        {
            try
            {
                FileVersionInfo info = FileVersionInfo.GetVersionInfo(obj["ExecutablePath"].ToString());
                response.Description = info.FileDescription;
            }
            catch{}
        }
    }

    return response;
}

该函数非常易于理解, 它将与GetProcesses提供的每个进程一起执行。

第二个问题是, 你不想以字节为单位向用户显示已用内存的值, 因为它根本不易读取, 因此, 如果以可读性强的KB, MB或GB表示可读性等等, 他们将很感激。要将过程信息返回的字节转换为可读值, 可以使用以下方法:

/// <summary>
/// Method that converts bytes to its human readable value
/// </summary>
/// <param name="number"></param>
/// <returns></returns>
public string BytesToReadableValue(long number)
{
    List<string> suffixes = new List<string> { " B", " KB", " MB", " GB", " TB", " PB" };

    for (int i = 0; i < suffixes.Count; i++)
    {
        long temp = number / (int)Math.Pow(1024, i + 1);

        if (temp == 0)
        {
            return (number / (int)Math.Pow(1024, i)) + suffixes[i];
        }    
    }

    return number.ToString();
}

请注意, 该方法需要一个长值, 并返回一个字符串。作为最后一步, 我们现在可以开始在列表视图上呈现信息。

你需要检索进程列表并将其存储到变量中, 此外, 你还应该创建一个Image List项, 该项将存储每个进程的图标(如果有的话)。然后, 我们需要使用foreach语句遍历processs数组, 在每个循环上, 将执行GetProcessExtraInformation(以过程的ID作为第一个参数)来检索过程变量中没有的必要信息在循环上。然后将创建行数组, 并将其存储(分别因为值需要遵循与List View项目中的Columns相同的顺序)List View项目中每一行的信息(请注意, 我们将转换字节使用BytesToReadableValue方法将其设置为可读值)。 (可选)你可以通过将新项目添加到先前创建的图像列表中来将图标添加到列表中。在图像列表中, 你需要为要添加的图像(在本例中为流程的图标)提供标识符(键), 并作为第二个参数, 可以使用Icon.ExtractAssociatedIcon检索到的图标的图像数据。期望过程可执行文件的路径(可以通过过程对象检索)的方法, 可以将该图标转换为位图, 如示例中所示, 以提高质量, 但是可以根据需要删除toBitmap方法。

最后, 创建一个新的ListViewItem, 并将该行作为第一个参数添加到该列表中, 并指定应使用”图像列表”中的哪个图像作为该项, 并将其添加到”列表视图”中:

/// <summary>
/// This method renders all the processes of Windows on a ListView with some values and icons.
/// </summary>
public void renderProcessesOnListView()
{
    // Create an array to store the processes
    Process[] processList = Process.GetProcesses();

    // Create an Imagelist that will store the icons of every process
    ImageList Imagelist = new ImageList();

    // Loop through the array of processes to show information of every process in your console
    foreach (Process process in processList)
    {
        // Define the status from a boolean to a simple string
        string status = (process.Responding == true ? "Responding" : "Not responding");

        // Retrieve the object of extra information of the process (to retrieve Username and Description)
        dynamic extraProcessInfo = GetProcessExtraInformation(process.Id);

        // Create an array of string that will store the information to display in our 
        string[] row = {
            // 1 Process name
            process.ProcessName, // 2 Process ID
            process.Id.ToString(), // 3 Process status
            status, // 4 Username that started the process
            extraProcessInfo.Username, // 5 Memory usage
            BytesToReadableValue(process.PrivateMemorySize64), // 6 Description of the process
            extraProcessInfo.Description
        };

        //
        // As not every process has an icon then, prevent the app from crash
        try
        {
            Imagelist.Images.Add(
                // Add an unique Key as identifier for the icon (same as the ID of the process)
                process.Id.ToString(), // Add Icon to the List 
                Icon.ExtractAssociatedIcon(process.MainModule.FileName).ToBitmap()
            );
        }
        catch { }

        // Create a new Item to add into the list view that expects the row of information as first argument
        ListViewItem item = new ListViewItem(row)
        {
            // Set the ImageIndex of the item as the same defined in the previous try-catch
            ImageIndex = Imagelist.Images.IndexOfKey(process.Id.ToString())
        };

        // Add the Item
        listView1.Items.Add(item);
    }

    // Set the imagelist of your list view the previous created list :)
    listView1.LargeImageList = Imagelist;
    listView1.SmallImageList = Imagelist;
}

请注意, 你需要将LargeImageList和SmallImageList属性的值设置为我们为图标创建的ImageList。要使此工作有效, 仅在需要在列表视图中显示服务时才需要执行renderProcessesOnListView函数。

4.最后的例子

如果你已经阅读了所有内容的工作原理, 则可以简单地遵循整个示例并自己在项目中实现它:

重要

不要忘了, 你需要在项目中添加对System.Management的引用, 如步骤3所示。另一方面, 你需要创建一个列表视图项, 其标识为listView1, 其”视图”属性设置为”详细信息”, 并且在”列”属性中分别具有6个项目。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

// Required namespaces
using System.Diagnostics;
using System.Management;
using System.Dynamic;

namespace Sandbox
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // Once the form loads, render the items on the list
            renderProcessesOnListView();
        }

        /// <summary>
        /// This method renders all the processes of Windows on a ListView with some values and icons.
        /// </summary>
        public void renderProcessesOnListView()
        {
            // Create an array to store the processes
            Process[] processList = Process.GetProcesses();

            // Create an Imagelist that will store the icons of every process
            ImageList Imagelist = new ImageList();

            // Loop through the array of processes to show information of every process in your console
            foreach (Process process in processList)
            {
                // Define the status from a boolean to a simple string
                string status = (process.Responding == true ? "Responding" : "Not responding");

                // Retrieve the object of extra information of the process (to retrieve Username and Description)
                dynamic extraProcessInfo = GetProcessExtraInformation(process.Id);

                // Create an array of string that will store the information to display in our 
                string[] row = {
                    // 1 Process name
                    process.ProcessName, // 2 Process ID
                    process.Id.ToString(), // 3 Process status
                    status, // 4 Username that started the process
                    extraProcessInfo.Username, // 5 Memory usage
                    BytesToReadableValue(process.PrivateMemorySize64), // 6 Description of the process
                    extraProcessInfo.Description
                };

                //
                // As not every process has an icon then, prevent the app from crash
                try
                {
                    Imagelist.Images.Add(
                        // Add an unique Key as identifier for the icon (same as the ID of the process)
                        process.Id.ToString(), // Add Icon to the List 
                        Icon.ExtractAssociatedIcon(process.MainModule.FileName).ToBitmap()
                    );
                }
                catch { }

                // Create a new Item to add into the list view that expects the row of information as first argument
                ListViewItem item = new ListViewItem(row)
                {
                    // Set the ImageIndex of the item as the same defined in the previous try-catch
                    ImageIndex = Imagelist.Images.IndexOfKey(process.Id.ToString())
                };

                // Add the Item
                listView1.Items.Add(item);
            }

            // Set the imagelist of your list view the previous created list :)
            listView1.LargeImageList = Imagelist;
            listView1.SmallImageList = Imagelist;
        }

        /// <summary>
        /// Method that converts bytes to its human readable value
        /// </summary>
        /// <param name="number"></param>
        /// <returns></returns>
        public string BytesToReadableValue(long number)
        {
            List<string> suffixes = new List<string> { " B", " KB", " MB", " GB", " TB", " PB" };

            for (int i = 0; i < suffixes.Count; i++)
            {
                long temp = number / (int)Math.Pow(1024, i + 1);

                if (temp == 0)
                {
                    return (number / (int)Math.Pow(1024, i)) + suffixes[i];
                }    
            }

            return number.ToString();
        }

        /// <summary>
        /// Returns an Expando object with the description and username of a process from the process ID.
        /// </summary>
        /// <param name="processId"></param>
        /// <returns></returns>
        public ExpandoObject GetProcessExtraInformation(int processId)
        {
            // Query the Win32_Process
            string query = "Select * From Win32_Process Where ProcessID = " + processId;
            ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
            ManagementObjectCollection processList = searcher.Get();

            // Create a dynamic object to store some properties on it
            dynamic response = new ExpandoObject();
            response.Description = "";
            response.Username = "Unknown";
 
            foreach (ManagementObject obj in processList)
            {
                // Retrieve username 
                string[] argList = new string[] { string.Empty, string.Empty };
                int returnVal = Convert.ToInt32(obj.InvokeMethod("GetOwner", argList));
                if (returnVal == 0)
                {
                    // return Username
                    response.Username = argList[0];

                    // You can return the domain too like (PCDesktop-123123\Username using instead
                    //response.Username = argList[1] + "\\" + argList[0];
                }

                // Retrieve process description if exists
                if (obj["ExecutablePath"] != null)
                {
                    try
                    {
                        FileVersionInfo info = FileVersionInfo.GetVersionInfo(obj["ExecutablePath"].ToString());
                        response.Description = info.FileDescription;
                    }
                    catch{}
                }
            }

            return response;
        }
    }
}

如文章图像所示, 将出现一个列出Windows中所有进程的窗口, 并且该进程的名称和其他属性旁边将显示图标。

编码愉快!

赞(0)
未经允许不得转载:srcmini » 如何在WinForms中使用C#列出所有Windows进程及其属性(类似任务管理器)

评论 抢沙发

评论前必须登录!