You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,并将初始对象加入字典:

  1. 在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]);
}
  1. 修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 22:05:05