在 C# WPF 中监控进程统计信息






4.85/5 (24投票s)
在本文中,我将解释如何以统计数据和图表的形式监控任何实例的性能。
引言
在本文中,我将解释如何监控任何实例的性能。在示例应用程序中(其输出如下所示),我将使用 regedit 进程,但是您可以使用任何实例并通过不同的方式监控其统计信息。基于此,您还可以显示统计图表。

在 Windows 任务管理器中,我们通常观察到的与特定进程相关的内存是应用程序工作集内存。这是由进程加载的所有虚拟内存页使用的内存,并且由于任何原因都没有被交换到磁盘。我们可以使用“工作集 - 专用”PerformanceCounter
查看此内存。
对于物理内存使用情况,我们可以使用“工作集”PerformanceCounter
进行监控。我们还可以使用System.Diagnostics
命名空间的Process.WorkingSet64
属性获取此值。但是,您必须在使用Process
类属性之前调用Process.Refresh
方法,否则它可能无法提供准确的值。推荐的方法是使用性能计数器。
类似地,要查看进程的专用内存大小,我们有“专用字节”PerformanceCounter
,以及System.Diagnostics
命名空间中相同的Process.PrivateMemorySize64
属性值。
对于与特定实例相关的处理器时间,我们在Process
类中具有不同的属性。我们还有不同的性能计数器用于处理器时间的用法,例如“% 处理器时间”、“% 用户时间”和“% 特权时间”。对于处理器时间的性能计数器,第一次它将返回零值。为了避免这种情况,先调用PerformanceCounter
的NextValue()
,然后进行一些延迟(例如一秒,因为计数器值的提供者通常每秒更新一次),然后再次调用NextValue()
,因为它将计算这两个值。通过这种方式,您将获得PerformanceCounter
的正确值。我将在示例应用程序中演示这一点。
Using the Code
在这里,我用 C# WPF 写了一个示例应用程序。它将显示System.Diagnostics
命名空间中进程类的不同属性值以及PerformanceCounter
值。它将显示与内存和处理器相关的“regedit”进程统计信息。它将每秒更新这些统计值(您可以更改 regedit 实例并对任何进程使用此示例代码)。MemoryandProcessCheck()
函数中有一段代码显示进程的所有可能的计数器(此示例代码中的 regedit。我在本文结尾处写下了 regedit 进程的所有计数器)。如果您想查看 regedit 实例的所有可能的计数器,请调整Window1.xaml
中listView1
的外观并将其可见性属性设置为visible
。(如果您想使用 regedit 之外的其他进程来观察所有这些功能,只需更改文件开头的进程名称即可)。最后,我还显示图表以显示此特定实例的内存和处理器使用情况。对于图表显示,我使用的是Microsoft.Research.DynamicDataDisplay
命名空间。在运行此示例应用程序之前,请打开 regedit 或运行您想要查看性能统计信息的特定实例。完整的源代码,请下载附加的MemoryPerformanceMonitoring.zip。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Threading;
namespace MemoryPerformanceMonitoring
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
//instance name for which you want to monitor Statistics
string sProcName = "regedit";
ObservableCollection<StatisticsData> _StatisticsCollection =
new ObservableCollection<StatisticsData>();
public ObservableCollection<StatisticsData> StatisticsCollection
{
get { return _StatisticsCollection; }
}
public Window1()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
//create child thread for statistics display
Thread MonitorThd = new Thread(this.StatisticsMonitoringThread);
MonitorThd.Start();
// MemoryandProcessCheck();
}
public void StatisticsMonitoringThread(object obj)
{
MonitorMemoryandProcess();
}
//query whether the specified process running or not
private Process CurrentlyRunning(string sProcessName)
{
//get a list of all running processes on current system
Process[] Processes = Process.GetProcesses();
//Iterate to every process to check if it is out required process
foreach (Process SingleProcess in Processes)
{
if (SingleProcess.ProcessName.Contains(sProcessName))
{
//process found
return SingleProcess;
}
}
//Process not found
return null;
}
//main function for all processing
private bool MonitorMemoryandProcess()
{
string ProcessStatus = null;
string[] str = new string[10];
Process ReqProcess;
try
{
GC.GetTotalMemory(true); // how much GC total use
ReqProcess = CurrentlyRunning(sProcName);
do
{
if (ReqProcess != null)
{
// Refresh the current process property values.
ReqProcess.Refresh();
if (ReqProcess.Responding)
{
ProcessStatus = "Running";
}
else
{
ProcessStatus = "Not Responding";
}
PerformanceCounter totalProcessorTimeCounter =
new PerformanceCounter("Process",
"% Processor Time", ReqProcess.ProcessName);
PerformanceCounter UserProcessorTimeCounter =
new PerformanceCounter("Process",
"% User Time", ReqProcess.ProcessName);
PerformanceCounter PrivilegedProcessorTimeCounter =
new PerformanceCounter("Process",
"% Privileged Time", ReqProcess.ProcessName);
PerformanceCounter WorkingSetMemoryCounter =
new PerformanceCounter("Process",
"Working Set", ReqProcess.ProcessName);
PerformanceCounter WorkingSetPeakMemoryCounter =
new PerformanceCounter("Process",
"Working Set Peak", ReqProcess.ProcessName);
PerformanceCounter ThreadCountCounter =
new PerformanceCounter("Process",
"Thread Count", ReqProcess.ProcessName);
PerformanceCounter WorkingSetPrivateMemoryCounter =
new PerformanceCounter("Process",
"Working Set - Private", ReqProcess.ProcessName);
PerformanceCounter HandleCountCounter =
new PerformanceCounter("Process",
"Handle Count", ReqProcess.ProcessName);
totalProcessorTimeCounter.NextValue();
UserProcessorTimeCounter.NextValue();
PrivilegedProcessorTimeCounter.NextValue();
System.Threading.Thread.Sleep(1000);// 1 second wait
// Dispatcher.Invoke(new ClearListViewFromOutside(ClearListView));
str[0] = ReqProcess.ProcessName;
str[1] = ProcessStatus;
str[2] = (WorkingSetMemoryCounter.NextValue() / 1024) + "K";
str[3] = (WorkingSetPrivateMemoryCounter.NextValue() / 1024) + "K";
str[4] = (WorkingSetPeakMemoryCounter.NextValue() / 1024) + "K";
str[5] = (ThreadCountCounter.NextValue()).ToString();
str[6] = (HandleCountCounter.NextValue()).ToString();
str[7] = (totalProcessorTimeCounter.NextValue()).ToString();
str[8] = (UserProcessorTimeCounter.NextValue()).ToString();
str[9] = (PrivilegedProcessorTimeCounter.NextValue()).ToString();
}
else
{
str[0] = sProcName;
str[1] = "Not Started";
str[2] = "";
str[3] = "";
str[4] = "";
str[5] = "";
str[6] = "";
str[7] = "";
str[8] = "";
str[9] = "";
}
Dispatcher.Invoke(new UpdateGUIOutsideFeedbackMessage
(UpdateGUIOutsideFeedbackMsg), new object[] { str });
}while (true); //infinite loop
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Performance Monitoring Statistics Exception ",
MessageBoxButton.OK, MessageBoxImage.Error);
return false;
}
return true;
}
public delegate void UpdateGUIOutsideFeedbackMessage(string[] msg);
public void UpdateGUIOutsideFeedbackMsg(string[] msg)
{
Mutex firstMutex = new Mutex(false);
firstMutex.WaitOne();
//first clear the previous value and than add new one
StatisticsCollection.Clear();
_StatisticsCollection.Add(new StatisticsData
{
ProcessName = msg[0],
ProcessRunningStatus = msg[1],
WorkingSetMemory = msg[2],
WorkingSetPrivateMemory = msg[3],
WorkingSetPeak = msg[4],
ThreadCount = msg[5],
HandleCount = msg[6],
TotalProcessorTime = msg[7],
UserProcessorTime = msg[8],
PrivilegedProcessorTime = msg[9]
});
firstMutex.Close();
}
public delegate void ClearListViewFromOutside();
public void ClearListView()
{
StatisticsCollection.Clear();
}
private void CloseButton_Click(object sender, RoutedEventArgs e)
{
//forcefully destroy the application
Environment.Exit(0);
}
private bool MemoryandProcessCheck()
{
Process ReqProcess;
try{
GC.GetTotalMemory(true); // how much GC total use
ReqProcess = CurrentlyRunning(sProcName);
if (ReqProcess != null)
{
// calculate the CPU load
System.TimeSpan CPULoad = (DateTime.Now - ReqProcess.StartTime);
listView1.Items.Add("CPU load: " +
(ReqProcess.TotalProcessorTime.TotalMilliseconds /
CPULoad.TotalMilliseconds) * 100);
PerformanceCounter TotalProcessorTimeCounter =
new PerformanceCounter("Process",
"% Processor Time", ReqProcess.ProcessName);
PerformanceCounter ProcessorUserTimeCounter =
new PerformanceCounter("Process",
"% User Time", ReqProcess.ProcessName);
PerformanceCounter ProcessorPrivilegedTimeCounter =
new PerformanceCounter("Process",
"% Privileged Time", ReqProcess.ProcessName);
PerformanceCounter ElapsedTimeCounter =
new PerformanceCounter("Process",
"Elapsed Time", ReqProcess.ProcessName);
PerformanceCounter VirtualBytesPeakMemoryCounter =
new PerformanceCounter("Process",
"Virtual Bytes Peak", ReqProcess.ProcessName);
PerformanceCounter VirtualBytesMemoryCounter =
new PerformanceCounter("Process",
"Virtual Bytes", ReqProcess.ProcessName);
PerformanceCounter WorkingSetMemoryCounter =
new PerformanceCounter("Process",
"Working Set", ReqProcess.ProcessName);
PerformanceCounter WorkingSetPeakMemoryCounter =
new PerformanceCounter("Process",
"Working Set Peak", ReqProcess.ProcessName);
PerformanceCounter PrivateBytesMemoryCounter =
new PerformanceCounter("Process",
"Private Bytes", ReqProcess.ProcessName);
PerformanceCounter WorkingSetPrivateMemoryCounter =
new PerformanceCounter("Process",
"Working Set - Private", ReqProcess.ProcessName);
PerformanceCounter PageFileBytesPeakMemoryCounter =
new PerformanceCounter("Process",
"Page File Bytes Peak", ReqProcess.ProcessName);
PerformanceCounter ThreadCountCounter =
new PerformanceCounter("Process",
"Thread Count", ReqProcess.ProcessName);
PerformanceCounter HandleCountCounter =
new PerformanceCounter("Process",
"Handle Count", ReqProcess.ProcessName);
TotalProcessorTimeCounter.NextValue();
ProcessorUserTimeCounter.NextValue();
ProcessorPrivilegedTimeCounter.NextValue();
//if there is no wait for 1 second after the first call
//to NextValue() than processing time value will not be correct.
System.Threading.Thread.Sleep(1000);// 1 second wait
if (!ReqProcess.HasExited)
{
// Refresh the process property values.
ReqProcess.Refresh();
// Display process statistics related to memory.
listView1.Items.Add(ReqProcess.ProcessName);
listView1.Items.Add("******************************");
listView1.Items.Add("Working Set: " +
(WorkingSetMemoryCounter.NextValue() / 1024) +
"K"); // more efficient. update quickly as compare to
//ReqProcess.WorkingSet64 if Process's Refresh() did not call
listView1.Items.Add("Physical memory usage(Working Set memory):
" + ReqProcess.WorkingSet64 / 1024 + "K");
listView1.Items.Add("Working Set - Private: " +
(WorkingSetPrivateMemoryCounter.NextValue() / 1024) + "K");
listView1.Items.Add("Private Memory Size: " +
ReqProcess.PrivateMemorySize64 / 1024 +
"K"); // usually same with PagedMemorySize64
listView1.Items.Add("Private Bytes: " +
(PrivateBytesMemoryCounter.NextValue() / 1024) + "K");
listView1.Items.Add("Virtual memory paging file
(Process using RAM): " + ReqProcess.PagedMemorySize64 / 1024 + "K");
listView1.Items.Add("Working Set Peak: " +
(WorkingSetPeakMemoryCounter.NextValue() / 1024) +
"K"); //same as peakWorkingSet
listView1.Items.Add("Peak physical memory usage: " +
ReqProcess.PeakWorkingSet64 / 1024 + "K");
listView1.Items.Add("Thread Count: " +
ThreadCountCounter.NextValue()); // how many threads are
listView1.Items.Add("Handle Count: " +
HandleCountCounter.NextValue()); // how many handles
//The amount of system memory, in bytes, allocated for the
//associated process that can be written to the virtual memory paging file.
listView1.Items.Add("Page System Memory Size: " +
ReqProcess.PagedSystemMemorySize64 / 1024 + "K");
//The amount of system memory, in bytes, allocated for the
//associated process that cannot be written to the virtual memory paging file.
listView1.Items.Add("Nonpage System Memory Size: " +
ReqProcess.NonpagedSystemMemorySize64 / 1024 + "K");
listView1.Items.Add("Virtual Memory: " +
ReqProcess.VirtualMemorySize64 / 1024 + "K");
listView1.Items.Add("Virtual Bytes: " +
(VirtualBytesMemoryCounter.NextValue() / 1024) + "K");
listView1.Items.Add("Virtual Bytes Peak: " +
(VirtualBytesPeakMemoryCounter.NextValue() / 1024) + "K");
listView1.Items.Add("Peak Virtual Memory usage: " +
ReqProcess.PeakVirtualMemorySize64 / 1024 + "K");
listView1.Items.Add("Page File Bytes Peak: " +
(PageFileBytesPeakMemoryCounter.NextValue() / 1024) + "K");
listView1.Items.Add("Peak virtual memory paging file usage: " +
ReqProcess.PeakPagedMemorySize64 / 1024 + "K");
if (ReqProcess.Responding)
{
listView1.Items.Add("Status = Running");
}
else
{
listView1.Items.Add("Status = Not Responding");
}
//Display process statistics related to Processor
listView1.Items.Add("%Processor Time: " +
TotalProcessorTimeCounter.NextValue());
listView1.Items.Add("%User Time: " +
ProcessorUserTimeCounter.NextValue());
listView1.Items.Add("%Privileged Time: " +
ProcessorPrivilegedTimeCounter.NextValue());
listView1.Items.Add("Elapsed Time: " +
ElapsedTimeCounter.NextValue());
listView1.Items.Add("Total processor time: " +
ReqProcess.TotalProcessorTime);
listView1.Items.Add("User processor time: " +
ReqProcess.UserProcessorTime);
listView1.Items.Add("Privileged processor time: " +
ReqProcess.PrivilegedProcessorTime);
//code to see all the possible Counter of a process.
PerformanceCounterCategory[] PCounterCtg =
PerformanceCounterCategory.GetCategories();
foreach (PerformanceCounterCategory category in PCounterCtg)
{
if (category.CategoryName != "Process")
continue;
listView1.Items.Add("");
listView1.Items.Add("Category: " + category.CategoryName);
string[] instances = category.GetInstanceNames();
if (instances.Length == 0)
{
foreach (PerformanceCounter PCounter in category.GetCounters())
listView1.Items.Add(" Counter: " + PCounter.CounterName);
}
else
{
foreach (string instance in instances)
{
if (!(instance.Equals(sProcName)))
continue;
listView1.Items.Add(" Instance: " + instance);
if (category.InstanceExists(instance))
foreach
(PerformanceCounter Pctr in category.GetCounters(instance))
listView1.Items.Add(" Counter: " + Pctr.CounterName);
}
}
}
//end test code to see all possible counter of specific process
}
}
else
{
listView1.Items.Add("");
listView1.Items.Add("Process " + sProcName + " is not started. ");
}
}
catch (Exception ex)
{
listView1.Items.Add("Process check exception: " + ex.Message);
return false;
}
return true;
}
private void button1_Click(object sender, RoutedEventArgs e)
{
Process viewProcess = null;
viewProcess = CurrentlyRunning(sProcName);
if (viewProcess != null)
{
Window2 w2 = new Window2("Process", "Working Set -
Private", sProcName, "Memory (Private Working Set)");
w2.Show();
}
else
{
MessageBox.Show(sProcName + " instance is not running.",
"Memory Graph Exception", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void button2_Click(object sender, RoutedEventArgs e)
{
Process viewProcess = null;
viewProcess = CurrentlyRunning(sProcName);
if (viewProcess != null)
{
Window2 w2 = new Window2("Process",
"% Processor Time", sProcName, "%Processor Time (Total)");
w2.Show();
}
else
{
MessageBox.Show(sProcName + " instance is not running.",
"Processor Graph Exception", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
public class StatisticsData
{
public string ProcessName { get; set; }
public string ProcessRunningStatus { get; set; }
public string WorkingSetMemory { get; set; }
public string WorkingSetPrivateMemory { get; set; }
public string WorkingSetPeak { get; set; }
public string ThreadCount { get; set; }
public string HandleCount { get; set; }
public string TotalProcessorTime { get; set; }
public string UserProcessorTime { get; set; }
public string PrivilegedProcessorTime { get; set; }
}
}
您可以在下面的图片中看到,当您在注册表中搜索某些内容时,它通常会占用多少内存和处理器时间。

在进程类别中,“regedit”实例的计数器是
- 实例:regedit
- 计数器:% 处理器时间
- 计数器:% 用户时间
- 计数器:% 特权时间
- 计数器:虚拟字节峰值
- 计数器:虚拟字节
- 计数器:每秒页错误
- 计数器:工作集峰值
- 计数器:工作集
- 计数器:页面文件字节峰值
- 计数器:页面文件字节
- 计数器:专用字节
- 计数器:线程数
- 计数器:基本优先级
- 计数器:已用时间
- 计数器:进程 ID
- 计数器:创建进程 ID
- 计数器:池分页字节
- 计数器:池非分页字节
- 计数器:句柄数
- 计数器:每秒 I/O 读取操作
- 计数器:每秒 I/O 写入操作
- 计数器:每秒 I/O 数据操作
- 计数器:每秒 I/O 其他操作
- 计数器:每秒 I/O 读取字节
- 计数器:每秒 I/O 写入字节
- 计数器:每秒 I/O 数据字节
- 计数器:每秒 I/O 其他字节
- 计数器:工作集 - 专用
历史
- 2009 年 7 月 25 日:初始发布