Files
PowerShell-PowerShell/src/System.Management.Automation/engine/ErrorPackage.cs
T

1843 lines
68 KiB
C#

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#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 System.Management.Automation.Language;
using System.Security.Permissions;
namespace System.Management.Automation
{
/// <summary>
/// Errors reported by PowerShell will be in one of these categories.
/// </summary>
/// <remarks>
/// Do not specify ErrorCategory.NotSpecified when creating an
/// <see cref="System.Management.Automation.ErrorRecord"/>.
/// Choose the best match from among the other values.
/// </remarks>
public enum ErrorCategory
{
/// <summary>
/// <para>
/// No error category is specified, or the error category is invalid.
/// </para>
/// <para>
/// Do not specify ErrorCategory.NotSpecified when creating an
/// <see cref="System.Management.Automation.ErrorRecord"/>.
/// Choose the best match from among the other values.
/// </para>
/// </summary>
NotSpecified = 0,
/// <summary>
/// </summary>
OpenError = 1,
/// <summary>
/// </summary>
CloseError = 2,
/// <summary>
/// </summary>
DeviceError = 3,
/// <summary>
/// </summary>
DeadlockDetected = 4,
/// <summary>
/// </summary>
InvalidArgument = 5,
/// <summary>
/// </summary>
InvalidData = 6,
/// <summary>
/// </summary>
InvalidOperation = 7,
/// <summary>
/// </summary>
InvalidResult = 8,
/// <summary>
/// </summary>
InvalidType = 9,
/// <summary>
/// </summary>
MetadataError = 10,
/// <summary>
/// </summary>
NotImplemented = 11,
/// <summary>
/// </summary>
NotInstalled = 12,
/// <summary>
/// Object can not be found (file, directory, computer, system resource, etc.)
/// </summary>
ObjectNotFound = 13,
/// <summary>
/// </summary>
OperationStopped = 14,
/// <summary>
/// </summary>
OperationTimeout = 15,
/// <summary>
/// </summary>
SyntaxError = 16,
/// <summary>
/// </summary>
ParserError = 17,
/// <summary>
/// Operation not permitted.
/// </summary>
PermissionDenied = 18,
/// <summary>
/// </summary>
ResourceBusy = 19,
/// <summary>
/// </summary>
ResourceExists = 20,
/// <summary>
/// </summary>
ResourceUnavailable = 21,
/// <summary>
/// </summary>
ReadError = 22,
/// <summary>
/// </summary>
WriteError = 23,
/// <summary>
/// <para>
/// A native command reported an error to its STDERR pipe.
/// </para>
/// <para>
/// 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.
/// </para>
/// </summary>
FromStdErr = 24,
/// <summary>
/// Used for security exceptions.
/// </summary>
SecurityError = 25,
/// <summary>
/// The contract of a protocol is not being followed. Should not happen
/// with well-behaved components.
/// </summary>
ProtocolError = 26,
/// <summary>
/// The operation depends on a network connection that cannot be
/// established or maintained.
/// </summary>
ConnectionError = 27,
/// <summary>
/// Could not authenticate the user to the service. Could mean that the
/// credentials are invalid or the authentication system is not
/// functioning properly.
/// </summary>
AuthenticationError = 28,
/// <summary>
/// Internal limits prevent the operation from being executed.
/// </summary>
LimitsExceeded = 29,
/// <summary>
/// Controls on the use of traffic or resources prevent the operation
/// from being executed.
/// </summary>
QuotaExceeded = 30,
/// <summary>
/// The operation attempted to use functionality that is currently
/// disabled.
/// </summary>
NotEnabled = 31,
}
/// <summary>
/// Contains auxiliary information about an
/// <see cref="System.Management.Automation.ErrorRecord"/>
/// </summary>
public class ErrorCategoryInfo
{
#region ctor
internal ErrorCategoryInfo(ErrorRecord errorRecord)
{
ArgumentNullException.ThrowIfNull(errorRecord);
_errorRecord = errorRecord;
}
#endregion ctor
#region Properties
/// <summary></summary>
/// <see cref="System.Management.Automation.ErrorCategory"/>
/// for this error
public ErrorCategory Category
{
get { return _errorRecord._category; }
}
/// <summary>
/// Text description of the operation which
/// encountered the error.
/// </summary>
/// <value>text description of the operation</value>
/// <remarks>
/// By default, this is the cmdlet name.
/// The default can be overridden by calling Set with a
/// non-empty value, for example "Delete".
/// </remarks>
public string Activity
{
get
{
if (!string.IsNullOrEmpty(_errorRecord._activityOverride))
{
return _errorRecord._activityOverride;
}
if (_errorRecord.InvocationInfo != null
&& (_errorRecord.InvocationInfo.MyCommand is CmdletInfo || _errorRecord.InvocationInfo.MyCommand is IScriptCommandInfo)
&& !string.IsNullOrEmpty(_errorRecord.InvocationInfo.MyCommand.Name)
)
{
return _errorRecord.InvocationInfo.MyCommand.Name;
}
return string.Empty;
}
set
{
_errorRecord._activityOverride = value;
}
}
/// <summary>
/// Text description of the error.
/// </summary>
/// <value>text description of the error</value>
/// <remarks>
/// By default, this is the exception type.
/// The default can be overridden by calling Set with a
/// non-empty value, for example "Permission Denied".
/// </remarks>
public string Reason
{
get
{
_reasonIsExceptionType = false;
if (!string.IsNullOrEmpty(_errorRecord._reasonOverride))
{
return _errorRecord._reasonOverride;
}
if (_errorRecord.Exception != null)
{
_reasonIsExceptionType = true;
return _errorRecord.Exception.GetType().Name;
}
return string.Empty;
}
set
{
_errorRecord._reasonOverride = value;
}
}
private bool _reasonIsExceptionType;
/// <summary>
/// Text description of the target object.
/// </summary>
/// <value>text description of the target object</value>
/// <remarks>
/// 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".
/// </remarks>
public string TargetName
{
get
{
if (!string.IsNullOrEmpty(_errorRecord._targetNameOverride))
{
return _errorRecord._targetNameOverride;
}
if (_errorRecord.TargetObject != null)
{
string targetInString;
try
{
targetInString = _errorRecord.TargetObject.ToString();
}
catch (Exception)
{
targetInString = null;
}
return ErrorRecord.NotNull(targetInString);
}
return string.Empty;
}
set
{
_errorRecord._targetNameOverride = value;
}
}
/// <summary>
/// Text description of the type of the target object.
/// </summary>
/// <value>text description of the type of the target object</value>
/// <remarks>
/// 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".
/// </remarks>
public string TargetType
{
get
{
if (!string.IsNullOrEmpty(_errorRecord._targetTypeOverride))
{
return _errorRecord._targetTypeOverride;
}
if (_errorRecord.TargetObject != null)
{
return _errorRecord.TargetObject.GetType().Name;
}
return string.Empty;
}
set
{
_errorRecord._targetTypeOverride = value;
}
}
#endregion Properties
#region Methods
/// <summary>
/// Concise text description based on
/// <see cref="System.Management.Automation.ErrorCategoryInfo.Category"/>
/// </summary>
/// <returns>Concise text description.</returns>
/// <remarks>
/// GetMessage returns a concise string which categorizes the error,
/// based on
/// <see cref="System.Management.Automation.ErrorCategoryInfo.Category"/>
/// and including the other fields of
/// <see cref="System.Management.Automation.ErrorCategoryInfo"/>
/// as appropriate. This string is much shorter
/// than
/// <see cref="System.Management.Automation.ErrorDetails.Message"/> or
/// <see cref="System.Exception.Message"/>, 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".
/// </remarks>
public string GetMessage()
{
/* Remoting not in E12
if (!string.IsNullOrEmpty (_errorRecord._serializedErrorCategoryMessageOverride))
return _errorRecord._serializedErrorCategoryMessageOverride;
*/
return GetMessage(CultureInfo.CurrentUICulture);
}
/// <summary>
/// Concise text description based on
/// <see cref="System.Management.Automation.ErrorCategoryInfo.Category"/>
/// </summary>
/// <param name="uiCultureInfo">Culture in which to display message.</param>
/// <returns>Concise text description.</returns>
/// <remarks>
/// GetMessage returns a concise string which categorizes the error,
/// based on
/// <see cref="System.Management.Automation.ErrorCategoryInfo.Category"/>
/// and including the other fields of
/// <see cref="System.Management.Automation.ErrorCategoryInfo"/>
/// as appropriate. This string is much shorter
/// than
/// <see cref="System.Management.Automation.ErrorDetails.Message"/> or
/// <see cref="System.Exception.Message"/>, 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".
/// </remarks>
public string GetMessage(CultureInfo uiCultureInfo)
{
// get template text
string errorCategoryString = Category.ToString();
if (string.IsNullOrEmpty(errorCategoryString))
{
// this probably indicates an invalid ErrorCategory value
errorCategoryString = nameof(ErrorCategory.NotSpecified);
}
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);
}
}
/// <summary>
/// Same as
/// <see cref="System.Management.Automation.ErrorCategoryInfo.GetMessage()"/>
/// </summary>
/// <returns>Developer-readable identifier.</returns>
public override string ToString()
{
return GetMessage(CultureInfo.CurrentUICulture);
}
#endregion Methods
#region Private
// back-reference for facade class
private readonly ErrorRecord _errorRecord;
/// <summary>
/// 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 19 characters plus "..." in the middle.
/// </summary>
/// <param name="uiCultureInfo">Culture to retrieve template if needed.</param>
/// <param name="original">Original string.</param>
/// <returns>Ellipsized version of string.</returns>
/// <remarks>
/// "Please do not make this public as ellipsize is not a word."
/// </remarks>
internal static string Ellipsize(CultureInfo uiCultureInfo, string original)
{
if (original.Length <= 40)
{
return original;
}
// We are splitting a string > 40 chars in half, so left and right can be
// at most 19 characters to include the ellipsis in the middle.
const int MaxHalfWidth = 19;
string first = original.Substring(0, MaxHalfWidth);
string last = original.Substring(original.Length - MaxHalfWidth, MaxHalfWidth);
return
string.Format(uiCultureInfo, ErrorPackage.Ellipsize, first, last);
}
#endregion Private
}
/// <summary>
/// Additional details about an
/// <see cref="System.Management.Automation.ErrorRecord"/>
/// </summary>
/// <remarks>
/// ErrorDetails represents additional details about an
/// <see cref="System.Management.Automation.ErrorRecord"/>,
/// 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 <see cref="ErrorDetails"/>
/// but there is no established scenario for doing this, nor has it been tested.
/// </remarks>
public class ErrorDetails : ISerializable
{
#region Constructor
/// <summary>
/// Creates an instance of ErrorDetails specifying a Message.
/// </summary>
/// <remarks>
/// It is preferred for Cmdlets to use
/// <see cref="ErrorDetails(Cmdlet,string,string,object[])"/>,
/// for CmdletProviders to use
/// <see cref="ErrorDetails(IResourceSupplier,string,string,object[])"/>,
/// and for other localizable code to use
/// <see cref="ErrorDetails(Assembly,string,string,object[])"/>
/// where possible.
/// </remarks>
/// <param name="message"></param>
public ErrorDetails(string message)
{
_message = message;
}
#region UseResourceId
/// <summary>
/// Creates an instance of ErrorDetails specifying a Message.
/// This variant is used by cmdlets.
/// </summary>
/// <param name="cmdlet">Cmdlet containing the template string.</param>
/// <param name="baseName">by default, the
/// <see cref="System.Resources.ResourceManager"/>
/// name</param>
/// <param name="resourceId">
/// by default, the resourceId in the
/// <see cref="System.Resources.ResourceManager"/>
/// </param>
/// <param name="args">
/// <see cref="System.String.Format(IFormatProvider,string,object[])"/>
/// insertion parameters
/// </param>
/// <remarks>
/// This variant is a shortcut to build an instance of
/// <see cref="System.Management.Automation.ErrorDetails"/>
/// reducing the steps which localizable code generally has to duplicate when it
/// generates a localizable string. This variant is preferred over
/// <see cref="System.Management.Automation.ErrorDetails(string)"/>,
/// since the improved
/// information about the error may help enable future scenarios.
///
/// This constructor first loads the error message template string using
/// <see cref="Cmdlet.GetResourceString"/>.
/// The default implementation of
/// <see cref="Cmdlet.GetResourceString"/>
/// will load a string resource from the cmdlet assembly using
/// <paramref name="baseName"/> and <paramref name="resourceId"/>;
/// however, specific cmdlets can override this behavior
/// by overriding virtual method
/// <see cref="Cmdlet.GetResourceString"/>.
/// This constructor then inserts the specified args using
/// <see cref="System.String.Format(IFormatProvider,string,object[])"/>.
/// </remarks>
public ErrorDetails(
Cmdlet cmdlet,
string baseName,
string resourceId,
params object[] args)
{
_message = BuildMessage(cmdlet, baseName, resourceId, args);
}
/// <summary>
/// Creates an instance of ErrorDetails specifying a Message.
/// This variant is used by CmdletProviders.
/// </summary>
/// <param name="resourceSupplier">
/// Resource supplier, most often an instance of
/// <see cref="Provider.CmdletProvider"/>.
/// </param>
/// <param name="baseName">by default, the
/// <see cref="System.Resources.ResourceManager"/>
/// name</param>
/// <param name="resourceId">
/// by default, the resourceId in the
/// <see cref="System.Resources.ResourceManager"/>
/// </param>
/// <param name="args">
/// <see cref="System.String.Format(IFormatProvider,string,object[])"/>
/// insertion parameters
/// </param>
/// <remarks>
/// This variant is a shortcut to build an instance of
/// <see cref="System.Management.Automation.ErrorDetails"/>
/// reducing the steps which localizable code generally has to duplicate when it
/// generates a localizable string. This variant is preferred over
/// <see cref="System.Management.Automation.ErrorDetails(string)"/>,
/// since the improved
/// information about the error may help enable future scenarios.
///
/// This constructor first loads a template string using
/// <see cref="System.Management.Automation.IResourceSupplier.GetResourceString"/>.
/// The default implementation of
/// <see cref="Provider.CmdletProvider.GetResourceString"/>
/// will load a string resource from the CmdletProvider assembly using
/// <paramref name="baseName"/> and <paramref name="resourceId"/>;
/// however, specific CmdletProviders can override this behavior
/// by overriding virtual method
/// <see cref="Provider.CmdletProvider.GetResourceString"/>,
/// and it is also possible that PSSnapin custom classes
/// which are not instances of
/// <see cref="Provider.CmdletProvider"/>
/// will implement
/// <see cref="IResourceSupplier"/>.
/// The constructor then inserts the specified args using
/// <see cref="System.String.Format(IFormatProvider,string,object[])"/>.
/// </remarks>
public ErrorDetails(
IResourceSupplier resourceSupplier,
string baseName,
string resourceId,
params object[] args)
{
_message = BuildMessage(resourceSupplier, baseName, resourceId, args);
}
/// <summary>
/// Creates an instance of ErrorDetails specifying a Message.
/// This variant is used by other code without a reference to
/// a <see cref="Cmdlet"/> or <see cref="Provider.CmdletProvider"/> instance.
/// </summary>
/// <param name="assembly">
/// assembly containing the template string
/// </param>
/// <param name="baseName">by default, the
/// <see cref="System.Resources.ResourceManager"/>
/// name</param>
/// <param name="resourceId">
/// by default, the resourceId in the
/// <see cref="System.Resources.ResourceManager"/>
/// </param>
/// <param name="args">
/// <see cref="System.String.Format(IFormatProvider,string,object[])"/>
/// insertion parameters
/// </param>
/// <remarks>
/// This variant is a shortcut to build an instance of
/// <see cref="System.Management.Automation.ErrorDetails"/>
/// reducing the steps which localizable code generally has to duplicate when it
/// generates a localizable string. This variant is preferred over
/// <see cref="System.Management.Automation.ErrorDetails(string)"/>,
/// since the improved
/// information about the error may help enable future scenarios.
///
/// This constructor first loads a template string from the assembly using
/// <see cref="System.Resources.ResourceManager.GetString(string)"/>.
/// The constructor then inserts the specified args using
/// <see cref="System.String.Format(IFormatProvider,string,object[])"/>.
/// </remarks>
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
/// <summary>
/// Initializes a new instance of the ErrorDetails class
/// using data serialized via
/// <see cref="ISerializable"/>
/// </summary>
/// <param name="info">Serialization information.</param>
/// <param name="context">Streaming context.</param>
/// <returns>Constructed object.</returns>
protected ErrorDetails(SerializationInfo info,
StreamingContext context)
{
_message = info.GetString("ErrorDetails_Message");
_recommendedAction = info.GetString(
"ErrorDetails_RecommendedAction");
}
/// <summary>
/// Serializer for <see cref="ISerializable"/>
/// </summary>
/// <param name="info">Serialization information.</param>
/// <param name="context">Streaming context.</param>
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
/// <summary>
/// Message which replaces
/// <see cref="System.Exception.Message"/> in
/// <see cref="System.Management.Automation.ErrorRecord.Exception"/>
/// </summary>
/// <remarks>
/// When an instance of
/// <see cref="System.Management.Automation.ErrorRecord"/>
/// contains a non-null
/// <see cref="System.Management.Automation.ErrorRecord.ErrorDetails"/>
/// and
/// <see cref="System.Management.Automation.ErrorDetails.Message"/>
/// is non-empty, the default host will display it instead of
/// the <see cref="System.Exception.Message"/> in
/// <see cref="System.Management.Automation.ErrorRecord.Exception"/>.
///
/// This should be a grammatically correct localized text string, as with
/// <see cref="System.Exception.Message"/>
/// </remarks>
public string Message
{
get { return ErrorRecord.NotNull(_message); }
}
private readonly string _message = string.Empty;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// This should be a grammatically correct localized text string.
/// This may be left empty.
/// </remarks>
public string RecommendedAction
{
get
{
return ErrorRecord.NotNull(_recommendedAction);
}
set
{
_recommendedAction = value;
}
}
private string _recommendedAction = string.Empty;
#endregion Public Properties
#region Internal Properties
internal Exception TextLookupError
{
get { return _textLookupError; }
set { _textLookupError = value; }
}
private Exception _textLookupError /* = null */;
#endregion Internal Properties
#region ToString
/// <summary>
/// As <see cref="System.Object.ToString()"/>
/// </summary>
/// <returns>Developer-readable identifier.</returns>
public override string ToString()
{
return Message;
}
#endregion ToString
#region Private
private string BuildMessage(
Cmdlet cmdlet,
string baseName,
string resourceId,
params object[] args)
{
if (cmdlet == null)
{
throw PSTraceSource.NewArgumentNullException(nameof(cmdlet));
}
if (string.IsNullOrEmpty(baseName))
{
throw PSTraceSource.NewArgumentNullException(nameof(baseName));
}
if (string.IsNullOrEmpty(resourceId))
{
throw PSTraceSource.NewArgumentNullException(nameof(resourceId));
}
string template = string.Empty;
try
{
template = cmdlet.GetResourceString(baseName, resourceId);
}
catch (MissingManifestResourceException e)
{
_textLookupError = e;
return string.Empty; // fallback to Exception.Message
}
catch (ArgumentException e)
{
_textLookupError = e;
return string.Empty; // fallback to Exception.Message
}
return BuildMessage(template, baseName, resourceId, args);
}
private string BuildMessage(
IResourceSupplier resourceSupplier,
string baseName,
string resourceId,
params object[] args)
{
if (resourceSupplier == null)
{
throw PSTraceSource.NewArgumentNullException(nameof(resourceSupplier));
}
if (string.IsNullOrEmpty(baseName))
{
throw PSTraceSource.NewArgumentNullException(nameof(baseName));
}
if (string.IsNullOrEmpty(resourceId))
{
throw PSTraceSource.NewArgumentNullException(nameof(resourceId));
}
string template = string.Empty;
try
{
template = resourceSupplier.GetResourceString(baseName, resourceId);
}
catch (MissingManifestResourceException e)
{
_textLookupError = e;
return string.Empty; // fallback to Exception.Message
}
catch (ArgumentException e)
{
_textLookupError = e;
return string.Empty; // fallback to Exception.Message
}
return BuildMessage(template, baseName, resourceId, args);
}
private string BuildMessage(
System.Reflection.Assembly assembly,
string baseName,
string resourceId,
params object[] args)
{
if (assembly == null)
{
throw PSTraceSource.NewArgumentNullException(nameof(assembly));
}
if (string.IsNullOrEmpty(baseName))
{
throw PSTraceSource.NewArgumentNullException(nameof(baseName));
}
if (string.IsNullOrEmpty(resourceId))
{
throw PSTraceSource.NewArgumentNullException(nameof(resourceId));
}
string template = string.Empty;
ResourceManager manager =
ResourceManagerCache.GetResourceManager(
assembly, baseName);
try
{
template = manager.GetString(
resourceId,
CultureInfo.CurrentUICulture);
}
catch (MissingManifestResourceException e)
{
_textLookupError = e;
return string.Empty; // fallback to Exception.Message
}
return BuildMessage(template, baseName, resourceId, args);
}
private string BuildMessage(
string template,
string baseName,
string resourceId,
params object[] args)
{
if (string.IsNullOrWhiteSpace(template))
{
_textLookupError = PSTraceSource.NewInvalidOperationException(
ErrorPackage.ErrorDetailsEmptyTemplate,
baseName,
resourceId);
return string.Empty; // fallback to Exception.Message
}
try
{
return string.Format(
CultureInfo.CurrentCulture,
template,
args);
}
catch (FormatException e)
{
_textLookupError = e;
return string.Empty; // fallback to Exception.Message
}
}
#endregion Private
}
/// <summary>
/// Represents an error.
/// </summary>
/// <remarks>
/// An ErrorRecord describes an error. It extends the usual information
/// in <see cref="System.Exception"/> with the additional information in
/// <see cref="System.Management.Automation.ErrorRecord.ErrorDetails"/>,
/// <see cref="System.Management.Automation.ErrorRecord.TargetObject"/>,
/// <see cref="System.Management.Automation.ErrorRecord.CategoryInfo"/>,
/// <see cref="System.Management.Automation.ErrorRecord.FullyQualifiedErrorId"/>,
/// <see cref="System.Management.Automation.ErrorRecord.ErrorDetails"/>, and
/// <see cref="System.Management.Automation.ErrorRecord.InvocationInfo"/>.
/// Non-terminating errors are stored as
/// <see cref="System.Management.Automation.ErrorRecord"/>
/// instances in shell variable
/// $error.
///
/// Some terminating errors implement
/// <see cref="System.Management.Automation.IContainsErrorRecord"/>
/// which gives them an ErrorRecord property containing this additional
/// information. In this case, ErrorRecord.Exception will be an instance of
/// <see cref="System.Management.Automation.ParentContainsErrorRecordException"/>.
/// rather than the actual exception, to avoid the mutual references.
/// </remarks>
public class ErrorRecord : ISerializable
{
#region Constructor
private ErrorRecord()
{
}
/// <summary>
/// Creates an instance of ErrorRecord.
/// </summary>
/// <param name="exception">
/// 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.
/// </param>
/// <param name="errorId">
/// 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.
/// </param>
/// <param name="errorCategory">
/// This is the ErrorCategory which best describes the error.
/// </param>
/// <param name="targetObject">
/// This is the object against which the cmdlet or provider
/// was operating when the error occurred. This is optional.
/// </param>
public ErrorRecord(
Exception exception,
string errorId,
ErrorCategory errorCategory,
object targetObject)
{
if (exception == null)
{
throw PSTraceSource.NewArgumentNullException(nameof(exception));
}
errorId ??= string.Empty;
// 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.
/// <summary>
/// Initializes a new instance of the ErrorRecord class
/// using data serialized via
/// <see cref="ISerializable"/>
/// </summary>
/// <param name="info">Serialization information.</param>
/// <param name="context">Streaming context.</param>
/// <returns>Constructed object.</returns>
/// <remarks>
/// ErrorRecord instances which are serialized using
/// <see cref="ISerializable"/>
/// will only be partially reconstructed.
/// </remarks>
protected ErrorRecord(SerializationInfo info,
StreamingContext context)
{
PSObject psObject = PSObject.ConstructPSObjectFromSerializationInfo(info, context);
ConstructFromPSObjectForRemoting(psObject);
}
/// <summary>
/// Deserializer for <see cref="ISerializable"/>
/// </summary>
/// <param name="info">Serialization information.</param>
/// <param name="context">Streaming context.</param>
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
/// <summary>
/// IsSerialized is set to true if this error record is serialized.
/// </summary>
private bool _isSerialized = false;
/// <summary>
/// Is this instance serialized.
/// </summary>
internal bool IsSerialized { get => _isSerialized; }
/// <summary>
/// Value for FullyQualifiedErrorId in case of serialized error record.
/// </summary>
private string _serializedFullyQualifiedErrorId = null;
/// <summary>
/// Message overridee for CategoryInfo.GetMessage method.
/// </summary>
internal string _serializedErrorCategoryMessageOverride = null;
/// <summary>
/// This constructor is used by remoting code to create ErrorRecord.
/// Various information is obtained from serialized ErrorRecord.
/// </summary>
/// <param name="exception"></param>
/// <param name="targetObject"></param>
/// <param name="fullyQualifiedErrorId"></param>
/// <param name="errorCategory"></param>
/// <param name="errorCategory_Activity"></param>
/// <param name="errorCategory_Reason"></param>
/// <param name="errorCategory_TargetName"></param>
/// <param name="errorCategory_TargetType"></param>
/// <param name="errorCategory_Message"></param>
/// <param name="errorDetails_Message"></param>
/// <param name="errorDetails_RecommendedAction"></param>
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,
errorCategory_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(nameof(exception));
}
if (fullyQualifiedErrorId == null)
{
throw PSTraceSource.NewArgumentNullException(nameof(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;
}
/// <summary>
/// Adds the information about this error record to PSObject as notes.
/// </summary>
/// <returns></returns>
internal void ToPSObjectForRemoting(PSObject dest)
{
ToPSObjectForRemoting(dest, SerializeExtendedInfo);
}
private void ToPSObjectForRemoting(PSObject dest, bool serializeExtInfo)
{
RemotingEncoder.AddNoteProperty<Exception>(dest, "Exception", () => Exception);
RemotingEncoder.AddNoteProperty<object>(dest, "TargetObject", () => TargetObject);
RemotingEncoder.AddNoteProperty<string>(dest, "FullyQualifiedErrorId", () => FullyQualifiedErrorId);
RemotingEncoder.AddNoteProperty<InvocationInfo>(dest, "InvocationInfo", () => InvocationInfo);
RemotingEncoder.AddNoteProperty<int>(dest, "ErrorCategory_Category", () => (int)CategoryInfo.Category);
RemotingEncoder.AddNoteProperty<string>(dest, "ErrorCategory_Activity", () => CategoryInfo.Activity);
RemotingEncoder.AddNoteProperty<string>(dest, "ErrorCategory_Reason", () => CategoryInfo.Reason);
RemotingEncoder.AddNoteProperty<string>(dest, "ErrorCategory_TargetName", () => CategoryInfo.TargetName);
RemotingEncoder.AddNoteProperty<string>(dest, "ErrorCategory_TargetType", () => CategoryInfo.TargetType);
RemotingEncoder.AddNoteProperty<string>(dest, "ErrorCategory_Message", () => CategoryInfo.GetMessage(CultureInfo.CurrentCulture));
if (ErrorDetails != null)
{
RemotingEncoder.AddNoteProperty<string>(dest, "ErrorDetails_Message", () => ErrorDetails.Message);
RemotingEncoder.AddNoteProperty<string>(dest, "ErrorDetails_RecommendedAction", () => ErrorDetails.RecommendedAction);
}
if (!serializeExtInfo || this.InvocationInfo == null)
{
RemotingEncoder.AddNoteProperty(dest, "SerializeExtendedInfo", () => false);
}
else
{
RemotingEncoder.AddNoteProperty(dest, "SerializeExtendedInfo", () => true);
this.InvocationInfo.ToPSObjectForRemoting(dest);
RemotingEncoder.AddNoteProperty<object>(dest, "PipelineIterationInfo", () => PipelineIterationInfo);
}
if (!string.IsNullOrEmpty(this.ScriptStackTrace))
{
RemotingEncoder.AddNoteProperty(dest, "ErrorDetails_ScriptStackTrace", () => this.ScriptStackTrace);
}
}
/// <summary>
/// Gets the value for note from mshObject.
/// </summary>
/// <param name="mshObject">
/// PSObject from which value is fetched.
/// </param>
/// <param name="note">
/// name of note whose value is fetched
/// </param>
/// <returns>
/// value of note
/// </returns>
private static object GetNoteValue(PSObject mshObject, string note)
{
if (mshObject.Properties[note] is PSNoteProperty p)
{
return p.Value;
}
else
{
return null;
}
}
/// <summary>
/// Create an ErrorRecord object from serialized ErrorRecord.
/// serializedErrorRecord PSObject is in the format returned
/// by ToPSObjectForRemoting method.
/// </summary>
/// <param name="serializedErrorRecord">
/// PSObject to convert to ErrorRecord
/// </param>
/// <returns>
/// ErrorRecord convert from mshObject.
/// </returns>
/// <exception cref="ArgumentNullException">
/// Thrown if mshObject parameter is null.
/// </exception>
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(nameof(serializedErrorRecord));
}
// Get Exception
PSObject serializedException = RemotingDecoder.GetPropertyValue<PSObject>(serializedErrorRecord, "Exception");
// Get Target object
object targetObject = RemotingDecoder.GetPropertyValue<object>(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<string>(serializedErrorRecord, "FullyQualifiedErrorId") ??
"fullyQualifiedErrorId";
// Get ErrorCategory...
ErrorCategory errorCategory = RemotingDecoder.GetPropertyValue<ErrorCategory>(serializedErrorRecord, "errorCategory_Category");
// Get Various ErrorCategory fileds
string errorCategory_Activity = RemotingDecoder.GetPropertyValue<string>(serializedErrorRecord, "ErrorCategory_Activity");
string errorCategory_Reason = RemotingDecoder.GetPropertyValue<string>(serializedErrorRecord, "ErrorCategory_Reason");
string errorCategory_TargetName = RemotingDecoder.GetPropertyValue<string>(serializedErrorRecord, "ErrorCategory_TargetName");
string errorCategory_TargetType = RemotingDecoder.GetPropertyValue<string>(serializedErrorRecord, "ErrorCategory_TargetType");
string errorCategory_Message = RemotingDecoder.GetPropertyValue<string>(serializedErrorRecord, "ErrorCategory_Message");
// Get InvocationInfo (optional property)
PSObject invocationInfo = Microsoft.PowerShell.DeserializingTypeConverter.GetPropertyValue<PSObject>(
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)) ? 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<bool>(serializedErrorRecord, "SerializeExtendedInfo");
if (_serializeExtendedInfo)
{
_invocationInfo = new InvocationInfo(serializedErrorRecord);
ArrayList iterationInfo = RemotingDecoder.GetPropertyValue<ArrayList>(serializedErrorRecord, "PipelineIterationInfo");
if (iterationInfo != null)
{
_pipelineIterationInfo = new ReadOnlyCollection<int>((int[])iterationInfo.ToArray(typeof(Int32)));
}
}
else
{
_invocationInfo = null;
}
}
#endregion Remoting
/// <summary>
/// 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.
/// </summary>
/// <param name="errorRecord">Wrapped ErrorRecord.</param>
/// <param name="replaceParentContainsErrorRecordException">
/// If the wrapped exception contains a ParentContainsErrorRecordException, the new
/// ErrorRecord should have this exception as its Exception instead.
/// </param>
public ErrorRecord(ErrorRecord errorRecord,
Exception replaceParentContainsErrorRecordException)
{
if (errorRecord == null)
{
throw new PSArgumentNullException(nameof(errorRecord));
}
if (replaceParentContainsErrorRecordException != null
&& (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 (errorRecord.ErrorDetails != null)
{
ErrorDetails = new ErrorDetails(errorRecord.ErrorDetails);
}
SetInvocationInfo(errorRecord._invocationInfo);
_scriptStackTrace = errorRecord._scriptStackTrace;
_serializedFullyQualifiedErrorId = errorRecord._serializedFullyQualifiedErrorId;
}
#endregion Constructor
#region Override
/// <summary>
/// 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.
/// </summary>
/// <param name="replaceParentContainsErrorRecordException">
/// If the wrapped exception contains a ParentContainsErrorRecordException, the new
/// ErrorRecord should have this exception as its Exception instead.
/// </param>
/// <returns></returns>
internal virtual ErrorRecord WrapException(Exception replaceParentContainsErrorRecordException)
{
return new ErrorRecord(this, replaceParentContainsErrorRecordException);
}
#endregion Override
#region Public Properties
/// <summary>
/// An Exception describing the error.
/// </summary>
/// <value>never null</value>
public Exception Exception
{
get
{
Diagnostics.Assert(_error != null, "_error is null");
return _error;
}
}
private Exception _error /* = null */;
/// <summary>
/// The object against which the error occurred.
/// </summary>
/// <value>may be null</value>
public object TargetObject { get => _target; }
private object _target /* = null */;
internal void SetTargetObject(object target)
{
_target = target;
}
/// <summary>
/// Information regarding the ErrorCategory
/// associated with this error, and with the categorized error message
/// for that ErrorCategory.
/// </summary>
/// <value>never null</value>
public ErrorCategoryInfo CategoryInfo { get => _categoryInfo ??= new ErrorCategoryInfo(this); }
private ErrorCategoryInfo _categoryInfo;
/// <summary>
/// String which uniquely identifies this error condition.
/// </summary>
/// <value>never null</value>
/// <remarks>
/// 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.
/// </remarks>
public string FullyQualifiedErrorId
{
get
{
if (_serializedFullyQualifiedErrorId != null)
{
return _serializedFullyQualifiedErrorId;
}
string typeName = GetInvocationTypeName();
string delimiter =
(string.IsNullOrEmpty(typeName) || string.IsNullOrEmpty(_errorId))
? string.Empty
: ",";
return NotNull(_errorId) + delimiter + NotNull(typeName);
}
}
/// <summary>
/// Additional information about the error.
/// </summary>
/// <value>may be null</value>
/// <remarks>
/// In particular, ErrorDetails.Message (if present and non-empty)
/// contains a replacement message which should be displayed instead of
/// Exception.Message.
/// </remarks>
public ErrorDetails ErrorDetails { get; set; }
/// <summary>
/// Identifies the cmdlet, script, or other command which caused
/// the error.
/// </summary>
/// <value>may be null</value>
public InvocationInfo InvocationInfo { get => _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<int>(snapshot);
}
}
// 2005/07/14-913791 "write-error output is confusing and misleading"
internal bool PreserveInvocationInfoOnce { get; set; }
/// <summary>
/// The script stack trace for the error.
/// </summary>
public string ScriptStackTrace { get => _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();
}
}
/// <summary>
/// The status of the pipeline when this record was created.
/// </summary>
public ReadOnlyCollection<int> PipelineIterationInfo { get => _pipelineIterationInfo; }
private ReadOnlyCollection<int> _pipelineIterationInfo = Utils.EmptyReadOnlyCollection<int>();
/// <summary>
/// Whether to serialize the InvocationInfo during remote calls.
/// </summary>
internal bool SerializeExtendedInfo
{
get => _serializeExtendedInfo;
set => _serializeExtendedInfo = value;
}
private bool _serializeExtendedInfo = false;
#endregion Public Properties
#region Private
private readonly 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) => s ?? string.Empty;
private string GetInvocationTypeName()
{
InvocationInfo invocationInfo = this.InvocationInfo;
if (invocationInfo == null)
{
return string.Empty;
}
CommandInfo commandInfo = invocationInfo.MyCommand;
if (commandInfo == null)
{
return string.Empty;
}
IScriptCommandInfo scriptInfo = commandInfo as IScriptCommandInfo;
if (scriptInfo != null)
{
return commandInfo.Name;
}
if (!(commandInfo is CmdletInfo cmdletInfo))
{
return string.Empty;
}
return cmdletInfo.ImplementingType.FullName;
}
#endregion Private
#region ToString
/// <summary>
/// As <see cref="System.Object.ToString()"/>
/// </summary>
/// <returns>Developer-readable identifier.</returns>
public override string ToString()
{
if (ErrorDetails != null && !string.IsNullOrEmpty(ErrorDetails.Message))
{
return ErrorDetails.Message;
}
if (Exception != null)
{
if (!string.IsNullOrEmpty(Exception.Message))
{
return Exception.Message;
}
return Exception.ToString();
}
return base.ToString();
}
#endregion ToString
}
/// <summary>
/// Dummy generic class for type inference purposes on typed catch blocks.
/// </summary>
/// <typeparam name="TException">Anything that inherits Exception.</typeparam>
internal class ErrorRecord<TException> : ErrorRecord where TException : Exception
{
public new TException Exception { get; }
public ErrorRecord(Exception exception, string errorId, ErrorCategory errorCategory, object targetObject) : base(exception, errorId, errorCategory, targetObject)
{
}
}
/// <summary>
/// Implemented by exception classes which contain additional
/// <see cref="System.Management.Automation.ErrorRecord"/>
/// information.
/// </summary>
/// <remarks>
/// PowerShell defines certain exception classes which implement this interface.
/// This includes wrapper exceptions such as
/// <see cref="System.Management.Automation.CmdletInvocationException"/>,
/// and also PowerShell engine errors such as
/// <see cref="System.Management.Automation.GetValueException"/>.
/// 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
/// <see cref="System.Management.Automation.ParentContainsErrorRecordException"/>
/// 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
/// <see cref="IContainsErrorRecord"/>,
/// but it is generally preferable for Cmdlets and CmdletProviders to communicate
/// <see cref="ErrorRecord"/>
/// information using
/// <see cref="Cmdlet.ThrowTerminatingError"/>
/// or
/// <see cref="Provider.CmdletProvider.ThrowTerminatingError"/>
/// rather than by throwing an exception which implements
/// <see cref="IContainsErrorRecord"/>.
/// Consider implementing
/// <seealso cref="IContainsErrorRecord"/>
/// in your custom exception only if you throw it from a context
/// where a reference to the active
/// <seealso cref="Cmdlet"/> or
/// <seealso cref="Provider.CmdletProvider"/>
/// is no longer available.
/// </remarks>
#nullable enable
public interface IContainsErrorRecord
{
/// <summary>
/// This is the
/// <see cref="ErrorRecord"/>
/// which provides additional information about the error.
/// </summary>
/// <remarks>
/// The <see cref="ErrorRecord"/> instance returned by
/// <see cref="IContainsErrorRecord.ErrorRecord"/>
/// should contain in its
/// <see cref="System.Management.Automation.ErrorRecord.Exception"/>
/// property an instance of
/// <see cref="ParentContainsErrorRecordException"/>
/// rather than a reference to the root exception. This prevents
/// a recursive reference between the exception implementing
/// <see cref="IContainsErrorRecord"/> and the
/// <see cref="ErrorRecord"/>.
/// Use the
/// <see cref="ParentContainsErrorRecordException(Exception)"/>
/// constructor so that the
/// <see cref="ParentContainsErrorRecordException"/>
/// will have the same
/// <see cref="System.Exception.Message"/>
/// as the root exception.
/// </remarks>
ErrorRecord ErrorRecord { get; }
}
#nullable restore
/// <summary>
/// Objects implementing this interface can be used by
/// <see cref="System.Management.Automation.ErrorDetails(IResourceSupplier,string,string,object[])"/>
/// </summary>
/// <remarks>
/// <see cref="Provider.CmdletProvider"/>
/// implements this interface. PSSnapins can implement
/// <see cref="IResourceSupplier"/>
/// on their custom classes, but the only purpose would be to permit
/// the custom class to be used in the
/// <see cref="ErrorDetails(IResourceSupplier,string,string,object[])"/>.
/// constructor.
/// <see cref="ErrorDetails"/> contains special constructor
/// <see cref="ErrorDetails(IResourceSupplier,string,string,object[])"/>
/// reducing the steps which localizable code generally has to duplicate when it
/// generates a localizable string. This variant is preferred over
/// <see cref="ErrorDetails(string)"/>,
/// since the improved
/// information about the error may help enable future scenarios.
/// </remarks>
#nullable enable
public interface IResourceSupplier
{
/// <summary>
/// Gets the error message template string corresponding to
/// <paramref name="baseName"/> and <paramref name="resourceId"/>.
/// </summary>
/// <remarks>
/// If the desired behavior is simple string lookup
/// in your assembly, you can use the
/// <see cref="ErrorDetails(Assembly,string,string,object[])"/>
/// constructor instead and not bother implementing
/// <see cref="IResourceSupplier"/>.
/// Consider implementing <see cref="IResourceSupplier"/>
/// if you want more complex behavior.
///
/// Insertions will be inserted into the string with
/// <see cref="System.String.Format(IFormatProvider,string,object[])"/>
/// to generate the final error message in
/// <see cref="ErrorDetails.Message"/>.
/// </remarks>
/// <param name="baseName">The base resource name.</param>
/// <param name="resourceId">The resource id.</param>
/// <returns>The error message template string corresponding to baseName and resourceId.</returns>
string GetResourceString(string baseName, string resourceId);
}
}
#pragma warning restore 56506