WPF进程列表ProcessDateTime变0001-01-01的原因及修复方案
WPF进程监控应用ProcessDateTime字段异常问题
我开发了一款WPF进程监控应用,运行时首次启动的1秒内所有进程的ProcessDateTime列显示当前时间,但1秒后该字段全部变为0001-01-01 00:00:00。相关MainWindow.xaml.cs代码如下:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Threading; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Media; namespace wpfMultiThreadListViewUpdate { public class ProcessesInfo { public string Name { get; set; } public string Status { get; set; } public int Id { get; set; } public long MemoryUsage { get; set; } public List<ProcessesInfo> SubProcesses { get; set; } = new List<ProcessesInfo>(); public DateTime ProcessDateTime { get; set; } } public partial class MainWindow : Window { private Dictionary<int, ProcessesInfo> processDictionary = new Dictionary<int, ProcessesInfo>(); Process[] processes; // New property for selected process public ProcessesInfo SelectedProcess { get; set; } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } public MainWindow() { InitializeComponent(); WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen; processes = Process.GetProcesses(); showInformation(processes.Length, lstvw); System.Timers.Timer timer = new System.Timers.Timer(); timer.Elapsed += Timer_Elapsed; timer.Interval = new TimeSpan(0, 0, 1).TotalMilliseconds; timer.Start(); } private void MainWindow_Loaded(object sender, RoutedEventArgs e) { } private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { UpdateProcessList(); } private void searchBox_TextChanged(object sender, TextChangedEventArgs e) { } private void UpdateProcessList() { // New list to hold the updated process information List<ProcessesInfo> updatedProcessList = new List<ProcessesInfo>(); processes = Process.GetProcesses(); // Check for killed processes and update the status foreach (var processInfo in processDictionary.Values.ToList()) { ProcessesInfo updatedProcess = new ProcessesInfo { Name = processInfo.Name, Id = processInfo.Id, MemoryUsage = processInfo.MemoryUsage, SubProcesses = processInfo.SubProcesses }; if (processes.All(p => p.Id != processInfo.Id)) { updatedProcess.Status = "Killed"; } else { updatedProcess.Status = "Opened"; } updatedProcessList.Add(updatedProcess); } // Check for new processes and add them to the dictionary foreach (var process in processes) { if (!processDictionary.ContainsKey(process.Id)) { processDictionary.Add(process.Id, new ProcessesInfo { Name = process.ProcessName, Id = process.Id, Status = "Opened", MemoryUsage = process.WorkingSet64 / 1024, }); updatedProcessList.Add(processDictionary[process.Id]); } else { // Update memory usage for existing processes processDictionary[process.Id].MemoryUsage = process.WorkingSet64 / 1024; updatedProcessList.Add(processDictionary[process.Id]); // Check if the process status has changed if (processDictionary[process.Id].Status != "Killed" && processDictionary[process.Id].Status != "Opened") { processDictionary[process.Id].Status = "Opened"; } } } // Update the ListView with the latest process information Dispatcher.BeginInvoke(new Action(delegate () { // Replace the entire data source with the updated list lstvw.ItemsSource = updatedProcessList; // Set the selected item to the previous selection if it still exists if (SelectedProcess != null && updatedProcessList.IndexOf(SelectedProcess) >= 0) { lstvw.SelectedItem = SelectedProcess; } })); } private void showInformation(int numberToGet, ListView listvw) { List<ProcessesInfo> information = getInfo(numberToGet); Dispatcher.BeginInvoke(new Action(delegate () { foreach (var processInfo in information) { // Set the ProcessDateTime property for each process processInfo.ProcessDateTime = DateTime.Now; } lstvw.ItemsSource = information; })); } private List<ProcessesInfo> getInfo(int numberToGet) { List<ProcessesInfo> info = new List<ProcessesInfo>(); for (int i = 0; i < numberToGet; i++) { info.Add(new ProcessesInfo() { Name = processes[i].ProcessName, Status = "Opened", Id = processes[i].Id, ProcessDateTime = DateTime.Now // Initialize ProcessDateTime here }); } return info; } private void expandButton_Click(object sender, RoutedEventArgs e) { // Handle sub-process expand button click here ToggleButton button = (ToggleButton)sender; ListViewItem listViewItem = FindVisualParent<ListViewItem>(button); ProcessesInfo selectedProcess = (ProcessesInfo)listViewItem.DataContext; if (button.IsChecked == true) { // Sub-processes are expanded foreach (var subProcess in selectedProcess.SubProcesses) { processDictionary.Add(subProcess.Id, subProcess); } } else { // Sub-processes are collapsed foreach (var subProcess in selectedProcess.SubProcesses) { processDictionary.Remove(subProcess.Id); } } UpdateProcessList(); } private static T FindVisualParent<T>(UIElement element) where T : UIElement { UIElement parent = element; while (parent != null) { if (parent is T correctlyTyped) { return correctlyTyped; } parent = VisualTreeHelper.GetParent(parent) as UIElement; } return null; } } }
问题原因
- 对象生命周期不一致:首次启动时通过
showInformation和getInfo创建的ProcessesInfo对象仅用于初始显示,并未加入processDictionary;1秒后Timer触发更新时,会从processDictionary中读取或创建新对象,这些对象的ProcessDateTime未被赋值,而DateTime类型的默认值就是0001-01-01 00:00:00。 - 更新逻辑缺失赋值:在
UpdateProcessList方法中,无论是处理已杀死进程、新进程还是已有进程的逻辑,都没有为ProcessDateTime属性赋值,导致显示默认值。
修复方案
方案1:保留进程首次被监控的时间
修改两处逻辑,确保对象创建时就赋值ProcessDateTime,并将初始对象加入字典:
- 在
UpdateProcessList的新进程创建逻辑中添加ProcessDateTime赋值:
if (!processDictionary.ContainsKey(process.Id)) { processDictionary.Add(process.Id, new ProcessesInfo { Name = process.ProcessName, Id = process.Id, Status = "Opened", MemoryUsage = process.WorkingSet64 / 1024, ProcessDateTime = DateTime.Now // 新增:赋值首次监控时间 }); updatedProcessList.Add(processDictionary[process.Id]); }
- 修改
showInformation方法,将初始对象加入字典:
private void showInformation(int numberToGet, ListView listvw) { List<ProcessesInfo> information = getInfo(numberToGet); Dispatcher.BeginInvoke(new Action(delegate () { foreach (var processInfo in information) { if (!processDictionary.ContainsKey(processInfo.Id)) { processDictionary.Add(processInfo.Id, processInfo); } } lstvw.ItemsSource = information; })); }
方案2:实时更新显示当前时间
如果需要每次刷新都显示最新时间,在UpdateProcessList中给所有加入updatedProcessList的对象赋值ProcessDateTime:
// 处理已杀死进程的循环中 updatedProcess.ProcessDateTime = DateTime.Now; updatedProcessList.Add(updatedProcess); // 处理已有进程的逻辑中 processDictionary[process.Id].ProcessDateTime = DateTime.Now; updatedProcessList.Add(processDictionary[process.Id]);
可选优化:实现INotifyPropertyChanged
为ProcessesInfo实现INotifyPropertyChanged接口,让UI能自动响应属性变化,无需每次替换整个ItemsSource:
using System.ComponentModel; using System.Runtime.CompilerServices; public class ProcessesInfo : INotifyPropertyChanged { private string _name; private string _status; private int _id; private long _memoryUsage; private DateTime _processDateTime; private List<ProcessesInfo> _subProcesses = new List<ProcessesInfo>(); public string Name { get => _name; set { _name = value; OnPropertyChanged(); } } public string Status { get => _status; set { _status = value; OnPropertyChanged(); } } public int Id { get => _id; set { _id = value; OnPropertyChanged(); } } public long MemoryUsage { get => _memoryUsage; set { _memoryUsage = value; OnPropertyChanged(); } } public List<ProcessesInfo> SubProcesses { get => _subProcesses; set { _subProcesses = value; OnPropertyChanged(); } } public DateTime ProcessDateTime { get => _processDateTime; set { _processDateTime = value; OnPropertyChanged(); } } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
内容的提问来源于stack exchange,提问作者Daniel Lip
相关产品推荐
相关产品推荐

