Introduction
I so often have to monitor what's happening in a background process and got tired of re-writing the code for this in so many ways. So I figured I'd share what I have with you, especially in the light of the fact that I've gotten so many helpful code snippets from you all. This is the first article I've submitted and have had a little trouble with the source formateeing ;). The source itself is formatted fine.
Background
Basically, the High Resolution Timing function is from Microsoft at: http://msdn.microsoft.com/en-us/library/aa964692(v=vs.85).aspx
That example was for Windows CE, so I changed the DllIMports to "kernel32.dll" instead of "coredll.dll" as in their example, and added various cycle timing/display methods.
Then I Inherited it into a very basic EventArg class such as is used with the BackgroundWorker class. So the timing details of my background processes would always be easily available as these args get passed around from EventHandler to EventHandler with slight variations.
Using the code
Except for the HiResTimer class, I've tried to keep it as simple as possible. The EventArg class that inherits it is very basic, but you could add what you want to it for specific purposes. The idea behind the HiResTimer class was to write it once and have high resolution timing details easily available and somewhat transparent to the actual work being done.
Outside of using the HiResTimer as a base for an EventArgs class, using the class in a stand-alone way is illustrated below:
private void button1_Click(object sender, EventArgs e)
{
Utility.HiResTimer hiResTimer = new Utility.HiResTimer();
Int64 totalCount = 1000; while (hiResTimer.CycleCount != totalCount - 1)
{
Thread.Sleep(100);
hiResTimer.UpdateCycleStats(); toolStripStatusLabel1.Text = " Run Time: " + hiResTimer.RunningTime
+ " Avg Time: " + hiResTimer.CycleAvgTime
+ " Cycle Time: " + hiResTimer.CycleTime;
toolStripProgressBar1.Value = hiResTimer.CyclePercentage(totalCount);
statusStrip1.Refresh();
}
toolStripStatusLabel1.Text = " Run Time: " + hiResTimer.RunningTime + " Avg Time: " + hiResTimer.CycleAvgTime + " Total Time: " + hiResTimer.TotalTime; }
The EventArgs class simply inherits this same functionality:
public class HiResEventArgs : HiResTimer
{
#region "Members"
private object argument = null;
public object RealResult = null;
public object RealUserState = null;
#endregion
#region "Construction"
public HiResEventArgs(object argument)
: base() {
this.argument = argument; }
public static HiResEventArgs CopyOfHiResEventArgs(HiResEventArgs hiResEventArgs)
{
HiResEventArgs returnCopy = new HiResEventArgs(hiResEventArgs.argument);
returnCopy.RealUserState = hiResEventArgs.RealUserState;
returnCopy.RealResult = hiResEventArgs.RealResult;
returnCopy.CopyHiResTimer(hiResEventArgs);
return returnCopy;
}
#endregion
#region "Properties"
public object Argument
{
get { return argument; }
}
#endregion
}
So, for example, the above stand-alone code moved over to a DoWork function of a BackgroundWorker class would be very similar, except it would be using a HiResEventArgs variable as the argument sent to it. The button1_Click handler would create the HiResEventArgs object and send it to the DoWork function as the arguument/paramater to RunWorkerAsync.
Utility.HiResEventArgs hiResArgs = new Utility.HiResEventArgs(100); backgroundWorker1.RunWorkerAsync(hiResArgs);
The DoWork function would pick this up and use it pretty much the same as the stand-alone example, except it wouldn't display anything:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bw = sender as BackgroundWorker;
Utility.HiResEventArgs hiResArgs = e.Argument as Utility.HiResEventArgs;
Int64 totalCount = 0; Int64.TryParse((hiResArgs.Argument != null ? hiResArgs.Argument.ToString() : "100"), out totalCount);
while (hiResArgs.CycleCount < totalCount && !bw.CancellationPending)
{
Thread.Sleep(100);
hiResArgs.UpdateCycleStats(); hiResArgs.RealUserState = null; hiResArgs.RealResult = null; bw.ReportProgress(hiResArgs.CyclePercentage(totalCount), Utility.HiResEventArgs.CopyOfHiResEventArgs(hiResArgs)); }
e.Result = Utility.HiResEventArgs.CopyOfHiResEventArgs(hiResArgs); if (bw.CancellationPending)
e.Cancel = true;
}
The ProgressChanged function takes over the display of the progress that was previously
handled directly in the stand-alone example:
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (((BackgroundWorker)sender).CancellationPending)
return;
if (e.UserState != null && e.UserState.GetType().ToString().EndsWith("HiResEventArgs"))
{
Utility.HiResEventArgs hiResArgs = e.UserState as Utility.HiResEventArgs;
toolStripStatusLabel1.Text = hiResArgs.CyclePercentage().ToString() + "% Done... "
+ " Run Time: " + hiResArgs.RunningTime
+ " Avg Time: " + hiResArgs.CycleAvgTime
+ " Cycle Time: " + hiResArgs.CycleTime;
toolStripProgressBar1.Value = hiResArgs.CyclePercentage();
}
else
toolStripProgressBar1.Value = e.ProgressPercentage;
statusStrip1.Refresh();
}
And the final results with TotalTime would be handed in the RunWorkerCompleted Event Handler:
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled == false && e.Error == null
&& e.Result != null && e.Result.GetType().ToString().EndsWith("HiResEventArgs"))
{
Utility.HiResEventArgs hiResArgs = e.Result as Utility.HiResEventArgs;
toolStripStatusLabel1.Text = hiResArgs.CyclePercentage().ToString() + "% Completed "
+ " Run Time: " + hiResArgs.RunningTime + " Avg Time: " + hiResArgs.CycleAvgTime + " Total Time: " + hiResArgs.TotalTime; }
button1.Text = "Start";
}
Sean O'Leary