/********************************************************************++ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ using System.Diagnostics; using System.Reflection; using System.Collections; using System.Globalization; using System.Management.Automation.Internal; using System.Text; using System.Text.RegularExpressions; using Microsoft.Win32; namespace System.Management.Automation { /// /// Encapsulates $PSVersionTable. /// internal class PSVersionInfo { internal const string PSVersionTableName = "PSVersionTable"; internal const string PSRemotingProtocolVersionName = "PSRemotingProtocolVersion"; internal const string PSVersionName = "PSVersion"; internal const string PSEditionName = "PSEdition"; internal const string PSGitCommitIdName = "GitCommitId"; internal const string PSCompatibleVersionsName = "PSCompatibleVersions"; internal const string PSPlatformName = "Platform"; internal const string PSOSName = "OS"; internal const string SerializationVersionName = "SerializationVersion"; internal const string WSManStackVersionName = "WSManStackVersion"; private static PSVersionHashTable s_psVersionTable = null; /// /// A constant to track current PowerShell Version. /// /// /// We can't depend on assembly version for PowerShell version. /// /// This is why we hard code the PowerShell version here. /// /// For each later release of PowerShell, this constant needs to /// be updated to reflect the right version. /// private static Version s_psV1Version = new Version(1, 0); private static Version s_psV2Version = new Version(2, 0); private static Version s_psV3Version = new Version(3, 0); private static Version s_psV4Version = new Version(4, 0); private static Version s_psV5Version = new Version(5, 0); private static Version s_psV51Version = new Version(5, 1, NTVerpVars.PRODUCTBUILD, NTVerpVars.PRODUCTBUILD_QFE); private static SemanticVersion s_psV6Version; /// /// A constant to track current PowerShell Edition /// internal const string PSEditionValue = "Core"; // Static Constructor. static PSVersionInfo() { s_psVersionTable = new PSVersionHashTable(StringComparer.OrdinalIgnoreCase); string assemblyPath = typeof(PSVersionInfo).Assembly.Location; string productVersion = FileVersionInfo.GetVersionInfo(assemblyPath).ProductVersion; // Get 'GitCommitId' and 'PSVersion' from the 'productVersion' assembly attribute. // // The strings can be one of the following format examples: // when powershell is built from a commit: // productVersion = '6.0.0-beta.7 Commits: 29 SHA: 52c6b...' convert to GitCommitId = 'v6.0.0-beta.7-29-g52c6b...' // PSVersion = '6.0.0-beta.7' // when powershell is built from a release tag: // productVersion = '6.0.0-beta.7 SHA: f1ec9...' convert to GitCommitId = 'v6.0.0-beta.7' // PSVersion = '6.0.0-beta.7' // when powershell is built from a release tag for RTM: // productVersion = '6.0.0 SHA: f1ec9...' convert to GitCommitId = 'v6.0.0' // PSVersion = '6.0.0' string rawGitCommitId; string mainVersion = productVersion.Substring(0, productVersion.IndexOf(' ')); if (productVersion.Contains(" Commits: ")) { rawGitCommitId = "v" + productVersion.Replace(" Commits: ", "-").Replace(" SHA: ", "-g"); } else { rawGitCommitId = "v" + mainVersion; } s_psV6Version = new SemanticVersion(mainVersion); s_psVersionTable[PSVersionInfo.PSVersionName] = s_psV6Version; s_psVersionTable[PSVersionInfo.PSEditionName] = PSEditionValue; s_psVersionTable[PSGitCommitIdName] = rawGitCommitId; s_psVersionTable[PSCompatibleVersionsName] = new Version[] { s_psV1Version, s_psV2Version, s_psV3Version, s_psV4Version, s_psV5Version, s_psV51Version, s_psV6Version }; s_psVersionTable[PSVersionInfo.SerializationVersionName] = new Version(InternalSerializer.DefaultVersion); s_psVersionTable[PSVersionInfo.PSRemotingProtocolVersionName] = RemotingConstants.ProtocolVersion; s_psVersionTable[PSVersionInfo.WSManStackVersionName] = GetWSManStackVersion(); s_psVersionTable[PSPlatformName] = Environment.OSVersion.Platform.ToString(); s_psVersionTable[PSOSName] = Runtime.InteropServices.RuntimeInformation.OSDescription.ToString(); } internal static PSVersionHashTable GetPSVersionTable() { return s_psVersionTable; } internal static Hashtable GetPSVersionTableForDownLevel() { var result = (Hashtable)s_psVersionTable.Clone(); // Downlevel systems don't support SemanticVersion, but Version is most likely good enough anyway. result[PSVersionInfo.PSVersionName] = (Version)(SemanticVersion)s_psVersionTable[PSVersionInfo.PSVersionName]; return result; } #region Private helper methods // Gets the current WSMan stack version from the registry. private static Version GetWSManStackVersion() { Version version = null; #if !UNIX try { using (RegistryKey wsManStackVersionKey = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\WSMAN")) { if (wsManStackVersionKey != null) { object wsManStackVersionObj = wsManStackVersionKey.GetValue("ServiceStackVersion"); string wsManStackVersion = (wsManStackVersionObj != null) ? (string)wsManStackVersionObj : null; if (!string.IsNullOrEmpty(wsManStackVersion)) { version = new Version(wsManStackVersion.Trim()); } } } } catch (ObjectDisposedException) { } catch (System.Security.SecurityException) { } catch (ArgumentException) { } catch (System.IO.IOException) { } catch (UnauthorizedAccessException) { } catch (FormatException) { } catch (OverflowException) { } catch (InvalidCastException) { } #endif return version ?? System.Management.Automation.Remoting.Client.WSManNativeApi.WSMAN_STACK_VERSION; } #endregion #region Programmer APIs internal static Version PSVersion { get { return (SemanticVersion)GetPSVersionTable()[PSVersionInfo.PSVersionName]; } } internal static string GitCommitId { get { return (string)GetPSVersionTable()[PSGitCommitIdName]; } } internal static Version[] PSCompatibleVersions { get { return (Version[])GetPSVersionTable()[PSCompatibleVersionsName]; } } internal static string PSEdition { get { return (string)GetPSVersionTable()[PSVersionInfo.PSEditionName]; } } internal static Version SerializationVersion { get { return (Version)GetPSVersionTable()[SerializationVersionName]; } } /// /// /// /// /// For 2.0 PowerShell, we still use "1" as the registry version key. /// For >=3.0 PowerShell, we still use "1" as the registry version key for /// Snapin and Custom shell lookup/discovery. /// internal static string RegistryVersion1Key { get { return "1"; } } /// /// /// /// /// For 3.0 PowerShell, we use "3" as the registry version key only for Engine /// related data like ApplicationBase. /// For 3.0 PowerShell, we still use "1" as the registry version key for /// Snapin and Custom shell lookup/discovery. /// internal static string RegistryVersionKey { get { // PowerShell >=4 is compatible with PowerShell 3 and hence reg key is 3. return "3"; } } internal static string GetRegistryVersionKeyForSnapinDiscovery(string majorVersion) { int tempMajorVersion = 0; LanguagePrimitives.TryConvertTo(majorVersion, out tempMajorVersion); if ((tempMajorVersion >= 1) && (tempMajorVersion <= PSVersionInfo.PSVersion.Major)) { // PowerShell version 3 took a dependency on CLR4 and went with: // SxS approach in GAC/Registry and in-place upgrade approach for // FileSystem. // For >=3.0 PowerShell, we still use "1" as the registry version key for // Snapin and Custom shell lookup/discovery. return "1"; } return null; } internal static string FeatureVersionString { get { return String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}.{1}", PSVersionInfo.PSVersion.Major, PSVersionInfo.PSVersion.Minor); } } internal static bool IsValidPSVersion(Version version) { if (version.Major == s_psV6Version.Major) { return version.Minor == s_psV6Version.Minor; } if (version.Major == s_psV5Version.Major) { return (version.Minor == s_psV5Version.Minor || version.Minor == s_psV51Version.Minor); } if (version.Major == s_psV4Version.Major) { return (version.Minor == s_psV4Version.Minor); } else if (version.Major == s_psV3Version.Major) { return version.Minor == s_psV3Version.Minor; } else if (version.Major == s_psV2Version.Major) { return version.Minor == s_psV2Version.Minor; } else if (version.Major == s_psV1Version.Major) { return version.Minor == s_psV1Version.Minor; } return false; } internal static Version PSV4Version { get { return s_psV4Version; } } internal static Version PSV5Version { get { return s_psV5Version; } } internal static Version PSV51Version { get { return s_psV51Version; } } internal static SemanticVersion PSV6Version { get { return s_psV6Version; } } #endregion } /// /// Represents an implementation of '$PSVersionTable' variable. /// The implementation contains ordered 'Keys' and 'GetEnumerator' to get user-friendly output. /// public sealed class PSVersionHashTable : Hashtable, IEnumerable { private static readonly PSVersionTableComparer s_keysComparer = new PSVersionTableComparer(); internal PSVersionHashTable(IEqualityComparer equalityComparer) : base(equalityComparer) { } /// /// Returns ordered collection with Keys of 'PSVersionHashTable' /// We want see special order: /// 1. PSVersionName /// 2. PSEditionName /// 3. Remaining properties in alphabetical order /// public override ICollection Keys { get { ArrayList keyList = new ArrayList(base.Keys); keyList.Sort(s_keysComparer); return keyList; } } private class PSVersionTableComparer : IComparer { public int Compare(object x, object y) { string xString = (string)LanguagePrimitives.ConvertTo(x, typeof(string), CultureInfo.CurrentCulture); string yString = (string)LanguagePrimitives.ConvertTo(y, typeof(string), CultureInfo.CurrentCulture); if (PSVersionInfo.PSVersionName.Equals(xString, StringComparison.OrdinalIgnoreCase)) { return -1; } else if (PSVersionInfo.PSVersionName.Equals(yString, StringComparison.OrdinalIgnoreCase)) { return 1; } else if (PSVersionInfo.PSEditionName.Equals(xString, StringComparison.OrdinalIgnoreCase)) { return -1; } else if (PSVersionInfo.PSEditionName.Equals(yString, StringComparison.OrdinalIgnoreCase)) { return 1; } else { return String.Compare(xString, yString, StringComparison.OrdinalIgnoreCase); } } } /// /// Returns an enumerator for 'PSVersionHashTable'. /// The enumeration is ordered (based on ordered version of 'Keys'). /// IEnumerator IEnumerable.GetEnumerator() { foreach (object key in Keys) { yield return new DictionaryEntry(key, this[key]); } } } /// /// An implementation of semantic versioning (http://semver.org) /// that can be converted to/from . /// /// When converting to , a PSNoteProperty is /// added to the instance to store the semantic version label so /// that it can be recovered when creating a new SemanticVersion. /// public sealed class SemanticVersion : IComparable, IComparable, IEquatable { private const string VersionSansRegEx = @"^(?\d+)(\.(?\d+))?(\.(?\d+))?$"; private const string LabelRegEx = @"^((?[0-9A-Za-z][0-9A-Za-z\-\.]*))?(\+(?[0-9A-Za-z][0-9A-Za-z\-\.]*))?$"; private const string LabelUnitRegEx = @"^[0-9A-Za-z][0-9A-Za-z\-\.]*$"; private const string PreLabelPropertyName = "PSSemVerPreReleaseLabel"; private const string BuildLabelPropertyName = "PSSemVerBuildLabel"; private const string TypeNameForVersionWithLabel = "System.Version#IncludeLabel"; private string versionString; /// /// Construct a SemanticVersion from a string. /// /// The version to parse /// /// public SemanticVersion(string version) { var v = SemanticVersion.Parse(version); Major = v.Major; Minor = v.Minor; Patch = v.Patch < 0 ? 0 : v.Patch; PreReleaseLabel = v.PreReleaseLabel; BuildLabel = v.BuildLabel; } /// /// Construct a SemanticVersion. /// /// The major version /// The minor version /// The patch version /// The pre-release label for the version /// The build metadata for the version /// /// If don't match 'LabelUnitRegEx'. /// If don't match 'LabelUnitRegEx'. /// public SemanticVersion(int major, int minor, int patch, string preReleaseLabel, string buildLabel) : this(major, minor, patch) { if (!string.IsNullOrEmpty(preReleaseLabel)) { if (!Regex.IsMatch(preReleaseLabel, LabelUnitRegEx)) throw new FormatException(nameof(preReleaseLabel)); PreReleaseLabel = preReleaseLabel; } if (!string.IsNullOrEmpty(buildLabel)) { if (!Regex.IsMatch(buildLabel, LabelUnitRegEx)) throw new FormatException(nameof(buildLabel)); BuildLabel = buildLabel; } } /// /// Construct a SemanticVersion. /// /// The major version /// The minor version /// The minor version /// The label for the version /// /// /// If don't match 'LabelRegEx'. /// public SemanticVersion(int major, int minor, int patch, string label) : this(major, minor, patch) { // We presume the SymVer : // 1) major.minor.patch-label // 2) 'label' starts with letter or digit. if (!string.IsNullOrEmpty(label)) { var match = Regex.Match(label, LabelRegEx); if (!match.Success) throw new FormatException(nameof(label)); PreReleaseLabel = match.Groups["preLabel"].Value; BuildLabel = match.Groups["buildLabel"].Value; } } /// /// Construct a SemanticVersion. /// /// The major version /// The minor version /// The minor version /// /// If , , or is less than 0. /// public SemanticVersion(int major, int minor, int patch) { if (major < 0) throw PSTraceSource.NewArgumentException(nameof(major)); if (minor < 0) throw PSTraceSource.NewArgumentException(nameof(minor)); if (patch < 0) throw PSTraceSource.NewArgumentException(nameof(patch)); Major = major; Minor = minor; Patch = patch; // We presume: // PreReleaseLabel = null; // BuildLabel = null; } /// /// Construct a SemanticVersion. /// /// The major version /// The minor version /// /// If or is less than 0. /// public SemanticVersion(int major, int minor) : this(major, minor, 0) {} /// /// Construct a SemanticVersion. /// /// The major version /// /// If is less than 0. /// public SemanticVersion(int major) : this(major, 0, 0) {} /// /// Construct a from a , /// copying the NoteProperty storing the label if the expected property exists. /// /// The version. /// /// If is null. /// /// /// If is more than 0. /// public SemanticVersion(Version version) { if (version == null) throw PSTraceSource.NewArgumentNullException(nameof(version)); if (version.Revision > 0) throw PSTraceSource.NewArgumentException(nameof(version)); Major = version.Major; Minor = version.Minor; Patch = version.Build == -1 ? 0 : version.Build; var psobj = new PSObject(version); var preLabelNote = psobj.Properties[PreLabelPropertyName]; if (preLabelNote != null) { PreReleaseLabel = preLabelNote.Value as string; } var buildLabelNote = psobj.Properties[BuildLabelPropertyName]; if (buildLabelNote != null) { BuildLabel = buildLabelNote.Value as string; } } /// /// Convert a to a . /// If there is a or/and a , /// it is added as a NoteProperty to the result so that you can round trip /// back to a without losing the label. /// /// public static implicit operator Version(SemanticVersion semver) { PSObject psobj; var result = new Version(semver.Major, semver.Minor, semver.Patch); if (!string.IsNullOrEmpty(semver.PreReleaseLabel) || !string.IsNullOrEmpty(semver.BuildLabel)) { psobj = new PSObject(result); if (!string.IsNullOrEmpty(semver.PreReleaseLabel)) { psobj.Properties.Add(new PSNoteProperty(PreLabelPropertyName, semver.PreReleaseLabel)); } if (!string.IsNullOrEmpty(semver.BuildLabel)) { psobj.Properties.Add(new PSNoteProperty(BuildLabelPropertyName, semver.BuildLabel)); } psobj.TypeNames.Insert(0, TypeNameForVersionWithLabel); } return result; } /// /// The major version number, never negative. /// public int Major { get; } /// /// The minor version number, never negative. /// public int Minor { get; } /// /// The patch version, -1 if not specified. /// public int Patch { get; } /// /// PreReleaseLabel position in the SymVer string 'major.minor.patch-PreReleaseLabel+BuildLabel'. /// public string PreReleaseLabel { get; } /// /// BuildLabel position in the SymVer string 'major.minor.patch-PreReleaseLabel+BuildLabel'. /// public string BuildLabel { get; } /// /// Parse and return the result if it is a valid , otherwise throws an exception. /// /// The string to parse /// /// /// /// public static SemanticVersion Parse(string version) { if (version == null) throw PSTraceSource.NewArgumentNullException(nameof(version)); if (version == String.Empty) throw new FormatException(nameof(version)); var r = new VersionResult(); r.Init(true); TryParseVersion(version, ref r); return r._parsedVersion; } /// /// Parse and return true if it is a valid , otherwise return false. /// No exceptions are raised. /// /// The string to parse /// The return value when the string is a valid public static bool TryParse(string version, out SemanticVersion result) { if (version != null) { var r = new VersionResult(); r.Init(false); if (TryParseVersion(version, ref r)) { result = r._parsedVersion; return true; } } result = null; return false; } private static bool TryParseVersion(string version, ref VersionResult result) { if (version.EndsWith('-') || version.EndsWith('+') || version.EndsWith('.')) { result.SetFailure(ParseFailureKind.FormatException); return false; } string versionSansLabel = null; var major=0; var minor=0; var patch=0; string preLabel = null; string buildLabel = null; // We parse the SymVer 'version' string 'major.minor.patch-PreReleaseLabel+BuildLabel'. var dashIndex = version.IndexOf('-'); var plusIndex = version.IndexOf('+'); if (dashIndex > plusIndex) { // 'PreReleaseLabel' can contains dashes. if (plusIndex == -1) { // No buildLabel: buildLabel == null // Format is 'major.minor.patch-PreReleaseLabel' preLabel = version.Substring(dashIndex+1); versionSansLabel = version.Substring(0, dashIndex); } else { // No PreReleaseLabel: preLabel == null // Format is 'major.minor.patch+BuildLabel' buildLabel = version.Substring(plusIndex+1); versionSansLabel = version.Substring(0, plusIndex); dashIndex = -1; } } else { if (dashIndex == -1) { // Here dashIndex == plusIndex == -1 // No preLabel - preLabel == null; // No buildLabel - buildLabel == null; // Format is 'major.minor.patch' versionSansLabel = version; } else { // Format is 'major.minor.patch-PreReleaseLabel+BuildLabel' preLabel = version.Substring(dashIndex+1, plusIndex-dashIndex-1); buildLabel = version.Substring(plusIndex+1); versionSansLabel = version.Substring(0, dashIndex); } } if ((dashIndex != - 1 && String.IsNullOrEmpty(preLabel)) || (plusIndex != - 1 && String.IsNullOrEmpty(buildLabel)) || String.IsNullOrEmpty(versionSansLabel)) { // We have dash and no preReleaseLabel or // we have plus and no buildLabel or // we have no main version part (versionSansLabel==null) result.SetFailure(ParseFailureKind.FormatException); return false; } var match = Regex.Match(versionSansLabel, VersionSansRegEx); if (!match.Success) { result.SetFailure(ParseFailureKind.FormatException); return false; } if (!int.TryParse(match.Groups["major"].Value, out major)) { result.SetFailure(ParseFailureKind.FormatException); return false; } if (match.Groups["minor"].Success && !int.TryParse(match.Groups["minor"].Value, out minor)) { result.SetFailure(ParseFailureKind.FormatException); return false; } if (match.Groups["patch"].Success && !int.TryParse(match.Groups["patch"].Value, out patch)) { result.SetFailure(ParseFailureKind.FormatException); return false; } if (preLabel != null && !Regex.IsMatch(preLabel, LabelUnitRegEx) || (buildLabel != null && !Regex.IsMatch(buildLabel, LabelUnitRegEx))) { result.SetFailure(ParseFailureKind.FormatException); return false; } result._parsedVersion = new SemanticVersion(major, minor, patch, preLabel, buildLabel); return true; } /// /// Implement ToString() /// public override string ToString() { if (versionString == null) { StringBuilder result = new StringBuilder(); result.Append(Major).Append(Utils.Separators.Dot).Append(Minor).Append(Utils.Separators.Dot).Append(Patch); if (!string.IsNullOrEmpty(PreReleaseLabel)) { result.Append("-").Append(PreReleaseLabel); } if (!string.IsNullOrEmpty(BuildLabel)) { result.Append("+").Append(BuildLabel); } versionString = result.ToString(); } return versionString; } /// /// Implement Compare. /// public static int Compare(SemanticVersion versionA, SemanticVersion versionB) { if (versionA != null) { return versionA.CompareTo(versionB); } if (versionB != null) { return -1; } return 0; } /// /// Implement /// public int CompareTo(object version) { if (version == null) { return 1; } var v = version as SemanticVersion; if (v == null) { throw PSTraceSource.NewArgumentException(nameof(version)); } return CompareTo(v); } /// /// Implement . /// Meets SymVer 2.0 p.11 http://semver.org/ /// public int CompareTo(SemanticVersion value) { if ((object)value == null) return 1; if (Major != value.Major) return Major > value.Major ? 1 : -1; if (Minor != value.Minor) return Minor > value.Minor ? 1 : -1; if (Patch != value.Patch) return Patch > value.Patch ? 1 : -1; // SymVer 2.0 standard requires to ignore 'BuildLabel' (Build metadata). return ComparePreLabel(this.PreReleaseLabel, value.PreReleaseLabel); } /// /// Override /// public override bool Equals(object obj) { return Equals(obj as SemanticVersion); } /// /// Implement /// public bool Equals(SemanticVersion other) { // SymVer 2.0 standard requires to ignore 'BuildLabel' (Build metadata). return other != null && (Major == other.Major) && (Minor == other.Minor) && (Patch == other.Patch) && string.Equals(PreReleaseLabel, other.PreReleaseLabel, StringComparison.Ordinal); } /// /// Override /// public override int GetHashCode() { return this.ToString().GetHashCode(); } /// /// Overloaded == operator /// public static bool operator ==(SemanticVersion v1, SemanticVersion v2) { if (object.ReferenceEquals(v1, null)) { return object.ReferenceEquals(v2, null); } return v1.Equals(v2); } /// /// Overloaded != operator /// public static bool operator !=(SemanticVersion v1, SemanticVersion v2) { return !(v1 == v2); } /// /// Overloaded < operator /// public static bool operator <(SemanticVersion v1, SemanticVersion v2) { return (Compare(v1, v2) < 0); } /// /// Overloaded <= operator /// public static bool operator <=(SemanticVersion v1, SemanticVersion v2) { return (Compare(v1, v2) <= 0); } /// /// Overloaded > operator /// public static bool operator >(SemanticVersion v1, SemanticVersion v2) { return (Compare(v1, v2) > 0); } /// /// Overloaded >= operator /// public static bool operator >=(SemanticVersion v1, SemanticVersion v2) { return (Compare(v1, v2) >= 0); } private static int ComparePreLabel(string preLabel1, string preLabel2) { // Symver 2.0 standard p.9 // Pre-release versions have a lower precedence than the associated normal version. // Comparing each dot separated identifier from left to right // until a difference is found as follows: // identifiers consisting of only digits are compared numerically // and identifiers with letters or hyphens are compared lexically in ASCII sort order. // Numeric identifiers always have lower precedence than non-numeric identifiers. // A larger set of pre-release fields has a higher precedence than a smaller set, // if all of the preceding identifiers are equal. if (String.IsNullOrEmpty(preLabel1)) { return String.IsNullOrEmpty(preLabel2) ? 0 : 1; } if (String.IsNullOrEmpty(preLabel2)) { return -1; } var units1 = preLabel1.Split('.'); var units2 = preLabel2.Split('.'); var minLength = units1.Length < units2.Length ? units1.Length : units2.Length; for (int i = 0; i < minLength; i++) { var ac = units1[i]; var bc = units2[i]; int number1, number2; var isNumber1 = Int32.TryParse(ac, out number1); var isNumber2 = Int32.TryParse(bc, out number2); if (isNumber1 && isNumber2) { if (number1 != number2) { return number1 < number2 ? -1 : 1; } } else { if (isNumber1) { return -1; } if (isNumber2) { return 1; } int result = String.CompareOrdinal(ac, bc); if (result != 0) { return result; } } } return units1.Length.CompareTo(units2.Length); } internal enum ParseFailureKind { ArgumentException, ArgumentOutOfRangeException, FormatException } internal struct VersionResult { internal SemanticVersion _parsedVersion; internal ParseFailureKind _failure; internal string _exceptionArgument; internal bool _canThrow; internal void Init(bool canThrow) { _canThrow = canThrow; } internal void SetFailure(ParseFailureKind failure) { SetFailure(failure, String.Empty); } internal void SetFailure(ParseFailureKind failure, string argument) { _failure = failure; _exceptionArgument = argument; if (_canThrow) { throw GetVersionParseException(); } } internal Exception GetVersionParseException() { switch (_failure) { case ParseFailureKind.ArgumentException: return PSTraceSource.NewArgumentException("version"); case ParseFailureKind.ArgumentOutOfRangeException: throw new ValidationMetadataException("ValidateRangeTooSmall", null, Metadata.ValidateRangeSmallerThanMinRangeFailure, _exceptionArgument, "0"); case ParseFailureKind.FormatException: // Regenerate the FormatException as would be thrown by Int32.Parse() try { Int32.Parse(_exceptionArgument, CultureInfo.InvariantCulture); } catch (FormatException e) { return e; } catch (OverflowException e) { return e; } break; } return PSTraceSource.NewArgumentException("version"); } } } }