为什么我在流程的运行持续时间内获得INSANELY不正确的值?

时间:2017-01-04 21:08:38

标签: c# validation process filtering

基本上,我想计算外部进程的运行持续时间不是由我的代码触发的。为了实现这一点,我订阅了特定进程的开始和结束事件,进程名称,使用以下代码(基于此answer):

private ManagementEventWatcher ProcessStartWatcher(string processName)
{
    string queryString =
        "SELECT TargetInstance" +
        "  FROM __InstanceCreationEvent " +
        "WITHIN  1 " +
        " WHERE TargetInstance ISA 'Win32_Process' " +
        "   AND TargetInstance.Name = '" + processName + "'";

    // The dot in the scope means use the current machine
    string scope = @"\\.\root\CIMV2";

    // Create a watcher and listen for events
    ManagementEventWatcher watcher = new ManagementEventWatcher(scope, queryString);
    watcher.EventArrived += ProcessStarted;
    watcher.Start();
    return watcher;
}

private ManagementEventWatcher ProcessEndWatcher(string processName)
{
    string queryString =
        "SELECT TargetInstance" +
        "  FROM __InstanceDeletionEvent " +
        "WITHIN  1 " +
        " WHERE TargetInstance ISA 'Win32_Process' " +
        "   AND TargetInstance.Name = '" + processName + "'";

    // The dot in the scope means use the current machine
    string scope = @"\\.\root\CIMV2";

    // Create a watcher and listen for events
    ManagementEventWatcher watcher = new ManagementEventWatcher(scope, queryString);
    watcher.EventArrived += ProcessEnded;
    watcher.Start();
    return watcher;
}

private void ProcessStarted(object sender, EventArrivedEventArgs e)
{
    this.processStart = DateTime.Now;
}

private void ProcessEnded(object sender, EventArrivedEventArgs e)
{
    // use time recorded from ProcessStarted to calculate run duration, write to log file
    this.logger.addRuntimeData((DateTime.Now - this.processStart).TotalSeconds);
}

addRuntimeData(double seconds)方法定义为:

public void addRuntimeData(double seconds)
{
    this.runDurations.Add(seconds);
    if (this.runDurations.Count > Properties.Settings.Default.MaxRunDurationData)
        this.runDurations.RemoveAt(0);
    this.updateLog();
}

public void updateLog()
{
    this.logfileDirectory = Properties.Settings.Default.LogfileDirectory;
    this.logfileFullPath = logfileDirectory + this.task.Name.toValidFilename() + this.logfileExtension;
    Directory.CreateDirectory(logfileDirectory); // create directory if it does not already exist
    this.toXElement().Save(this.logfileFullPath); // generate the XML and write it to the log file
}

现在,我只是试图计算我编写的测试过程,并且所有测试过程都是对Console.WriteLine("Test process");的单次调用,因此记录持续时间的适当值应大致在0-范围内2秒。

有时,我得到合适的值,有时我会得到几乎无法记录的值,例如63619141321.2978秒。这相当于大约2017。3年,这让我觉得它可能与开始时间被记录为01/01/0001 00:00:01或类似的东西有关;该问题可能与此有关吗?

如果这是一个我无法帮助的问题(例如,如果它与操作系统触发消息的方式/时间有关),有没有办法可以过滤掉这些明显无效的数据点?

2 个答案:

答案 0 :(得分:3)

你对01/01/0001 00:00:01基本上是正确的。

创建DateTime对象在1/1/0001 12:00:00 AM启动。您评论说processStart是在没有初始化的情况下声明的,这意味着它的默认开始时间为1/1/0001 12:00:00 AM。我猜测ProcessEnd在被解雇之前被解雇了。

下面的代码会产生与您描述的类似的结果,并使用在声明后未更改的DateTime。

private static DateTime date;

public static void Main(string[] args) {
    const double largeVal = 63619141321;

    Console.WriteLine(date.ToString());

    double totalSeconds = (DateTime.Now - date).TotalSeconds;
    Console.WriteLine(totalSeconds - largeVal);

    Console.WriteLine("Press any key to continue . . . ");
    Console.ReadKey(true);
}

答案 1 :(得分:3)

潜在的问题是,ManagementEventWatcher基本上是一种轮询机制,并且在非常短的进程的启动事件之前可能会收到结束事件。如果从未初始化开始时间(默认值为1/1/0001),那么您将看到您所描述的内容。实际上,这可能不是一个现实的用例,但它可能会发生,正如您所观察到的那样。我认为解决问题的最佳方法是不要记录过程开始事件的开始时间。

您真的不需要查看流程启动事件来计算总运行时间。您可以从结束事件中获取Win32_Process实例,并使用CreationDate计算进程的总运行时间。我注意到TerminationDate还没有确定。在这种情况下,我只使用结束事件被触发的当前时间:

private static void ProcessEnded(object sender, EventArrivedEventArgs e)
{
    Console.WriteLine($"Process ended event at: {DateTime.Now}");
    var targetProcess = e.NewEvent.Properties["TargetInstance"].Value as ManagementBaseObject;
    if (targetProcess != null)
    {
        Console.WriteLine("Properties:");
        foreach (PropertyData data in targetProcess.Properties)
        {
            Console.WriteLine($"{data.Name} = {data.Value}");
        }

        DateTime creationDate = GetDateTimeOrDefault(targetProcess.Properties["CreationDate"], DateTime.Now);
        DateTime terminationDate = GetDateTimeOrDefault(targetProcess.Properties["TerminationDate"], DateTime.Now);

        var totalRunTime = (terminationDate - creationDate).TotalSeconds;
        Console.WriteLine($"Creation: {creationDate}, Termination: {terminationDate}, Elapsed: {totalRunTime}");
        // this.logger.addRuntimeData(totalRunTime);
    }
    else
    {
        Console.WriteLine("Could not get target process");
    }
}

private static DateTime GetDateTimeOrDefault(PropertyData managementDateProperty, DateTime defaultValue)
{
    string dateString = managementDateProperty.Value as string;
    if (!string.IsNullOrEmpty(dateString))
    {
        return ManagementDateTimeConverter.ToDateTime(dateString);
    }
    else
    {
        return defaultValue;
    }
}