基本上,我想计算外部进程的运行持续时间不是由我的代码触发的。为了实现这一点,我订阅了特定进程的开始和结束事件,进程名称,使用以下代码(基于此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
或类似的东西有关;该问题可能与此有关吗?
如果这是一个我无法帮助的问题(例如,如果它与操作系统触发消息的方式/时间有关),有没有办法可以过滤掉这些明显无效的数据点?
答案 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;
}
}