Содержание
Если вы ищете создание собственного диспетчера задач или просто создать виджет для своего приложения, который отображает информацию о процессах в Windows, то вы находитесь в правильном месте.
В этой статье вы узнаете, как получить информацию о процессах в Windows и как легко создать приложение, подобное диспетчеру задач.
1. Используйте пространство имен Diagnostics
Для работы с процессами в вашем приложении вам необходимо импортировать 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. Создание приложения, похожего на диспетчер задач
В качестве первого шага вам нужно будет создать элемент List View в вашей форме, чтобы отобразить информацию о нем. Этот элемент списка необходимо настроить следующим образом:
- На панели свойств элемента (нижний правый угол) установите Посмотреть собственность на подробности из выпадающего меню.
- На той же панели выберите свойство Columns, и должно появиться новое окно, в этом окне вы создадите 6 обязательных столбцов, которые мы будем использовать для отображения соответственно той же информации, что и диспетчер задач:
Теперь вы будете готовы легко отобразить некоторую информацию в этом списке.
Если вы уже прочитали обо всех свойствах, которые объект внутри массива возвращает Process.GetProcesses
метод, вы увидите, что по сравнению с информацией, которую вы видите в исходном диспетчере задач Windows, это не так уж полно. Список процессов не содержит всю информацию, которую нам нужно показать в нашем списке, поэтому нам нужно добавить ссылку на System.Management
чтобы получить имя пользователя, связанное с процессом и описанием. Для этого вам нужно будет добавить ссылку с менеджером ссылок Visual Studio. Для этого выполните следующие действия:
Щелкните правой кнопкой мыши на Project, Добавить ссылки
Выберите Сборки (рамки) Вкладка и поиск
System.Management
и, наконец, добавьте ссылку и нажмите ОК.
Нам нужно добавить System.Management
создавать запросы в WMI Classes
, Узнайте больше о получении классов WMI в .NET в msdn здесь. После того, как ссылка была добавлена в ваш проект, вы можете импортировать пространство имен System.Management
в вашем проекте. Помимо импорта System.Dynamic
тоже на вершине вашего класса:
using System.Management;
using System.Dynamic;
Наконец, мы можем начать с кода для отображения информации о процессах в вашем приложении.
Вам решать, когда и где вы хотите выполнить код, который мы сейчас объясним, он может быть выполнен либо нажатием кнопки, либо событием Form_Load формы. Перед тем, как начать с рендеринга, нам нужно решить первую проблему, а именно, что список процессов не содержит имени пользователя Windows, запустившего процесс, и описания процесса. Как мы уже говорили ранее, нам нужно запросить класс WMI, а именно Win32_Process, этот запрос будет искать процесс по одному идентификатору. Как вы, возможно, знаете, у каждого процесса в Windows есть идентификатор, чтобы его можно было легко идентифицировать, поэтому мы можем создать следующий метод, который ожидает в качестве первого аргумента идентификатор процесса, который вы хотите запросить, и он возвратит объект Expando с 2 свойства, которые нам нужны: описание и имя пользователя:
///
/// Returns an Expando object with the description and username of a process from the process ID.
///
///
///
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.
Вторая проблема заключается в том, что вы не хотите отображать значение используемой памяти пользователю в байтах, поскольку ее совсем не легко прочитать, поэтому, если вы сделаете его читаемым с понятными представлениями в КБ, МБ или ГБ и т.д. они будут благодарны. Чтобы преобразовать байты, возвращенные из информации о процессе, в читаемое значение, вы можете использовать следующий метод:
///
/// Method that converts bytes to its human readable value
///
///
///
public string BytesToReadableValue(long number)
{
List suffixes = new List { " 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();
}
Обратите внимание, что метод ожидает длинное значение и возвращает строку. В качестве последнего шага мы можем теперь начать отображать информацию в представлении списка.
Вам необходимо получить список процессов и сохранить его в переменной, кроме того, вам нужно создать элемент списка изображений, в котором будут храниться значки каждого процесса (в случае, если они есть). Затем нам нужно перебрать массив процессов с помощью оператора foreach, в каждом цикле GetProcessExtraInformation
будет выполнен (с идентификатором процесса в качестве первого аргумента), чтобы получить необходимую информацию, которой у нас нет, из переменной процесса в цикле. Затем будет создан массив строк, в котором будут храниться (соответственно значения должны следовать в том же порядке, что и столбцы в элементе списка) информация о каждой строке в элементе списка (обратите внимание, что мы преобразуем байты в читаемое значение с помощью BytesToReadableValue
метод). При желании вы можете добавить значки в список, добавив новый элемент в ранее созданный список изображений. В списке изображений необходимо указать идентификатор (ключ) для изображения, которое вы хотите добавить (в данном случае значок процесса), а в качестве второго аргумента - данные изображения значка, которые можно получить с помощью Icon.ExtractAssociatedIcon
метод, который ожидает путь к исполняемому файлу процесса (который может быть получен через объект процесса), этот значок можно преобразовать в растровое изображение, как показано в примере, чтобы повысить качество, однако при желании вы можете удалить метод toBitmap.
Наконец, создайте новый ListViewItem для добавления со строкой в качестве первого аргумента и укажите, какое изображение из списка изображений следует использовать для элемента, и добавьте его в представление списка:
///
/// This method renders all the processes of Windows on a ListView with some values and icons.
///
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, со свойством View, установленным в Details, и соответственно с 6 элементами в свойстве Columns.
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();
}
///
/// This method renders all the processes of Windows on a ListView with some values and icons.
///
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;
}
///
/// Method that converts bytes to its human readable value
///
///
///
public string BytesToReadableValue(long number)
{
List suffixes = new List { " 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();
}
///
/// Returns an Expando object with the description and username of a process from the process ID.
///
///
///
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, а рядом с названием процесса и другими свойствами появится значок.