2016-03-30 14:20:52 -07:00
/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
using System.Runtime.Serialization ;
2016-08-02 10:08:57 -07:00
using Dbg = System . Management . Automation . Diagnostics ;
2016-03-30 14:20:52 -07:00
namespace System.Management.Automation
{
/// <summary>
2017-01-16 13:31:14 -08:00
///
/// Defines a data structure used to represent the status of an ongoing operation at a point in time.
///
2016-03-30 14:20:52 -07:00
/// </summary>
/// <remarks>
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// ProgressRecords are passed to <see cref="System.Management.Automation.Cmdlet.WriteProgress(ProgressRecord)"/>,
/// which, according to user preference, forwards that information on to the host for rendering to the user.
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// </remarks>
/// <seealso cref="System.Management.Automation.Cmdlet.WriteProgress(ProgressRecord)"/>
[DataContract()]
2016-07-29 13:02:49 -07:00
public
2016-03-30 14:20:52 -07:00
class ProgressRecord
{
#region Public API
/// <summary>
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// Initializes a new instance of the ProgressRecord class and defines the activity Id,
/// activity description, and status description.
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// </summary>
/// <param name="activityId">
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// A unique numeric key that identifies the activity to which this record applies.
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// </param>
/// <param name="activity">
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// A description of the activity for which progress is being reported.
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// </param>
/// <param name="statusDescription">
///
/// A description of the status of the activity.
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// </param>
public
ProgressRecord ( int activityId , string activity , string statusDescription )
{
if ( activityId < 0 )
{
// negative Ids are reserved to indicate "no id" for parent Ids.
throw PSTraceSource . NewArgumentOutOfRangeException ( "activityId" , activityId , ProgressRecordStrings . ArgMayNotBeNegative , "activityId" );
}
if ( String . IsNullOrEmpty ( activity ))
{
throw PSTraceSource . NewArgumentException ( "activity" , ProgressRecordStrings . ArgMayNotBeNullOrEmpty , "activity" );
}
if ( String . IsNullOrEmpty ( statusDescription ))
{
throw PSTraceSource . NewArgumentException ( "activity" , ProgressRecordStrings . ArgMayNotBeNullOrEmpty , "statusDescription" );
}
2016-07-29 13:02:49 -07:00
_id = activityId ;
_activity = activity ;
_status = statusDescription ;
2016-03-30 14:20:52 -07:00
}
/// <summary>
2017-01-16 13:31:14 -08:00
/// Cloning constructor (all fields are value types - can treat our implementation of cloning as "deep" copy)
2016-03-30 14:20:52 -07:00
/// </summary>
/// <param name="other"></param>
internal ProgressRecord ( ProgressRecord other )
{
2016-07-29 13:02:49 -07:00
_activity = other . _activity ;
_currentOperation = other . _currentOperation ;
_id = other . _id ;
_parentId = other . _parentId ;
_percent = other . _percent ;
_secondsRemaining = other . _secondsRemaining ;
_status = other . _status ;
_type = other . _type ;
2016-03-30 14:20:52 -07:00
}
/// <summary>
/// Added to enable ClrFacade.GetUninitializedObject to instantiate an uninitialized version of this class.
/// </summary>
internal ProgressRecord ()
{ }
/// <summary>
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// Gets the Id of the activity to which this record corresponds. Used as a 'key' for the
/// linking of subordinate activities.
///
/// </summary>
public
int
ActivityId
{
get
{
2016-07-29 13:02:49 -07:00
return _id ;
2016-03-30 14:20:52 -07:00
}
}
/// <summary>
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// Gets and sets the Id of the activity for which this record is a subordinate.
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// </summary>
/// <remarks>
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// Used to allow chaining of progress records (such as when one installation invokes a child installation). UI:
/// normally not directly visible except as already displayed as its own activity. Usually a sub-activity will be
/// positioned below and to the right of its parent.
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// A negative value (the default) indicates that the activity is not a subordinate.
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// May not be the same as ActivityId.
2017-01-16 13:31:14 -08:00
///
/// <!--NTRAID#Windows OS Bugs-1161549 the default value for this should be picked up from a variable in the
/// shell so that a script can set that variable, and have all subsequent calls to WriteProgress (the API) be
2016-03-30 14:20:52 -07:00
/// subordinate to the "current parent id".-->
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// </remarks>
2016-07-29 13:02:49 -07:00
public
int
2016-03-30 14:20:52 -07:00
ParentActivityId
{
get
{
2016-07-29 13:02:49 -07:00
return _parentId ;
2016-03-30 14:20:52 -07:00
}
set
{
if ( value == ActivityId )
{
throw PSTraceSource . NewArgumentException ( "value" , ProgressRecordStrings . ParentActivityIdCantBeActivityId );
}
2016-07-29 13:02:49 -07:00
_parentId = value ;
2016-03-30 14:20:52 -07:00
}
}
/// <summary>
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// Gets and sets the description of the activity for which progress is being reported.
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// </summary>
/// <remarks>
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// States the overall intent of whats being accomplished, such as "Recursively removing item c:\temp." Typically
/// displayed in conjunction with a progress bar.
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// </remarks>
2016-07-29 13:02:49 -07:00
public
string
2016-03-30 14:20:52 -07:00
Activity
{
get
{
2016-07-29 13:02:49 -07:00
return _activity ;
2016-03-30 14:20:52 -07:00
}
set
{
if ( String . IsNullOrEmpty ( value ))
{
throw PSTraceSource . NewArgumentException ( "value" , ProgressRecordStrings . ArgMayNotBeNullOrEmpty , "value" );
}
2016-07-29 13:02:49 -07:00
_activity = value ;
2016-03-30 14:20:52 -07:00
}
}
2016-07-29 13:02:49 -07:00
2016-03-30 14:20:52 -07:00
/// <summary>
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// Gets and sets the current status of the operation, e.g., "35 of 50 items Copied." or "95% completed." or "100 files purged."
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// </summary>
2016-07-29 13:02:49 -07:00
public
string
2016-03-30 14:20:52 -07:00
StatusDescription
{
get
{
2016-07-29 13:02:49 -07:00
return _status ;
2016-03-30 14:20:52 -07:00
}
set
{
if ( String . IsNullOrEmpty ( value ))
{
throw PSTraceSource . NewArgumentException ( "value" , ProgressRecordStrings . ArgMayNotBeNullOrEmpty , "value" );
}
2016-07-29 13:02:49 -07:00
_status = value ;
2016-03-30 14:20:52 -07:00
}
}
/// <summary>
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// Gets and sets the current operation of the many required to accomplish the activity (such as "copying foo.txt"). Normally displayed
2017-01-16 13:31:14 -08:00
/// below its associated progress bar, e.g., "deleting file foo.bar"
2016-03-30 14:20:52 -07:00
/// Set to null or empty in the case a sub-activity will be used to show the current operation.
///
/// </summary>
public
2016-07-29 13:02:49 -07:00
string
2016-03-30 14:20:52 -07:00
CurrentOperation
{
get
{
2016-07-29 13:02:49 -07:00
return _currentOperation ;
2016-03-30 14:20:52 -07:00
}
set
{
// null or empty string is allowed
2016-07-29 13:02:49 -07:00
_currentOperation = value ;
2016-03-30 14:20:52 -07:00
}
}
2016-07-29 13:02:49 -07:00
2016-03-30 14:20:52 -07:00
/// <summary>
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// Gets and sets the estimate of the percentage of total work for the activity that is completed. Typically displayed as a progress bar.
/// Set to a negative value to indicate that the percentage completed should not be displayed.
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// </summary>
public
int
PercentComplete
{
get
{
2016-07-29 13:02:49 -07:00
return _percent ;
2016-03-30 14:20:52 -07:00
}
set
{
// negative values are allowed
2016-07-29 13:02:49 -07:00
2016-03-30 14:20:52 -07:00
if ( value > 100 )
{
2016-07-29 13:02:49 -07:00
throw
2016-03-30 14:20:52 -07:00
PSTraceSource . NewArgumentOutOfRangeException (
"value" , value , ProgressRecordStrings . PercentMayNotBeMoreThan100 , "PercentComplete" );
}
2016-07-29 13:02:49 -07:00
_percent = value ;
2016-03-30 14:20:52 -07:00
}
}
/// <summary>
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// Gets and sets the estimate of time remaining until this activity is completed. This can be based upon a measurement of time since
/// started and the percent complete or another approach deemed appropriate by the caller.
///
/// Normally displayed beside the progress bar, as "N seconds remaining."
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// </summary>
///<remarks>
///
/// A value less than 0 means "don't display a time remaining."
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// </remarks>
public
int
SecondsRemaining
{
get
{
2016-07-29 13:02:49 -07:00
return _secondsRemaining ;
2016-03-30 14:20:52 -07:00
}
set
{
// negative values are allowed
2016-07-29 13:02:49 -07:00
_secondsRemaining = value ;
2016-03-30 14:20:52 -07:00
}
}
/// <summary>
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// Gets and sets the type of record represented by this instance.
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// </summary>
2016-07-29 13:02:49 -07:00
2016-03-30 14:20:52 -07:00
public
ProgressRecordType
RecordType
{
get
{
2016-07-29 13:02:49 -07:00
return _type ;
2016-03-30 14:20:52 -07:00
}
set
{
if ( value != ProgressRecordType . Completed && value != ProgressRecordType . Processing )
{
throw PSTraceSource . NewArgumentException ( "value" );
}
2016-07-29 13:02:49 -07:00
_type = value ;
2016-03-30 14:20:52 -07:00
}
}
/// <summary>
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// Overrides <see cref="System.Object.ToString"/>
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// </summary>
/// <returns>
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// "parent = a id = b act = c stat = d cur = e pct = f sec = g type = h" where
/// a, b, c, d, e, f, and g are the values of ParentActivityId, ActivityId, Activity, StatusDescription,
/// CurrentOperation, PercentComplete, SecondsRemaining and RecordType properties.
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// </returns>
2016-07-29 13:02:49 -07:00
public override
string
2016-03-30 14:20:52 -07:00
ToString ()
{
2016-07-29 13:02:49 -07:00
return
2016-03-30 14:20:52 -07:00
String . Format (
System . Globalization . CultureInfo . CurrentCulture ,
"parent = {0} id = {1} act = {2} stat = {3} cur = {4} pct = {5} sec = {6} type = {7}" ,
2016-07-29 13:02:49 -07:00
_parentId ,
_id ,
_activity ,
_status ,
_currentOperation ,
_percent ,
_secondsRemaining ,
_type );
2016-03-30 14:20:52 -07:00
}
#endregion
#region Helper methods
2016-08-02 19:00:50 -07:00
internal static int? GetSecondsRemaining ( DateTime startTime , double percentageComplete )
2016-03-30 14:20:52 -07:00
{
Dbg . Assert ( percentageComplete >= 0.0 , "Caller should verify percentageComplete >= 0.0" );
Dbg . Assert ( percentageComplete <= 1.0 , "Caller should verify percentageComplete <= 1.0" );
Dbg . Assert (
2016-07-29 13:02:49 -07:00
startTime . Kind == DateTimeKind . Utc ,
2016-03-30 14:20:52 -07:00
"DateTime arithmetic should always be done in utc mode [to avoid problems when some operands are calculated right before and right after switching to /from a daylight saving time" );
if (( percentageComplete < 0.00001 ) || double . IsNaN ( percentageComplete ))
{
return null ;
}
DateTime now = DateTime . UtcNow ;
Dbg . Assert ( startTime <= now , "Caller should pass a valid startTime" );
TimeSpan elapsedTime = now - startTime ;
TimeSpan totalTime ;
try
{
totalTime = TimeSpan . FromMilliseconds ( elapsedTime . TotalMilliseconds / percentageComplete );
}
catch ( OverflowException )
{
return null ;
}
catch ( ArgumentException )
{
return null ;
}
TimeSpan remainingTime = totalTime - elapsedTime ;
2016-07-29 13:02:49 -07:00
return ( int )( remainingTime . TotalSeconds );
2016-03-30 14:20:52 -07:00
}
/// <summary>
/// Returns percentage complete when it is impossible to predict how long an operation might take.
/// The percentage complete will slowly converge toward 100%.
/// At the <paramref name="expectedDuration"/> the percentage complete will be 90%.
/// </summary>
/// <param name="startTime">When did the operation start</param>
/// <param name="expectedDuration">How long does the operation usually take</param>
/// <returns>Estimated percentage complete of the operation (always between 0 and 99% - never returns 100%)</returns>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when
/// 1) <paramref name="startTime"/> is in the future
/// 2) <paramref name="expectedDuration"/> is negative or zero
/// </exception>
2016-08-02 19:00:50 -07:00
internal static int GetPercentageComplete ( DateTime startTime , TimeSpan expectedDuration )
2016-03-30 14:20:52 -07:00
{
DateTime now = DateTime . UtcNow ;
Dbg . Assert (
2016-07-29 13:02:49 -07:00
startTime . Kind == DateTimeKind . Utc ,
2016-03-30 14:20:52 -07:00
"DateTime arithmetic should always be done in utc mode [to avoid problems when some operands are calculated right before and right after switching to /from a daylight saving time" );
if ( startTime > now )
{
throw new ArgumentOutOfRangeException ( "startTime" );
}
if ( expectedDuration <= TimeSpan . Zero )
{
throw new ArgumentOutOfRangeException ( "expectedDuration" );
}
/*
2017-01-16 13:31:14 -08:00
* According to the spec of Checkpoint-Computer
2016-03-30 14:20:52 -07:00
* (http://cmdletdesigner/SpecViewer/Default.aspx?Project=PowerShell&Cmdlet=Checkpoint-Computer)
* we have percentage remaining = f(t) where
* f(inf) = 0%
* f(0) = 100%
* f(90) = <something small> = 10%
2017-01-16 13:31:14 -08:00
*
2016-03-30 14:20:52 -07:00
* The spec talks about exponential decay, but function based on 1/x seems better:
2017-01-16 13:31:14 -08:00
* f(t) = a / (T + b)
*
2016-03-30 14:20:52 -07:00
* This by definition has f(inf) = 0, so we have to find a and b for the last 2 cases:
2017-01-16 13:31:14 -08:00
* E1: f(0) = a / (0 + b) = 100
2016-03-30 14:20:52 -07:00
* E2: f(T = 90) = a / (T + b) = 10
2017-01-16 13:31:14 -08:00
*
2016-03-30 14:20:52 -07:00
* From E1 we have a = 100 * b, which we can use in E2:
* (100 * b) / (T + b) = 10
* 100 * b = 10 * T + 10 * b
* 90 * b = 10 * T
* b = T / 9
2017-01-16 13:31:14 -08:00
*
2016-03-30 14:20:52 -07:00
* Some sample values (for T=90):
2017-01-16 13:31:14 -08:00
* t | %rem
2016-03-30 14:20:52 -07:00
* -----------
* 0 | 100.0%
* 5 | 66.6%
* 10 | 50.0%
* 30 | 25.0%
* 70 | 12.5%
* 90 | 10.0%
* 300 | 3.2%
* 600 | 1.6%
* 3600| 0.2%
*/
TimeSpan timeElapsed = now - startTime ;
double b = expectedDuration . TotalSeconds / 9.0 ;
double a = 100.0 * b ;
double percentageRemaining = a / ( timeElapsed . TotalSeconds + b );
double percentageCompleted = 100.0 - percentageRemaining ;
return ( int ) Math . Floor ( percentageCompleted );
}
#endregion
[DataMemberAttribute()]
2016-07-29 13:02:49 -07:00
private int _id ;
2016-03-30 14:20:52 -07:00
[DataMemberAttribute()]
2016-07-29 13:02:49 -07:00
private int _parentId = - 1 ;
2016-03-30 14:20:52 -07:00
[DataMemberAttribute()]
2016-07-29 13:02:49 -07:00
private string _activity ;
2016-03-30 14:20:52 -07:00
[DataMemberAttribute()]
2016-07-29 13:02:49 -07:00
private string _status ;
2016-03-30 14:20:52 -07:00
[DataMemberAttribute()]
2016-07-29 13:02:49 -07:00
private string _currentOperation ;
2016-03-30 14:20:52 -07:00
[DataMemberAttribute()]
2016-07-29 13:02:49 -07:00
private int _percent = - 1 ;
2016-03-30 14:20:52 -07:00
[DataMemberAttribute()]
2016-07-29 13:02:49 -07:00
private int _secondsRemaining = - 1 ;
2016-03-30 14:20:52 -07:00
[DataMemberAttribute()]
2016-07-29 13:02:49 -07:00
private ProgressRecordType _type = ProgressRecordType . Processing ;
2016-03-30 14:20:52 -07:00
#region Serialization / deserialization for remoting
/// <summary>
2017-01-16 13:31:14 -08:00
/// Creates a ProgressRecord object from a PSObject property bag.
2016-03-30 14:20:52 -07:00
/// PSObject has to be in the format returned by ToPSObjectForRemoting method.
/// </summary>
/// <param name="progressAsPSObject">PSObject to rehydrate</param>
/// <returns>
/// ProgressRecord rehydrated from a PSObject property bag
2017-01-16 13:31:14 -08:00
/// </returns>
2016-03-30 14:20:52 -07:00
/// <exception cref="ArgumentNullException">
/// Thrown if the PSObject is null.
/// </exception>
/// <exception cref="System.Management.Automation.Remoting.PSRemotingDataStructureException">
/// Thrown when the PSObject is not in the expected format
/// </exception>
2016-08-02 19:00:50 -07:00
internal static ProgressRecord FromPSObjectForRemoting ( PSObject progressAsPSObject )
2016-03-30 14:20:52 -07:00
{
if ( progressAsPSObject == null )
{
throw PSTraceSource . NewArgumentNullException ( "progressAsPSObject" );
}
string activity = RemotingDecoder . GetPropertyValue < string >( progressAsPSObject , RemoteDataNameStrings . ProgressRecord_Activity );
int activityId = RemotingDecoder . GetPropertyValue < int >( progressAsPSObject , RemoteDataNameStrings . ProgressRecord_ActivityId );
string statusDescription = RemotingDecoder . GetPropertyValue < string >( progressAsPSObject , RemoteDataNameStrings . ProgressRecord_StatusDescription );
ProgressRecord result = new ProgressRecord ( activityId , activity , statusDescription );
result . CurrentOperation = RemotingDecoder . GetPropertyValue < string >( progressAsPSObject , RemoteDataNameStrings . ProgressRecord_CurrentOperation );
result . ParentActivityId = RemotingDecoder . GetPropertyValue < int >( progressAsPSObject , RemoteDataNameStrings . ProgressRecord_ParentActivityId );
result . PercentComplete = RemotingDecoder . GetPropertyValue < int >( progressAsPSObject , RemoteDataNameStrings . ProgressRecord_PercentComplete );
result . RecordType = RemotingDecoder . GetPropertyValue < ProgressRecordType >( progressAsPSObject , RemoteDataNameStrings . ProgressRecord_Type );
result . SecondsRemaining = RemotingDecoder . GetPropertyValue < int >( progressAsPSObject , RemoteDataNameStrings . ProgressRecord_SecondsRemaining );
return result ;
}
/// <summary>
/// Returns this object as a PSObject property bag
/// that can be used in a remoting protocol data object.
/// </summary>
/// <returns>This object as a PSObject property bag</returns>
internal PSObject ToPSObjectForRemoting ()
{
PSObject progressAsPSObject = RemotingEncoder . CreateEmptyPSObject ();
progressAsPSObject . Properties . Add ( new PSNoteProperty ( RemoteDataNameStrings . ProgressRecord_Activity , this . Activity ));
progressAsPSObject . Properties . Add ( new PSNoteProperty ( RemoteDataNameStrings . ProgressRecord_ActivityId , this . ActivityId ));
2016-07-29 13:02:49 -07:00
progressAsPSObject . Properties . Add ( new PSNoteProperty ( RemoteDataNameStrings . ProgressRecord_StatusDescription , this . StatusDescription ));
2016-03-30 14:20:52 -07:00
progressAsPSObject . Properties . Add ( new PSNoteProperty ( RemoteDataNameStrings . ProgressRecord_CurrentOperation , this . CurrentOperation ));
progressAsPSObject . Properties . Add ( new PSNoteProperty ( RemoteDataNameStrings . ProgressRecord_ParentActivityId , this . ParentActivityId ));
progressAsPSObject . Properties . Add ( new PSNoteProperty ( RemoteDataNameStrings . ProgressRecord_PercentComplete , this . PercentComplete ));
progressAsPSObject . Properties . Add ( new PSNoteProperty ( RemoteDataNameStrings . ProgressRecord_Type , this . RecordType ));
progressAsPSObject . Properties . Add ( new PSNoteProperty ( RemoteDataNameStrings . ProgressRecord_SecondsRemaining , this . SecondsRemaining ));
return progressAsPSObject ;
}
#endregion
} //ProgressRecord
2016-07-29 13:02:49 -07:00
2016-03-30 14:20:52 -07:00
/// <summary>
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// Defines two types of progress record that refer to the beginning (or middle) and end of an operation.
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// </summary>
public
enum ProgressRecordType
{
///<summary>
/// Operation just started or is not yet complete
/// </summary>
/// <remarks>
/// A cmdlet can call WriteProgress with ProgressRecordType.Processing
/// as many times as it wishes. However, at the end of the operation,
/// it should call once more with ProgressRecordType.Completed.
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// The first time that a host receives a progress record
/// for a given activity, it will typically display a progress
/// indicator for that activity. For each subsequent record
/// of the same Id, the host will update that display.
/// Finally, when the host receives a 'completed' record
/// for that activity, it will remove the progress indicator.
/// </remarks>
Processing ,
/// <summary>
/// Operation is complete
/// </summary>
/// <remarks>
/// If a cmdlet uses WriteProgress, it should use
/// ProgressRecordType.Completed exactly once, in the last call
/// to WriteProgress.
/// </remarks>
Completed
}
}