// // Copyright (c) 2009 Microsoft Corporation. All rights reserved. // // DISCLAIMER OF WARRANTY: The software is licensed “as-is.” You // bear the risk of using it. Microsoft gives no express warranties, // guarantees or conditions. You may have additional consumer rights // under your local laws which this agreement cannot change. To the extent // permitted under your local laws, Microsoft excludes the implied warranties // of merchantability, fitness for a particular purpose and non-infringement. using System; using System.IO; using System.Management.Automation; using System.Management.Automation.Runspaces; namespace Microsoft.Samples.PowerShell.Serialization { using PowerShell = System.Management.Automation.PowerShell; public class DeserializingTypeConverter : PSTypeConverter { #region Conversion / rehydration methods /// /// Determines if the converter can rehydrate /// /// The value to convert from (deserialized property bag) /// The type to convert to (artificial and ignored - this is always DeserializingTypeConverter class) /// True if the converter can rehydrate the parameter public override bool CanConvertFrom(PSObject sourceValue, Type destinationType) { return sourceValue.TypeNames.Contains("Deserialized.System.ArgumentException"); } /// /// Rehydrates the parameter /// /// The value to convert from (deserialized property bag) /// The type to convert to (artificial and ignored - this is always DeserializingTypeConverter class) /// /// /// the parameter rehydrated into a live object /// if no conversion was possible (deserializer is going to use the deserialized property bag instead of a live object) public override object ConvertFrom(PSObject sourceValue, Type destinationType, IFormatProvider formatProvider, bool ignoreCase) { // go through the types, from the most specific/derived, to the most broad/base type foreach (string deserializedTypeName in sourceValue.TypeNames) { // Get rid of the "Deserialized." prefix from the type names const string deserializedPrefix = "Deserialized."; string typeName = deserializedTypeName; if (typeName.StartsWith(deserializedPrefix, StringComparison.OrdinalIgnoreCase)) { typeName = typeName.Substring(deserializedPrefix.Length); } // Get the original type associated with the deserialized property bag Type originalType = Type.GetType(typeName, false /* throwOnError */); // originalType might be null in the following cases: // - the assembly containing the type is not loaded into the current AppDomain // - the typeName is artificially introduced by PowerShell's Extended Type System // (i.e. Selected.System.ArgumentOutOfRangeException) if (originalType == null) { continue; } // Only handle the types that we know how to rehydrate if (!originalType.IsSubclassOf(typeof(System.ArgumentException))) { continue; } // Get the exception message from one of deserialized properties. // It is ok to throw exceptions here - i.e. when // 1) the property doesn't exist or // 2) the property value is of a wrong type // Type convertion in PowerShell engine will simply fail the conversion // if our type converter throws any exception. string exceptionMessage = (string)sourceValue.Properties["Message"].Value; // rehydrate the property bag and return the live object System.Console.WriteLine("Rehydrating an ArgumentException object"); return Activator.CreateInstance(originalType, exceptionMessage); } // this property bag didn't get recognized by the loop above throw new InvalidCastException("Cannot rehydrate sourceValue"); } #endregion Conversion / rehydration methods #region Methods from PSTypeConverter that are not needed and can be left out empty /// /// This method is not implemented - CanConvertFrom method is used instead. /// public override bool CanConvertTo(object sourceValue, Type destinationType) { return false; } /// /// This method is not implemented - ConvertFrom method is used instead. /// public override object ConvertTo(object sourceValue, Type destinationType, IFormatProvider formatProvider, bool ignoreCase) { throw new NotImplementedException(); } /// /// This method is not implemented - an overload taking a PSObject is implemented instead /// public override bool CanConvertFrom(object sourceValue, Type destinationType) { throw new NotImplementedException(); } /// /// This method is not implemented - an overload taking a PSObject is implemented instead /// public override bool CanConvertTo(PSObject sourceValue, Type destinationType) { throw new NotImplementedException(); } /// /// This method is not implemented - an overload taking a PSObject is implemented instead /// public override object ConvertFrom(object sourceValue, Type destinationType, IFormatProvider formatProvider, bool ignoreCase) { throw new NotImplementedException(); } /// /// This method is not implemented - an overload taking a PSObject is implemented instead /// public override object ConvertTo(PSObject sourceValue, Type destinationType, IFormatProvider formatProvider, bool ignoreCase) { throw new NotImplementedException(); } #endregion Methods from PSTypeConverter that are not needed and can be left out empty } class Serialization03 { /// /// This sample looks at an existing .NET class and shows how to make sure that /// instances of this class and of derived classes are deserialized (rehydrated) /// into live .NET objects. /// static void Main() { string typesPs1XmlPath = Path.Combine(Environment.CurrentDirectory, "Serialization03.types.ps1xml"); if (!File.Exists(typesPs1XmlPath)) { Console.WriteLine("Building the project in Visual Studio should have created a types.ps1xml file at the following path:"); Console.WriteLine("{0}", typesPs1XmlPath); Console.WriteLine(); Console.WriteLine("Cannot continue without this file being present."); return; } // Create a default InitialSessionState InitialSessionState iss = InitialSessionState.CreateDefault(); // Add our types.ps1xml file to the InitialSessionState // (one alternative would be to associate the file with a module or with a snap-in) iss.Types.Add(new SessionStateTypeEntry(typesPs1XmlPath)); // // Demonstrate the effects of the types.ps1xml and DeserializingTypeConverter // using (Runspace myRunspace = RunspaceFactory.CreateRunspace(iss)) { myRunspace.Open(); // // Demonstrate that the deserializing an exception results in a live object // using (PowerShell powershell = PowerShell.Create()) { powershell.Runspace = myRunspace; powershell.AddScript(@" # Get an object derived from System.ArgumentException $exception = New-Object System.ArgumentOutOfRangeException 'ParameterName' # Serialize the object to the disk # (remoting would also serialize the object before sending it to the remote session) $exception | Export-CliXml .\Serialization03.xml # Deserialize the object $deserializedException = Import-CliXml .\Serialization03.xml # Verify that the object is rehydrated Write-Output ('Deserialized object is of type: ' + $deserializedException.GetType().FullName) "); foreach (string s in powershell.Invoke()) { System.Console.WriteLine(s); } } // Close the runspace and release any resources. myRunspace.Close(); } System.Console.WriteLine("Hit any key to exit..."); System.Console.ReadKey(); } } }