/********************************************************************++ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ #pragma warning disable 1634, 1691 #pragma warning disable 56506 using System.Collections; using System.Collections.ObjectModel; using System.Globalization; using System.Management.Automation.Runspaces; using System.Text; using System.Resources; using System.Runtime.Serialization; using System.Reflection; using Dbg = System.Management.Automation.Diagnostics; using System.Management.Automation.Language; #if CORECLR // Use stubs for SerializableAttribute, SecurityPermissionAttribute and ISerializable related types using Microsoft.PowerShell.CoreClr.Stubs; #else using System.Security.Permissions; #endif namespace System.Management.Automation { /// /// Errors reported by Monad will be in one of these categories. /// /// /// Do not specify ErrorCategory.NotSpecified when creating an /// . /// Choose the best match from among the other values. /// public enum ErrorCategory { /// /// No error category is specified, or the error category is invalid. /// /// /// Do not specify ErrorCategory.NotSpecified when creating an /// . /// Choose the best match from among the other values. /// NotSpecified = 0, /// /// /// OpenError = 1, /// /// /// CloseError = 2, /// /// /// DeviceError = 3, /// /// /// DeadlockDetected = 4, /// /// /// InvalidArgument = 5, /// /// /// InvalidData = 6, /// /// /// InvalidOperation = 7, /// /// /// InvalidResult = 8, /// /// /// InvalidType = 9, /// /// /// MetadataError = 10, /// /// /// NotImplemented = 11, /// /// /// NotInstalled = 12, /// /// Object can not be found (file, directory, computer, system resource, etc.) /// ObjectNotFound = 13, /// /// /// OperationStopped = 14, /// /// /// OperationTimeout = 15, /// /// /// SyntaxError = 16, /// /// /// ParserError = 17, /// /// Operation not permitted /// PermissionDenied = 18, /// /// /// ResourceBusy = 19, /// /// /// ResourceExists = 20, /// /// /// ResourceUnavailable = 21, /// /// /// ReadError = 22, /// /// /// WriteError = 23, /// /// A non-Monad command reported an error to its STDERR pipe. /// /// /// The Engine uses this ErrorCategory when it executes a native /// console applications and captures the errors reported by the /// native application. Avoid using ErrorCategory.FromStdErr /// in other circumstances. /// FromStdErr = 24, /// /// Used for security exceptions /// SecurityError = 25, /// /// The contract of a protocol is not being followed. Should not happen /// with well-behaved components. /// ProtocolError = 26, /// /// The operation depends on a network connection that cannot be /// established or maintained. /// ConnectionError = 27, /// /// Could not authenticate the user to the service. Could mean that the /// credentials are invalid or the authentication system is not /// functioning properly. /// AuthenticationError = 28, /// /// Internal limits prevent the operation from being executed. /// LimitsExceeded = 29, /// /// Controls on the use of traffic or resources prevent the operation /// from being executed. /// QuotaExceeded = 30, /// /// The operation attempted to use functionality that is currently /// disabled. /// NotEnabled = 31, } // enum ErrorCategory /// /// Contains auxiliary information about an /// /// public class ErrorCategoryInfo { #region ctor internal ErrorCategoryInfo(ErrorRecord errorRecord) { if (null == errorRecord) throw new ArgumentNullException("errorRecord"); _errorRecord = errorRecord; } #endregion ctor #region Properties /// /// /// for this error /// public ErrorCategory Category { get { return _errorRecord._category; } } /// /// text description of the operation which /// encountered the error /// /// text description of the operation /// /// By default, this is the cmdlet name. /// The default can be overridden by calling Set with a /// non-empty value, for example "Delete". /// public string Activity { get { if (!String.IsNullOrEmpty(_errorRecord._activityOverride)) return _errorRecord._activityOverride; if (null != _errorRecord.InvocationInfo && (_errorRecord.InvocationInfo.MyCommand is CmdletInfo || _errorRecord.InvocationInfo.MyCommand is IScriptCommandInfo) && !String.IsNullOrEmpty(_errorRecord.InvocationInfo.MyCommand.Name) ) { return _errorRecord.InvocationInfo.MyCommand.Name; } return ""; } set { _errorRecord._activityOverride = value; } } /// /// text description of the error /// /// text description of the error /// /// By default, this is the exception type. /// The default can be overridden by calling Set with a /// non-empty value, for example "Permission Denied". /// public string Reason { get { _reasonIsExceptionType = false; if (!String.IsNullOrEmpty(_errorRecord._reasonOverride)) return _errorRecord._reasonOverride; if (null != _errorRecord.Exception) { _reasonIsExceptionType = true; return _errorRecord.Exception.GetType().Name; } return ""; } set { _errorRecord._reasonOverride = value; } } private bool _reasonIsExceptionType; /// /// text description of the target object /// /// text description of the target object /// /// By default, this is TargetObject.ToString(), or the empty string /// if the target object is null. /// The default can be overridden by calling Set with a /// non-empty value, for example "John Doe". /// public string TargetName { get { if (!String.IsNullOrEmpty(_errorRecord._targetNameOverride)) return _errorRecord._targetNameOverride; if (null != _errorRecord.TargetObject) { string targetInString; try { targetInString = _errorRecord.TargetObject.ToString(); } catch (Exception e) { CommandProcessorBase.CheckForSevereException(e); targetInString = null; } return ErrorRecord.NotNull(targetInString); } return ""; } set { _errorRecord._targetNameOverride = value; } } /// /// text description of the type of the target object /// /// text description of the type of the target object /// /// By default, this is TargetObject.GetType().ToString(), /// or the empty string if the target object is null. /// The default can be overridden by calling Set with a /// non-empty value, for example "Active Directory User". /// public string TargetType { get { if (!String.IsNullOrEmpty(_errorRecord._targetTypeOverride)) return _errorRecord._targetTypeOverride; if (null != _errorRecord.TargetObject) { return _errorRecord.TargetObject.GetType().Name; } return ""; } set { _errorRecord._targetTypeOverride = value; } } #endregion Properties #region Methods /// /// concise text description based on /// /// /// concise text description /// /// GetMessage returns a concise string which categorizes the error, /// based on /// /// and including the other fields of /// /// as appropriate. This string is much shorter /// than /// or /// , since it only /// categorizes the error and does not contain a full description /// or recommended actions. The default host will display this /// string instead of the full message if shell variable /// $ErrorView is set to "CategoryView". /// public string GetMessage() { /* Remoting not in E12 if (!String.IsNullOrEmpty (_errorRecord._serializedErrorCategoryMessageOverride)) return _errorRecord._serializedErrorCategoryMessageOverride; */ return GetMessage(CultureInfo.CurrentUICulture); } /// /// concise text description based on /// /// /// Culture in which to display message /// concise text description /// /// GetMessage returns a concise string which categorizes the error, /// based on /// /// and including the other fields of /// /// as appropriate. This string is much shorter /// than /// or /// , since it only /// categorizes the error and does not contain a full description /// or recommended actions. The default host will display this /// string instead of the full message if shell variable /// $ErrorView is set to "CategoryView". /// public string GetMessage(CultureInfo uiCultureInfo) { // get template text string errorCategoryString = Category.ToString(); if (String.IsNullOrEmpty(errorCategoryString)) { // this probably indicates an invalid ErrorCategory value errorCategoryString = ErrorCategory.NotSpecified.ToString(); } string templateText = ErrorCategoryStrings.ResourceManager.GetString(errorCategoryString, uiCultureInfo); if (String.IsNullOrEmpty(templateText)) { // this probably indicates an invalid ErrorCategory value templateText = ErrorCategoryStrings.NotSpecified; } Diagnostics.Assert(!String.IsNullOrEmpty(templateText), "ErrorCategoryStrings.resx resource failure"); string activityInUse = Ellipsize(uiCultureInfo, Activity); string targetNameInUse = Ellipsize(uiCultureInfo, TargetName); string targetTypeInUse = Ellipsize(uiCultureInfo, TargetType); // if the reason is a exception type name, we should output the whole name string reasonInUse = Reason; reasonInUse = _reasonIsExceptionType ? reasonInUse : Ellipsize(uiCultureInfo, reasonInUse); // assemble final string try { return String.Format(uiCultureInfo, templateText, activityInUse, targetNameInUse, targetTypeInUse, reasonInUse, errorCategoryString); } catch (FormatException) { templateText = ErrorCategoryStrings.InvalidErrorCategory; return String.Format(uiCultureInfo, templateText, activityInUse, targetNameInUse, targetTypeInUse, reasonInUse, errorCategoryString); } } /// /// Same as /// /// /// developer-readable identifier public override string ToString() { return GetMessage(CultureInfo.CurrentUICulture); } #endregion Methods #region Private // back-reference for facade class private ErrorRecord _errorRecord; /// /// The Activity, Reason, TargetName and TargetType strings in /// ErrorCategoryInfo can be of unlimited length. In order to /// control the maximum length of the GetMessage() string, we /// ellipsize these strings. The current heuristic is to take /// strings longer than 40 characters and ellipsize them to /// the first and last 15 characters plus "..." in the middle. /// /// culture to retrieve template if needed /// original string /// Ellipsized version of string /// /// "Please do not make this public as ellipsize is not a word." /// internal static string Ellipsize(CultureInfo uiCultureInfo, string original) { if (40 >= original.Length) { return original; } string first = original.Substring(0, 15); string last = original.Substring(original.Length - 15, 15); return string.Format(uiCultureInfo, ErrorPackage.Ellipsize, first, last); } #endregion Private } // class ErrorCategoryInfo /// /// additional details about an /// /// /// /// ErrorDetails represents additional details about an /// , /// starting with a replacement Message. Clients can use ErrorDetails /// when they want to display a more specific Message than the one /// contained in a particular Exception, without having to create /// a new Exception or define a new Exception class. /// /// It is permitted to subclass /// but there is no established scenario for doing this, nor has it been tested. /// [Serializable] public class ErrorDetails : ISerializable { #region Constructor /// /// Creates an instance of ErrorDetails specifying a Message. /// /// /// It is preferred for Cmdlets to use /// , /// for CmdletProviders to use /// , /// and for other localizable code to use /// /// where possible. /// /// public ErrorDetails(string message) { _message = message; } #region UseResourceId /// /// Creates an instance of ErrorDetails specifying a Message. /// This variant is used by cmdlets. /// /// cmdlet containing the template string /// by default, the /// /// name /// /// by default, the resourceId in the /// /// /// /// /// insertion parameters /// /// /// This variant is a shortcut to build an instance of /// /// reducing the steps which localizable code generally has to duplicate when it /// generates a localizable string. This variant is preferred over /// , /// since the improved /// information about the error may help enable future scenarios. /// /// This constructor first loads the error message template string using /// . /// The default implementation of /// /// will load a string resource from the cmdlet assembly using /// and ; /// however, specific cmdlets can override this behavior /// by overriding virtual method /// . /// This constructor then inserts the specified args using /// . /// public ErrorDetails( Cmdlet cmdlet, string baseName, string resourceId, params object[] args) { _message = BuildMessage(cmdlet, baseName, resourceId, args); } /// /// Creates an instance of ErrorDetails specifying a Message. /// This variant is used by CmdletProviders. /// /// /// Resource supplier, most often an instance of /// . /// /// by default, the /// /// name /// /// by default, the resourceId in the /// /// /// /// /// insertion parameters /// /// /// This variant is a shortcut to build an instance of /// /// reducing the steps which localizable code generally has to duplicate when it /// generates a localizable string. This variant is preferred over /// , /// since the improved /// information about the error may help enable future scenarios. /// /// This constructor first loads a template string using /// . /// The default implementation of /// /// will load a string resource from the CmdletProvider assembly using /// and ; /// however, specific CmdletProviders can override this behavior /// by overriding virtual method /// , /// and it is also possible that PSSnapin custom classes /// which are not instances of /// /// will implement /// . /// The constructor then inserts the specified args using /// . /// public ErrorDetails( IResourceSupplier resourceSupplier, string baseName, string resourceId, params object[] args) { _message = BuildMessage(resourceSupplier, baseName, resourceId, args); } /// /// Creates an instance of ErrorDetails specifying a Message. /// This variant is used by other code without a reference to /// a or instance. /// /// /// assembly containing the template string /// /// by default, the /// /// name /// /// by default, the resourceId in the /// /// /// /// /// insertion parameters /// /// /// This variant is a shortcut to build an instance of /// /// reducing the steps which localizable code generally has to duplicate when it /// generates a localizable string. This variant is preferred over /// , /// since the improved /// information about the error may help enable future scenarios. /// /// This constructor first loads a template string from the assembly using /// . /// The constructor then inserts the specified args using /// . /// public ErrorDetails( System.Reflection.Assembly assembly, string baseName, string resourceId, params object[] args) { _message = BuildMessage(assembly, baseName, resourceId, args); } #endregion UseResourceId // deep-copy constructor internal ErrorDetails(ErrorDetails errorDetails) { _message = errorDetails._message; _recommendedAction = errorDetails._recommendedAction; } #endregion Constructor #region Serialization /// /// Initializes a new instance of the ErrorDetails class /// using data serialized via /// /// /// serialization information /// streaming context /// constructed object protected ErrorDetails(SerializationInfo info, StreamingContext context) { _message = info.GetString("ErrorDetails_Message"); _recommendedAction = info.GetString( "ErrorDetails_RecommendedAction"); } /// /// Serializer for /// /// serialization information /// streaming context [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)] public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { if (info != null) { info.AddValue("ErrorDetails_Message", _message); info.AddValue("ErrorDetails_RecommendedAction", _recommendedAction); } } #endregion Serialization #region Public Properties /// /// Message which replaces /// in /// /// /// /// /// When an instance of /// /// contains a non-null /// /// and /// /// is non-empty, the default host will display it instead of /// the in /// . /// /// This should be a grammatically correct localized text string, as with /// /// public string Message { get { return ErrorRecord.NotNull(_message); } } private string _message = ""; /// /// Text describing the recommended action in the event that this error /// occurs. This is empty unless the code which generates the error /// specifies it explicitly. /// /// /// /// This should be a grammatically correct localized text string. /// This may be left empty. /// public string RecommendedAction { get { return ErrorRecord.NotNull(_recommendedAction); } set { _recommendedAction = value; } } private string _recommendedAction = ""; #endregion Public Properties #region Internal Properties internal Exception TextLookupError { get { return _textLookupError; } set { _textLookupError = value; } } private Exception _textLookupError /* = null */; #endregion Internal Properties #region ToString /// /// As /// /// developer-readable identifier public override string ToString() { return Message; } #endregion ToString #region Private private string BuildMessage( Cmdlet cmdlet, string baseName, string resourceId, params object[] args) { if (null == cmdlet) throw PSTraceSource.NewArgumentNullException("cmdlet"); if (String.IsNullOrEmpty(baseName)) throw PSTraceSource.NewArgumentNullException("baseName"); if (String.IsNullOrEmpty(resourceId)) throw PSTraceSource.NewArgumentNullException("resourceId"); string template = ""; try { template = cmdlet.GetResourceString(baseName, resourceId); } catch (MissingManifestResourceException e) { _textLookupError = e; return ""; // fallback to Exception.Message } catch (ArgumentException e) { _textLookupError = e; return ""; // fallback to Exception.Message } return BuildMessage(template, baseName, resourceId, args); } // BuildMessage private string BuildMessage( IResourceSupplier resourceSupplier, string baseName, string resourceId, params object[] args) { if (null == resourceSupplier) throw PSTraceSource.NewArgumentNullException("resourceSupplier"); if (String.IsNullOrEmpty(baseName)) throw PSTraceSource.NewArgumentNullException("baseName"); if (String.IsNullOrEmpty(resourceId)) throw PSTraceSource.NewArgumentNullException("resourceId"); string template = ""; try { template = resourceSupplier.GetResourceString(baseName, resourceId); } catch (MissingManifestResourceException e) { _textLookupError = e; return ""; // fallback to Exception.Message } catch (ArgumentException e) { _textLookupError = e; return ""; // fallback to Exception.Message } return BuildMessage(template, baseName, resourceId, args); } // BuildMessage private string BuildMessage( System.Reflection.Assembly assembly, string baseName, string resourceId, params object[] args) { if (null == assembly) throw PSTraceSource.NewArgumentNullException("assembly"); if (String.IsNullOrEmpty(baseName)) throw PSTraceSource.NewArgumentNullException("baseName"); if (String.IsNullOrEmpty(resourceId)) throw PSTraceSource.NewArgumentNullException("resourceId"); string template = ""; ResourceManager manager = ResourceManagerCache.GetResourceManager( assembly, baseName); try { template = manager.GetString( resourceId, CultureInfo.CurrentUICulture); } catch (MissingManifestResourceException e) { _textLookupError = e; return ""; // fallback to Exception.Message } return BuildMessage(template, baseName, resourceId, args); } // BuildMessage private string BuildMessage( string template, string baseName, string resourceId, params object[] args) { if (String.IsNullOrEmpty(template) || 1 >= template.Trim().Length) { _textLookupError = PSTraceSource.NewInvalidOperationException( ErrorPackage.ErrorDetailsEmptyTemplate, baseName, resourceId); return ""; // fallback to Exception.Message } try { return String.Format( CultureInfo.CurrentCulture, template, args); } catch (FormatException e) { _textLookupError = e; return ""; // fallback to Exception.Message } } // BuildMessage #endregion Private } // class ErrorDetails /// /// Represents an error. /// /// /// An ErrorRecord describes an error. It extends the usual information /// in with the additional information in /// , /// , /// , /// , /// , and /// . /// Non-terminating errors are stored as /// /// instances in shell variable /// $error. /// /// Some terminating errors implement /// /// which gives them an ErrorRecord property containing this additional /// information. In this case, ErrorRecord.Exception will be an instance of /// . /// rather than the actual exception, to avoid the mutual references. /// [Serializable] public class ErrorRecord : ISerializable { #region Constructor private ErrorRecord() { } /// /// Creates an instance of ErrorRecord. /// /// /// This is an exception which describes the error. /// This argument may not be null, but it is not required /// that the exception have ever been thrown. /// /// /// This string will be used to construct the FullyQualifiedErrorId, /// which is a global identifier of the error condition. Pass a /// non-empty string which is specific to this error condition in /// this context. /// /// /// This is the ErrorCategory which best describes the error. /// /// /// This is the object against which the cmdlet or provider /// was operating when the error occurred. This is optional. /// public ErrorRecord( Exception exception, string errorId, ErrorCategory errorCategory, object targetObject) { if (null == exception) throw PSTraceSource.NewArgumentNullException("exception"); if (errorId == null) errorId = ""; // targetObject may be null _error = exception; _errorId = errorId; _category = errorCategory; _target = targetObject; } #region Serialization // We serialize the exception as its original type, ensuring // that the ErrorRecord information arrives in full, but taking // the risk that it cannot be serialized/deserialized at all if // (1) the exception type does not exist on the target machine, or // (2) the exception serializer/deserializer fails or is not // implemented/supported. // // We do not attempt to serialize TargetObject. // // We do not attempt to serialize InvocationInfo. There is // potentially some useful information there, but serializing // InvocationInfo, Token, InternalCommand and its subclasses, and // CommandInfo and its subclasses is too expensive. /// /// Initializes a new instance of the ErrorRecord class /// using data serialized via /// /// /// serialization information /// streaming context /// constructed object /// /// ErrorRecord instances which are serialized using /// /// will only be partially reconstructed. /// protected ErrorRecord(SerializationInfo info, StreamingContext context) { PSObject psObject = PSObject.ConstructPSObjectFromSerializationInfo(info, context); ConstructFromPSObjectForRemoting(psObject); } /// /// Deserializer for /// /// serialization information /// streaming context [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)] public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { if (info != null) { PSObject psObject = RemotingEncoder.CreateEmptyPSObject(); // for binary serialization always serialize the extended info ToPSObjectForRemoting(psObject, true); psObject.GetObjectData(info, context); } } #endregion Serialization #region Remoting /// /// isSerialized is set to true if this error record is serialized. /// private bool _isSerialized = false; /// /// Is this instance serialized. /// /// internal bool IsSerialized { get { return _isSerialized; } } /// /// Value for FullyQualifiedErrorId in case of serialized error record. /// private string _serializedFullyQualifiedErrorId = null; /// /// Message overidee for CategoryInfo.GetMessage method /// internal string _serializedErrorCategoryMessageOverride = null; /// /// This constructor is used by remoting code to create ErrorRecord. /// Various information is obtained from serialized ErrorRecord. /// /// /// /// /// /// /// /// /// /// /// /// internal ErrorRecord ( Exception exception, object targetObject, string fullyQualifiedErrorId, ErrorCategory errorCategory, string errorCategory_Activity, string errorCategory_Reason, string errorCategory_TargetName, string errorCategory_TargetType, string errorCategory_Message, string errorDetails_Message, string errorDetails_RecommendedAction ) { PopulateProperties(exception, targetObject, fullyQualifiedErrorId, errorCategory, errorCategory_Activity, errorCategory_Reason, errorCategory_TargetName, errorCategory_TargetType, errorDetails_Message, errorDetails_Message, errorDetails_RecommendedAction, null); } private void PopulateProperties(Exception exception, object targetObject, string fullyQualifiedErrorId, ErrorCategory errorCategory, string errorCategory_Activity, string errorCategory_Reason, string errorCategory_TargetName, string errorCategory_TargetType, string errorCategory_Message, string errorDetails_Message, string errorDetails_RecommendedAction, string errorDetails_ScriptStackTrace) { if (exception == null) { throw PSTraceSource.NewArgumentNullException("exception"); } if (fullyQualifiedErrorId == null) { throw PSTraceSource.NewArgumentNullException("fullyQualifiedErrorId"); } //Mark this error record as serialized _isSerialized = true; _error = exception; _target = targetObject; _serializedFullyQualifiedErrorId = fullyQualifiedErrorId; _category = errorCategory; _activityOverride = errorCategory_Activity; _reasonOverride = errorCategory_Reason; _targetNameOverride = errorCategory_TargetName; _targetTypeOverride = errorCategory_TargetType; _serializedErrorCategoryMessageOverride = errorCategory_Message; if (errorDetails_Message != null) { _errorDetails = new ErrorDetails(errorDetails_Message); if (errorDetails_RecommendedAction != null) { _errorDetails.RecommendedAction = errorDetails_RecommendedAction; } } _scriptStackTrace = errorDetails_ScriptStackTrace; } /// /// Adds the information about this error record to PSObject as notes. /// /// internal void ToPSObjectForRemoting(PSObject dest) { ToPSObjectForRemoting(dest, SerializeExtendedInfo); } private void ToPSObjectForRemoting(PSObject dest, bool serializeExtInfo) { RemotingEncoder.AddNoteProperty(dest, "Exception", delegate () { return Exception; }); RemotingEncoder.AddNoteProperty(dest, "TargetObject", delegate () { return TargetObject; }); RemotingEncoder.AddNoteProperty(dest, "FullyQualifiedErrorId", delegate () { return FullyQualifiedErrorId; }); RemotingEncoder.AddNoteProperty(dest, "InvocationInfo", delegate () { return InvocationInfo; }); RemotingEncoder.AddNoteProperty(dest, "ErrorCategory_Category", delegate () { return (int)CategoryInfo.Category; }); RemotingEncoder.AddNoteProperty(dest, "ErrorCategory_Activity", delegate () { return CategoryInfo.Activity; }); RemotingEncoder.AddNoteProperty(dest, "ErrorCategory_Reason", delegate () { return CategoryInfo.Reason; }); RemotingEncoder.AddNoteProperty(dest, "ErrorCategory_TargetName", delegate () { return CategoryInfo.TargetName; }); RemotingEncoder.AddNoteProperty(dest, "ErrorCategory_TargetType", delegate () { return CategoryInfo.TargetType; }); RemotingEncoder.AddNoteProperty(dest, "ErrorCategory_Message", delegate () { return CategoryInfo.GetMessage(CultureInfo.CurrentCulture); }); if (ErrorDetails != null) { RemotingEncoder.AddNoteProperty(dest, "ErrorDetails_Message", delegate () { return ErrorDetails.Message; }); RemotingEncoder.AddNoteProperty(dest, "ErrorDetails_RecommendedAction", delegate () { return ErrorDetails.RecommendedAction; }); } if (!serializeExtInfo || this.InvocationInfo == null) { RemotingEncoder.AddNoteProperty(dest, "SerializeExtendedInfo", () => false); } else { RemotingEncoder.AddNoteProperty(dest, "SerializeExtendedInfo", () => true); this.InvocationInfo.ToPSObjectForRemoting(dest); RemotingEncoder.AddNoteProperty(dest, "PipelineIterationInfo", delegate () { return PipelineIterationInfo; }); } if (!string.IsNullOrEmpty(this.ScriptStackTrace)) { RemotingEncoder.AddNoteProperty(dest, "ErrorDetails_ScriptStackTrace", delegate () { return this.ScriptStackTrace; }); } } /// /// Gets the value for note from mshObject /// /// /// /// PSObject from which value is fetched. /// /// /// /// name of note whose value is fetched /// /// /// value of note /// /// private static object GetNoteValue ( PSObject mshObject, string note ) { PSNoteProperty property = mshObject.Properties[note] as PSNoteProperty; if (property != null) { return property.Value; } else { return null; } } /// /// Create an ErrorRecord object from serialized ErrorRecord. /// serializedErrorRecord PSObject is in the format returned /// by ToPSObjectForRemoting method. /// /// /// /// PSObject to convert to ErrorRecord /// /// /// /// /// ErrorRecord convert from mshObject. /// /// /// /// /// Thrown if mshObject parameter is null. /// /// internal static ErrorRecord FromPSObjectForRemoting ( PSObject serializedErrorRecord ) { ErrorRecord er = new ErrorRecord(); er.ConstructFromPSObjectForRemoting(serializedErrorRecord); return er; } private void ConstructFromPSObjectForRemoting(PSObject serializedErrorRecord) { if (serializedErrorRecord == null) { throw PSTraceSource.NewArgumentNullException("serializedErrorRecord"); } //Get Exception PSObject serializedException = RemotingDecoder.GetPropertyValue(serializedErrorRecord, "Exception"); //Get Target object object targetObject = RemotingDecoder.GetPropertyValue(serializedErrorRecord, "TargetObject"); string exceptionMessage = null; if (serializedException != null) { PSPropertyInfo messageProperty = serializedException.Properties["Message"] as PSPropertyInfo; if (messageProperty != null) { exceptionMessage = messageProperty.Value as string; } } //Get FullyQualifiedErrorId string fullyQualifiedErrorId = RemotingDecoder.GetPropertyValue(serializedErrorRecord, "FullyQualifiedErrorId") ?? "fullyQualifiedErrorId"; //Get ErrorCategory... ErrorCategory errorCategory = RemotingDecoder.GetPropertyValue(serializedErrorRecord, "errorCategory_Category"); //Get Various ErrorCategory fileds string errorCategory_Activity = RemotingDecoder.GetPropertyValue(serializedErrorRecord, "ErrorCategory_Activity"); string errorCategory_Reason = RemotingDecoder.GetPropertyValue(serializedErrorRecord, "ErrorCategory_Reason"); string errorCategory_TargetName = RemotingDecoder.GetPropertyValue(serializedErrorRecord, "ErrorCategory_TargetName"); string errorCategory_TargetType = RemotingDecoder.GetPropertyValue(serializedErrorRecord, "ErrorCategory_TargetType"); string errorCategory_Message = RemotingDecoder.GetPropertyValue(serializedErrorRecord, "ErrorCategory_Message"); //Get InvocationInfo (optional property) PSObject invocationInfo = Microsoft.PowerShell.DeserializingTypeConverter.GetPropertyValue( serializedErrorRecord, "InvocationInfo", Microsoft.PowerShell.DeserializingTypeConverter.RehydrationFlags.MissingPropertyOk); //Get Error Detail (these note properties are optional, so can't right now use RemotingDecoder...) string errorDetails_Message = GetNoteValue(serializedErrorRecord, "ErrorDetails_Message") as string; string errorDetails_RecommendedAction = GetNoteValue(serializedErrorRecord, "ErrorDetails_RecommendedAction") as string; string errorDetails_ScriptStackTrace = GetNoteValue(serializedErrorRecord, "ErrorDetails_ScriptStackTrace") as string; RemoteException re = new RemoteException((String.IsNullOrWhiteSpace(exceptionMessage) == false) ? exceptionMessage : errorCategory_Message, serializedException, invocationInfo); //Create ErrorRecord PopulateProperties( re, targetObject, fullyQualifiedErrorId, errorCategory, errorCategory_Activity, errorCategory_Reason, errorCategory_TargetName, errorCategory_TargetType, errorCategory_Message, errorDetails_Message, errorDetails_RecommendedAction, errorDetails_ScriptStackTrace ); re.SetRemoteErrorRecord(this); // // Get the InvocationInfo // _serializeExtendedInfo = RemotingDecoder.GetPropertyValue(serializedErrorRecord, "SerializeExtendedInfo"); if (_serializeExtendedInfo) { _invocationInfo = new InvocationInfo(serializedErrorRecord); ArrayList iterationInfo = RemotingDecoder.GetPropertyValue(serializedErrorRecord, "PipelineIterationInfo"); if (null != iterationInfo) { _pipelineIterationInfo = new ReadOnlyCollection((int[])iterationInfo.ToArray(typeof(Int32))); } } else { _invocationInfo = null; } } #endregion Remoting /// /// Copy constructor, for use when a new wrapper exception wraps an /// exception which already has an ErrorRecord /// ErrorCategoryInfo and ErrorDetails are deep-copied, other fields are not. /// /// wrapped ErrorRecord /// /// If the wrapped exception contains a ParentContainsErrorRecordException, the new /// ErrorRecord should have this exception as its Exception instead. /// public ErrorRecord(ErrorRecord errorRecord, Exception replaceParentContainsErrorRecordException) { if (errorRecord == null) { throw new PSArgumentNullException("errorRecord"); } if (null != replaceParentContainsErrorRecordException && (errorRecord.Exception is ParentContainsErrorRecordException)) { _error = replaceParentContainsErrorRecordException; } else { _error = errorRecord.Exception; } _target = errorRecord.TargetObject; _errorId = errorRecord._errorId; _category = errorRecord._category; _activityOverride = errorRecord._activityOverride; _reasonOverride = errorRecord._reasonOverride; _targetNameOverride = errorRecord._targetNameOverride; _targetTypeOverride = errorRecord._targetTypeOverride; if (null != errorRecord.ErrorDetails) _errorDetails = new ErrorDetails(errorRecord.ErrorDetails); SetInvocationInfo(errorRecord._invocationInfo); _scriptStackTrace = errorRecord._scriptStackTrace; _serializedFullyQualifiedErrorId = errorRecord._serializedFullyQualifiedErrorId; } #endregion Constructor #region Override /// /// Wrap the current ErrorRecord instance /// A derived class needs to override this method if it contains additional info that needs to be kept when it gets wrapped. /// /// /// If the wrapped exception contains a ParentContainsErrorRecordException, the new /// ErrorRecord should have this exception as its Exception instead. /// /// internal virtual ErrorRecord WrapException(Exception replaceParentContainsErrorRecordException) { return new ErrorRecord(this, replaceParentContainsErrorRecordException); } #endregion Override #region Public Properties /// /// An Exception describing the error. /// /// never null public Exception Exception { get { Diagnostics.Assert(null != _error, "_error is null"); return _error; } } private Exception _error /* = null */; /// /// The object against which the error occurred. /// /// may be null public object TargetObject { get { return _target; } } private object _target /* = null */; internal void SetTargetObject(object target) { _target = target; } /// /// Information regarding the ErrorCategory /// associated with this error, and with the categorized error message /// for that ErrorCategory. /// /// never null public ErrorCategoryInfo CategoryInfo { get { return _categoryInfo ?? (_categoryInfo = new ErrorCategoryInfo(this)); } } private ErrorCategoryInfo _categoryInfo; /// /// String which uniquely identifies this error condition /// /// never null /// /// FullyQualifiedErrorid identifies this error condition /// more specifically than either the ErrorCategory /// or the Exception. Use FullyQualifiedErrorId to filter specific /// error conditions, or to associate special handling with specific /// error conditions. /// public string FullyQualifiedErrorId { get { if (_serializedFullyQualifiedErrorId != null) return _serializedFullyQualifiedErrorId; string typeName = GetInvocationTypeName(); string delimiter = (String.IsNullOrEmpty(typeName) || String.IsNullOrEmpty(_errorId)) ? "" : ","; return NotNull(_errorId) + delimiter + NotNull(typeName); } } /// /// Additional information about the error. /// /// may be null /// /// In particular, ErrorDetails.Message (if present and non-empty) /// contains a replacement message which should be displayed instead of /// Exception.Message. /// public ErrorDetails ErrorDetails { get { return _errorDetails; } set { _errorDetails = value; } } private ErrorDetails _errorDetails; /// /// Identifies the cmdlet, script, or other command which caused /// the error. /// /// may be null public InvocationInfo InvocationInfo { get { return _invocationInfo; } } private InvocationInfo _invocationInfo /* = null */; internal void SetInvocationInfo(InvocationInfo invocationInfo) { // Save the DisplayScriptPosition, if set IScriptExtent savedDisplayScriptPosition = null; if (_invocationInfo != null) { savedDisplayScriptPosition = _invocationInfo.DisplayScriptPosition; } // Assign the invocationInfo if (invocationInfo != null) { _invocationInfo = new InvocationInfo(invocationInfo.MyCommand, invocationInfo.ScriptPosition); _invocationInfo.InvocationName = invocationInfo.InvocationName; if (invocationInfo.MyCommand == null) { // Pass the history id to new InvocationInfo object of command info is null since history // information cannot be obtained in this case. _invocationInfo.HistoryId = invocationInfo.HistoryId; } } // Restore the DisplayScriptPosition if (savedDisplayScriptPosition != null) { _invocationInfo.DisplayScriptPosition = savedDisplayScriptPosition; } LockScriptStackTrace(); // // Copy a snapshot of the PipelinePositionInfo from the InvocationInfo to this ErrorRecord // if (invocationInfo != null && invocationInfo.PipelineIterationInfo != null) { int[] snapshot = (int[])invocationInfo.PipelineIterationInfo.Clone(); _pipelineIterationInfo = new ReadOnlyCollection(snapshot); } } // 2005/07/14-913791 "write-error output is confusing and misleading" internal bool PreserveInvocationInfoOnce { get { return _preserveInvocationInfoOnce; } set { _preserveInvocationInfoOnce = value; } } private bool _preserveInvocationInfoOnce /* = false */; /// /// The script stack trace for the error. /// public string ScriptStackTrace { get { return _scriptStackTrace; } } private string _scriptStackTrace; internal void LockScriptStackTrace() { if (_scriptStackTrace != null) { return; } var context = LocalPipeline.GetExecutionContextFromTLS(); if (context != null) { StringBuilder sb = new StringBuilder(); var callstack = context.Debugger.GetCallStack(); bool first = true; foreach (var frame in callstack) { if (!first) { sb.Append(Environment.NewLine); } first = false; sb.Append(frame.ToString()); } _scriptStackTrace = sb.ToString(); } } /// /// The status of the pipeline when this record was created. /// public ReadOnlyCollection PipelineIterationInfo { get { return _pipelineIterationInfo; } } private ReadOnlyCollection _pipelineIterationInfo = Utils.EmptyReadOnlyCollection(); /// /// Whether to serizalize the InvocationInfo during remote calls /// internal bool SerializeExtendedInfo { get { return _serializeExtendedInfo; } set { _serializeExtendedInfo = value; } } private bool _serializeExtendedInfo = false; #endregion Public Properties #region Private private string _errorId; #region Exposed by ErrorCategoryInfo internal ErrorCategory _category; internal string _activityOverride; internal string _reasonOverride; internal string _targetNameOverride; internal string _targetTypeOverride; #endregion Exposed by ErrorCategoryInfo internal static string NotNull(string s) { return s ?? ""; } private string GetInvocationTypeName() { InvocationInfo invocationInfo = this.InvocationInfo; if (null == invocationInfo) return ""; CommandInfo commandInfo = invocationInfo.MyCommand; if (null == commandInfo) return ""; IScriptCommandInfo scriptInfo = commandInfo as IScriptCommandInfo; if (scriptInfo != null) return commandInfo.Name; CmdletInfo cmdletInfo = commandInfo as CmdletInfo; if (null == cmdletInfo) return ""; return cmdletInfo.ImplementingType.FullName; } #endregion Private #region ToString /// /// As /// /// developer-readable identifier public override string ToString() { if (null != ErrorDetails && !String.IsNullOrEmpty(ErrorDetails.Message)) { return ErrorDetails.Message; } if (null != Exception) { if (!String.IsNullOrEmpty(Exception.Message)) { return Exception.Message; } return Exception.ToString(); } return base.ToString(); } #endregion ToString } // class ErrorRecord /// /// Implemented by exception classes which contain additional /// /// information. /// /// /// MSH defines certain exception classes which implement this interface. /// This includes wrapper exceptions such as /// , /// and also MSH engine errors such as /// . /// Cmdlets and providers should not define this interface; /// instead, they should use the /// WriteError(ErrorRecord) or /// ThrowTerminatingError(ErrorRecord) methods. /// The ErrorRecord property will contain an ErrorRecord /// which contains an instance of /// /// rather than the actual exception. /// /// Do not call WriteError(e.ErrorRecord). /// The ErrorRecord contained in the ErrorRecord property of /// an exception which implements IContainsErrorRecord /// should not be passed directly to WriteError, since it contains /// a ParentContainsErrorRecordException rather than the real exception. /// /// It is permitted for PSSnapins to implement custom Exception classes which implement /// , /// but it is generally preferable for Cmdlets and CmdletProviders to communicate /// /// information using /// /// or /// /// rather than by throwing an exception which implements /// . /// Consider implementing /// /// in your custom exception only if you throw it from a context /// where a reference to the active /// or /// /// is no longer available. /// public interface IContainsErrorRecord { /// /// This is the /// /// which provides additional information about the error. /// /// /// The instance returned by /// /// should contain in its /// /// property an instance of /// /// rather than a reference to the root exception. This prevents /// a recursive reference between the exception implementing /// and the /// . /// Use the /// /// constructor so that the /// /// will have the same /// /// as the root exception. /// /// ErrorRecord ErrorRecord { get; } } /// /// Objects implementing this interface can be used by /// /// /// /// /// implements this interface. PSSnapins can implement /// /// on their custom classes, but the only purpose would be to permit /// the custom class to be used in the /// . /// constructor. /// /// contains special constructor /// /// reducing the steps which localizable code generally has to duplicate when it /// generates a localizable string. This variant is preferred over /// , /// since the improved /// information about the error may help enable future scenarios. /// public interface IResourceSupplier { /// /// Gets the error message template string corresponding to /// and . /// /// /// If the desired behavior is simple string lookup /// in your assembly, you can use the /// /// constructor instead and not bother implementing /// . /// Consider implementing /// if you want more complex behavior. /// /// Insertions will be inserted into the string with /// /// to generate the final error message in /// . /// /// the base resource name /// the resource id /// the error message template string corresponding to baseName and resourceId string GetResourceString(string baseName, string resourceId); } } // namespace System.Management.Automation #pragma warning restore 56506