883ca98dd7
* Seal private classes * Fix CS0509 * Fix CS0628
7609 lines
298 KiB
C#
7609 lines
298 KiB
C#
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Management.Automation.Internal;
|
|
using System.Management.Automation.Language;
|
|
using System.Management.Automation.Remoting;
|
|
using System.Management.Automation.Runspaces;
|
|
using System.Management.Automation.Tracing;
|
|
using System.Net.Mail;
|
|
using System.Reflection;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Security;
|
|
using System.Text;
|
|
using System.Xml;
|
|
|
|
using Microsoft.Management.Infrastructure;
|
|
using Microsoft.Management.Infrastructure.Serialization;
|
|
using Microsoft.PowerShell.Commands;
|
|
|
|
using Dbg = System.Management.Automation.Diagnostics;
|
|
|
|
namespace System.Management.Automation
|
|
{
|
|
[Flags]
|
|
internal enum SerializationOptions
|
|
{
|
|
None = 0,
|
|
UseDepthFromTypes = 1,
|
|
NoRootElement = 2,
|
|
NoNamespace = 4,
|
|
NoObjectRefIds = 8,
|
|
PreserveSerializationSettingOfOriginal = 16,
|
|
|
|
RemotingOptions = UseDepthFromTypes | NoRootElement | NoNamespace | PreserveSerializationSettingOfOriginal,
|
|
}
|
|
|
|
internal class SerializationContext
|
|
{
|
|
private const int DefaultSerializationDepth = 2;
|
|
|
|
internal SerializationContext()
|
|
: this(DefaultSerializationDepth, true)
|
|
{
|
|
}
|
|
|
|
internal SerializationContext(int depth, bool useDepthFromTypes)
|
|
: this(
|
|
depth,
|
|
(useDepthFromTypes ? SerializationOptions.UseDepthFromTypes : SerializationOptions.None) |
|
|
SerializationOptions.PreserveSerializationSettingOfOriginal,
|
|
null)
|
|
{
|
|
}
|
|
|
|
internal SerializationContext(int depth, SerializationOptions options, PSRemotingCryptoHelper cryptoHelper)
|
|
{
|
|
if (depth < 1)
|
|
{
|
|
throw PSTraceSource.NewArgumentException("writer", Serialization.DepthOfOneRequired);
|
|
}
|
|
|
|
this.depth = depth;
|
|
this.options = options;
|
|
this.cryptoHelper = cryptoHelper;
|
|
}
|
|
|
|
internal readonly int depth;
|
|
internal readonly SerializationOptions options;
|
|
internal readonly PSRemotingCryptoHelper cryptoHelper;
|
|
|
|
internal readonly CimClassSerializationCache<CimClassSerializationId> cimClassSerializationIdCache = new CimClassSerializationCache<CimClassSerializationId>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// This class provides public functionality for serializing a PSObject.
|
|
/// </summary>
|
|
public class PSSerializer
|
|
{
|
|
internal PSSerializer() { }
|
|
|
|
/// <summary>
|
|
/// Serializes an object into PowerShell CliXml.
|
|
/// </summary>
|
|
/// <param name="source">The input object to serialize. Serializes to a default depth of 1.</param>
|
|
/// <returns>The serialized object, as CliXml.</returns>
|
|
public static string Serialize(object source)
|
|
{
|
|
return Serialize(source, s_mshDefaultSerializationDepth);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serializes an object into PowerShell CliXml.
|
|
/// </summary>
|
|
/// <param name="source">The input object to serialize.</param>
|
|
/// <param name="depth">The depth of the members to serialize.</param>
|
|
/// <returns>The serialized object, as CliXml.</returns>
|
|
public static string Serialize(object source, int depth)
|
|
{
|
|
// Create an xml writer
|
|
StringBuilder sb = new StringBuilder();
|
|
XmlWriterSettings xmlSettings = new XmlWriterSettings();
|
|
xmlSettings.CloseOutput = true;
|
|
xmlSettings.Encoding = System.Text.Encoding.Unicode;
|
|
xmlSettings.Indent = true;
|
|
xmlSettings.OmitXmlDeclaration = true;
|
|
XmlWriter xw = XmlWriter.Create(sb, xmlSettings);
|
|
|
|
// Serialize the objects
|
|
Serializer serializer = new Serializer(xw, depth, true);
|
|
serializer.Serialize(source);
|
|
serializer.Done();
|
|
serializer = null;
|
|
|
|
// Return the output
|
|
return sb.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deserializes PowerShell CliXml into an object.
|
|
/// </summary>
|
|
/// <param name="source">The CliXml the represents the object to deserialize.</param>
|
|
/// <returns>An object that represents the serialized content.</returns>
|
|
public static object Deserialize(string source)
|
|
{
|
|
object[] results = DeserializeAsList(source);
|
|
|
|
// Return the results
|
|
if (results.Length == 0)
|
|
{
|
|
return null;
|
|
}
|
|
else if (results.Length == 1)
|
|
{
|
|
return results[0];
|
|
}
|
|
else
|
|
{
|
|
return results;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deserializes PowerShell CliXml into a list of objects.
|
|
/// </summary>
|
|
/// <param name="source">The CliXml the represents the object to deserialize.</param>
|
|
/// <returns>An object array represents the serialized content.</returns>
|
|
public static object[] DeserializeAsList(string source)
|
|
{
|
|
List<object> results = new List<object>();
|
|
|
|
// Create the text reader to hold the content
|
|
TextReader textReader = new StringReader(source);
|
|
XmlReader xmlReader = XmlReader.Create(textReader, InternalDeserializer.XmlReaderSettingsForCliXml);
|
|
|
|
// Deserialize the content
|
|
Deserializer deserializer = new Deserializer(xmlReader);
|
|
while (!deserializer.Done())
|
|
{
|
|
object result = deserializer.Deserialize();
|
|
results.Add(result);
|
|
}
|
|
|
|
return results.ToArray();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Default depth of serialization.
|
|
/// </summary>
|
|
private static readonly int s_mshDefaultSerializationDepth = 1;
|
|
}
|
|
|
|
/// <summary>
|
|
/// This class provides functionality for serializing a PSObject.
|
|
/// </summary>
|
|
internal class Serializer
|
|
{
|
|
#region constructor
|
|
|
|
private readonly InternalSerializer _serializer;
|
|
|
|
/// <summary>
|
|
/// Creates a Serializer using default serialization context.
|
|
/// </summary>
|
|
/// <param name="writer">Writer to be used for serialization.</param>
|
|
internal Serializer(XmlWriter writer)
|
|
: this(writer, new SerializationContext())
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a Serializer using specified serialization context.
|
|
/// </summary>
|
|
/// <param name="writer">Writer to be used for serialization.</param>
|
|
/// <param name="depth">Depth of serialization.</param>
|
|
/// <param name="useDepthFromTypes">
|
|
/// if <see langword="true"/> then types.ps1xml can override depth
|
|
/// for a particular types (using SerializationDepth property)
|
|
/// </param>
|
|
internal Serializer(XmlWriter writer, int depth, bool useDepthFromTypes)
|
|
: this(writer, new SerializationContext(depth, useDepthFromTypes))
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a Serializer using specified serialization context.
|
|
/// </summary>
|
|
/// <param name="writer">Writer to be used for serialization.</param>
|
|
/// <param name="context">Serialization context.</param>
|
|
internal Serializer(XmlWriter writer, SerializationContext context)
|
|
{
|
|
if (writer == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentException(nameof(writer));
|
|
}
|
|
|
|
if (context == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentException(nameof(context));
|
|
}
|
|
|
|
_serializer = new InternalSerializer(writer, context);
|
|
_serializer.Start();
|
|
}
|
|
|
|
#endregion constructor
|
|
|
|
#region public methods / properties
|
|
|
|
/// <summary>
|
|
/// Used by Remoting infrastructure. This TypeTable instance
|
|
/// will be used by Serializer if ExecutionContext is not
|
|
/// available (to get the ExecutionContext's TypeTable)
|
|
/// </summary>
|
|
internal TypeTable TypeTable
|
|
{
|
|
get { return _serializer.TypeTable; }
|
|
|
|
set { _serializer.TypeTable = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serializes the object.
|
|
/// </summary>
|
|
/// <param name="source">Object to be serialized.</param>
|
|
/// <remarks>
|
|
/// Please note that this method shouldn't throw any exceptions.
|
|
/// If it throws - please open a bug.
|
|
/// </remarks>
|
|
internal void Serialize(object source)
|
|
{
|
|
Serialize(source, null);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serializes passed in object.
|
|
/// </summary>
|
|
/// <param name="source">
|
|
/// object to be serialized
|
|
/// </param>
|
|
/// <param name="streamName">
|
|
/// Stream to which this object belong. Ex: Output, Error etc.
|
|
/// </param>
|
|
/// <remarks>
|
|
/// Please note that this method shouldn't throw any exceptions.
|
|
/// If it throws - please open a bug.
|
|
/// </remarks>
|
|
internal void Serialize(object source, string streamName)
|
|
{
|
|
_serializer.WriteOneTopLevelObject(source, streamName);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write the end of root element.
|
|
/// </summary>
|
|
internal void Done()
|
|
{
|
|
_serializer.End();
|
|
}
|
|
|
|
internal void Stop()
|
|
{
|
|
_serializer.Stop();
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
[Flags]
|
|
internal enum DeserializationOptions
|
|
{
|
|
None = 0,
|
|
NoRootElement = 256, // I start at 256 to try not to overlap
|
|
NoNamespace = 512, // with SerializationOptions and to catch bugs early
|
|
DeserializeScriptBlocks = 1024,
|
|
|
|
RemotingOptions = NoRootElement | NoNamespace,
|
|
}
|
|
|
|
internal class DeserializationContext
|
|
{
|
|
internal DeserializationContext()
|
|
: this(DeserializationOptions.None, null)
|
|
{
|
|
}
|
|
|
|
internal DeserializationContext(DeserializationOptions options, PSRemotingCryptoHelper cryptoHelper)
|
|
{
|
|
this.options = options;
|
|
this.cryptoHelper = cryptoHelper;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Limits the total data processed by the deserialization context. Deserialization context
|
|
/// is used by PriorityReceivedDataCollection (remoting) to process incoming data from the
|
|
/// remote end. A value of Null means that the max memory is unlimited.
|
|
/// </summary>
|
|
internal int? MaximumAllowedMemory { get; set; }
|
|
|
|
/// <summary>
|
|
/// Logs that memory used by deserialized objects is not related to the size of input xml.
|
|
/// Used mainly to account for memory usage of cloned TypeNames when calculating memory quota usage.
|
|
/// </summary>
|
|
/// <param name="amountOfExtraMemory"></param>
|
|
internal void LogExtraMemoryUsage(int amountOfExtraMemory)
|
|
{
|
|
if (amountOfExtraMemory < 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (MaximumAllowedMemory.HasValue)
|
|
{
|
|
if (amountOfExtraMemory > (MaximumAllowedMemory.Value - _totalDataProcessedSoFar))
|
|
{
|
|
string message = StringUtil.Format(Serialization.DeserializationMemoryQuota, ((double)MaximumAllowedMemory.Value) / (1 << 20),
|
|
ConfigurationDataFromXML.MAXRCVDOBJSIZETOKEN_CamelCase,
|
|
ConfigurationDataFromXML.MAXRCVDCMDSIZETOKEN_CamelCase);
|
|
throw new XmlException(message);
|
|
}
|
|
|
|
_totalDataProcessedSoFar += amountOfExtraMemory;
|
|
}
|
|
}
|
|
|
|
private int _totalDataProcessedSoFar;
|
|
|
|
internal readonly DeserializationOptions options;
|
|
internal readonly PSRemotingCryptoHelper cryptoHelper;
|
|
|
|
internal static readonly int MaxItemsInCimClassCache = 100;
|
|
internal readonly CimClassDeserializationCache<CimClassSerializationId> cimClassSerializationIdCache = new CimClassDeserializationCache<CimClassSerializationId>();
|
|
}
|
|
|
|
internal class CimClassDeserializationCache<TKey>
|
|
{
|
|
private readonly Dictionary<TKey, CimClass> _cimClassIdToClass = new Dictionary<TKey, CimClass>();
|
|
|
|
internal void AddCimClassToCache(TKey key, CimClass cimClass)
|
|
{
|
|
if (_cimClassIdToClass.Count >= DeserializationContext.MaxItemsInCimClassCache)
|
|
{
|
|
_cimClassIdToClass.Clear();
|
|
}
|
|
|
|
_cimClassIdToClass.Add(key, cimClass);
|
|
|
|
/* PRINTF DEBUG
|
|
Console.WriteLine("Contents of deserialization cache (after a call to AddCimClassToCache ({0})):", key);
|
|
Console.WriteLine(" Count = {0}", this._cimClassIdToClass.Count);
|
|
foreach (var t in this._cimClassIdToClass.Keys)
|
|
{
|
|
Console.WriteLine(" {0}", t);
|
|
}
|
|
*/
|
|
}
|
|
|
|
internal CimClass GetCimClassFromCache(TKey key)
|
|
{
|
|
CimClass cimClass;
|
|
if (_cimClassIdToClass.TryGetValue(key, out cimClass))
|
|
{
|
|
/* PRINTF DEBUG
|
|
Console.WriteLine("GetCimClassFromCache - class found: {0}", key);
|
|
*/
|
|
|
|
return cimClass;
|
|
}
|
|
|
|
/* PRINTF DEBUG
|
|
Console.WriteLine("GetCimClassFromCache - class NOT found: {0}", key);
|
|
*/
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
internal class CimClassSerializationCache<TKey>
|
|
{
|
|
private readonly HashSet<TKey> _cimClassesHeldByDeserializer = new HashSet<TKey>(EqualityComparer<TKey>.Default);
|
|
|
|
internal bool DoesDeserializerAlreadyHaveCimClass(TKey key)
|
|
{
|
|
return _cimClassesHeldByDeserializer.Contains(key);
|
|
}
|
|
|
|
internal void AddClassToCache(TKey key)
|
|
{
|
|
Dbg.Assert(!_cimClassesHeldByDeserializer.Contains(key), "This method should not be called for classes already in the cache");
|
|
|
|
if (_cimClassesHeldByDeserializer.Count >= DeserializationContext.MaxItemsInCimClassCache)
|
|
{
|
|
_cimClassesHeldByDeserializer.Clear();
|
|
}
|
|
|
|
_cimClassesHeldByDeserializer.Add(key);
|
|
|
|
/* PRINTF DEBUG
|
|
Console.WriteLine("Contents of serialization cache (after adding {0}):", key);
|
|
Console.WriteLine(" Count = {0}", this._cimClassesHeldByDeserializer.Count);
|
|
foreach (var t in _cimClassesHeldByDeserializer)
|
|
{
|
|
Console.WriteLine(" {0}", t);
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
|
|
internal class CimClassSerializationId : Tuple<string, string, string, int>
|
|
{
|
|
public CimClassSerializationId(string className, string namespaceName, string computerName, int hashCode)
|
|
: base(className, namespaceName, computerName, hashCode)
|
|
{
|
|
}
|
|
|
|
public string ClassName { get { return this.Item1; } }
|
|
|
|
public string NamespaceName { get { return this.Item2; } }
|
|
|
|
public string ComputerName { get { return this.Item3; } }
|
|
|
|
public int ClassHashCode { get { return this.Item4; } }
|
|
}
|
|
|
|
/// <summary>
|
|
/// This class provides functionality for deserializing a PSObject.
|
|
/// </summary>
|
|
internal class Deserializer
|
|
{
|
|
#region constructor
|
|
|
|
private readonly XmlReader _reader;
|
|
private readonly InternalDeserializer _deserializer;
|
|
private readonly DeserializationContext _context;
|
|
|
|
/// <summary>
|
|
/// Creates a Deserializer using default deserialization context.
|
|
/// </summary>
|
|
/// <param name="reader">Reader to be used for deserialization.</param>
|
|
/// <exception cref="XmlException">
|
|
/// Thrown when the xml is in an incorrect format
|
|
/// </exception>
|
|
internal Deserializer(XmlReader reader)
|
|
: this(reader, new DeserializationContext())
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a Deserializer using specified serialization context.
|
|
/// </summary>
|
|
/// <param name="reader">Reader to be used for deserialization.</param>
|
|
/// <param name="context">Serialization context.</param>
|
|
/// <exception cref="XmlException">
|
|
/// Thrown when the xml is in an incorrect format
|
|
/// </exception>
|
|
internal Deserializer(XmlReader reader, DeserializationContext context)
|
|
{
|
|
if (reader == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(reader));
|
|
}
|
|
|
|
_reader = reader;
|
|
_context = context;
|
|
_deserializer = new InternalDeserializer(_reader, _context);
|
|
|
|
try
|
|
{
|
|
Start();
|
|
}
|
|
catch (XmlException exception)
|
|
{
|
|
ReportExceptionForETW(exception);
|
|
throw;
|
|
}
|
|
}
|
|
|
|
#endregion constructor
|
|
|
|
#region public method / properties
|
|
|
|
private static void ReportExceptionForETW(XmlException exception)
|
|
{
|
|
PSEtwLog.LogAnalyticError(
|
|
PSEventId.Serializer_XmlExceptionWhenDeserializing, PSOpcode.Exception, PSTask.Serialization,
|
|
PSKeyword.Serializer | PSKeyword.UseAlwaysAnalytic,
|
|
exception.LineNumber, exception.LinePosition, exception.ToString());
|
|
}
|
|
|
|
private bool _done = false;
|
|
|
|
/// <summary>
|
|
/// Used by Remoting infrastructure. This TypeTable instance
|
|
/// will be used by Deserializer if ExecutionContext is not
|
|
/// available (to get the ExecutionContext's TypeTable)
|
|
/// </summary>
|
|
internal TypeTable TypeTable
|
|
{
|
|
get { return _deserializer.TypeTable; }
|
|
|
|
set { _deserializer.TypeTable = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Read the root element tag and set the cursor to start tag of
|
|
/// first object.
|
|
/// </summary>
|
|
private void Start()
|
|
{
|
|
Dbg.Assert(_reader.ReadState == ReadState.Initial, "When deserialization starts we should have XmlReader.ReadState == Initial");
|
|
Dbg.Assert(_reader.NodeType == XmlNodeType.None, "When deserialization starts we should have XmlReader.NodeType == None");
|
|
_reader.Read();
|
|
|
|
// If version is not provided, we assume it is the default
|
|
string version = InternalSerializer.DefaultVersion;
|
|
if ((_context.options & DeserializationOptions.NoRootElement) == DeserializationOptions.NoRootElement)
|
|
{
|
|
_done = _reader.EOF;
|
|
}
|
|
else
|
|
{
|
|
// Make sure the reader is positioned on the root ( <Objs> ) element (not on XmlDeclaration for example)
|
|
_reader.MoveToContent();
|
|
Dbg.Assert(_reader.EOF || (_reader.NodeType == XmlNodeType.Element), "When deserialization starts reading we should have XmlReader.NodeType == Element");
|
|
|
|
// Read version attribute and validate it.
|
|
string versionAttribute = _reader.GetAttribute(SerializationStrings.VersionAttribute);
|
|
if (versionAttribute != null)
|
|
{
|
|
version = versionAttribute;
|
|
}
|
|
|
|
// If the root element tag is empty, there are no objects to read.
|
|
if (!_deserializer.ReadStartElementAndHandleEmpty(SerializationStrings.RootElementTag))
|
|
{
|
|
_done = true;
|
|
}
|
|
}
|
|
|
|
_deserializer.ValidateVersion(version);
|
|
}
|
|
|
|
internal bool Done()
|
|
{
|
|
if (!_done)
|
|
{
|
|
if ((_context.options & DeserializationOptions.NoRootElement) == DeserializationOptions.NoRootElement)
|
|
{
|
|
_done = _reader.EOF;
|
|
}
|
|
else
|
|
{
|
|
if (_reader.NodeType == XmlNodeType.EndElement)
|
|
{
|
|
try
|
|
{
|
|
_reader.ReadEndElement();
|
|
}
|
|
catch (XmlException exception)
|
|
{
|
|
ReportExceptionForETW(exception);
|
|
throw;
|
|
}
|
|
|
|
_done = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return _done;
|
|
}
|
|
|
|
internal void Stop()
|
|
{
|
|
_deserializer.Stop();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deserializes next object.
|
|
/// </summary>
|
|
/// <exception cref="XmlException">
|
|
/// Thrown when the xml is in an incorrect format
|
|
/// </exception>
|
|
internal object Deserialize()
|
|
{
|
|
string ignore;
|
|
return Deserialize(out ignore);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deserializes next object.
|
|
/// </summary>
|
|
/// <param name="streamName">Stream the object belongs to (i.e. "Error", "Output", etc.).</param>
|
|
/// <exception cref="XmlException">
|
|
/// Thrown when the xml is in an incorrect format
|
|
/// </exception>
|
|
internal object Deserialize(out string streamName)
|
|
{
|
|
if (Done())
|
|
{
|
|
throw PSTraceSource.NewInvalidOperationException(Serialization.ReadCalledAfterDone);
|
|
}
|
|
|
|
try
|
|
{
|
|
return _deserializer.ReadOneObject(out streamName);
|
|
}
|
|
catch (XmlException exception)
|
|
{
|
|
ReportExceptionForETW(exception);
|
|
throw;
|
|
}
|
|
}
|
|
|
|
#endregion public methods
|
|
|
|
#region Helper methods for dealing with "Deserialized." prefix
|
|
|
|
/// <summary>
|
|
/// Adds "Deserialized." prefix to passed in argument if not already present.
|
|
/// </summary>
|
|
/// <param name="type"></param>
|
|
internal static void AddDeserializationPrefix(ref string type)
|
|
{
|
|
Dbg.Assert(type != null, "caller should validate the parameter");
|
|
if (!type.StartsWith(Deserializer.DeserializationTypeNamePrefix, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
type = type.Insert(0, Deserializer.DeserializationTypeNamePrefix);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if an object <paramref name="o"/> is either a live or deserialized instance of class <paramref name="type"/> or one of its subclasses.
|
|
/// </summary>
|
|
/// <param name="o"></param>
|
|
/// <param name="type"></param>
|
|
/// <returns><see langword="true"/> if <paramref name="o"/> is either a live or deserialized instance of class <paramref name="type"/> or one of its subclasses; <see langword="false"/> otherwise.</returns>
|
|
internal static bool IsInstanceOfType(object o, Type type)
|
|
{
|
|
if (type == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(type));
|
|
}
|
|
|
|
if (o == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return type.IsInstanceOfType(PSObject.Base(o)) || IsDeserializedInstanceOfType(o, type);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if an object <paramref name="o"/> is a deserialized instance of class <paramref name="type"/> or one of its subclasses.
|
|
/// </summary>
|
|
/// <param name="o"></param>
|
|
/// <param name="type"></param>
|
|
/// <returns><see langword="true"/> if <paramref name="o"/> is a deserialized instance of class <paramref name="type"/> or one of its subclasses; <see langword="false"/> otherwise.</returns>
|
|
internal static bool IsDeserializedInstanceOfType(object o, Type type)
|
|
{
|
|
if (type == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(type));
|
|
}
|
|
|
|
if (o == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
PSObject pso = o as PSObject;
|
|
if (pso != null)
|
|
{
|
|
IEnumerable<string> typeNames = pso.InternalTypeNames;
|
|
if (typeNames != null)
|
|
{
|
|
foreach (string typeName in typeNames)
|
|
{
|
|
if (typeName.Length == Deserializer.DeserializationTypeNamePrefix.Length + type.FullName.Length &&
|
|
typeName.StartsWith(Deserializer.DeserializationTypeNamePrefix, StringComparison.OrdinalIgnoreCase) &&
|
|
typeName.EndsWith(type.FullName, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// not the right type
|
|
return false;
|
|
}
|
|
|
|
internal static string MaskDeserializationPrefix(string typeName)
|
|
{
|
|
if (typeName == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (typeName.StartsWith(Deserializer.DeserializationTypeNamePrefix, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
typeName = typeName.Substring(Deserializer.DeserializationTypeNamePrefix.Length);
|
|
}
|
|
|
|
return typeName;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a new collection of typenames without "Deserialization." prefix
|
|
/// in the typename. This will allow to map type info/format info of the orignal type
|
|
/// for deserialized objects.
|
|
/// </summary>
|
|
/// <param name="typeNames"></param>
|
|
/// <returns>
|
|
/// Null if no type with "Deserialized." prefix is found.
|
|
/// Otherwise <paramref name="typeNames"/> with the prefix removed if any.
|
|
/// </returns>
|
|
internal static Collection<string> MaskDeserializationPrefix(Collection<string> typeNames)
|
|
{
|
|
Dbg.Assert(typeNames != null, "typeNames cannot be null");
|
|
|
|
bool atleastOneDeserializedTypeFound = false;
|
|
|
|
Collection<string> typesWithoutPrefix = new Collection<string>();
|
|
foreach (string type in typeNames)
|
|
{
|
|
if (type.StartsWith(Deserializer.DeserializationTypeNamePrefix,
|
|
StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
atleastOneDeserializedTypeFound = true;
|
|
// remove *only* the prefix
|
|
typesWithoutPrefix.Add(type.Substring(Deserializer.DeserializationTypeNamePrefix.Length));
|
|
}
|
|
else
|
|
{
|
|
typesWithoutPrefix.Add(type);
|
|
}
|
|
}
|
|
|
|
if (atleastOneDeserializedTypeFound)
|
|
{
|
|
return typesWithoutPrefix;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used to prefix a typename for deserialization.
|
|
/// </summary>
|
|
private const string DeserializationTypeNamePrefix = "Deserialized.";
|
|
|
|
#endregion
|
|
}
|
|
|
|
/// <summary>
|
|
/// Types of known type container supported by monad.
|
|
/// </summary>
|
|
internal enum ContainerType
|
|
{
|
|
Dictionary,
|
|
Queue,
|
|
Stack,
|
|
List,
|
|
Enumerable,
|
|
None
|
|
}
|
|
|
|
/// <summary>
|
|
/// This internal helper class provides methods for serializing mshObject.
|
|
/// </summary>
|
|
internal class InternalSerializer
|
|
{
|
|
#region constructor
|
|
|
|
internal const string DefaultVersion = "1.1.0.1";
|
|
|
|
/// <summary>
|
|
/// Xml writer to be used.
|
|
/// </summary>
|
|
private readonly XmlWriter _writer;
|
|
|
|
/// <summary>
|
|
/// Serialization context.
|
|
/// </summary>
|
|
private readonly SerializationContext _context;
|
|
|
|
/// Used by Remoting infrastructure. This TypeTable instance
|
|
/// will be used by Serializer if ExecutionContext is not
|
|
/// available (to get the ExecutionContext's TypeTable)
|
|
private TypeTable _typeTable;
|
|
|
|
/// <summary>
|
|
/// Depth below top level - used to prevent infinitely deep serialization
|
|
/// (without this protection it would be possible i.e. with SerializationDepth and recursion)
|
|
/// </summary>
|
|
private int _depthBelowTopLevel;
|
|
|
|
private const int MaxDepthBelowTopLevel = 50;
|
|
|
|
private readonly ReferenceIdHandlerForSerializer<object> _objectRefIdHandler;
|
|
private readonly ReferenceIdHandlerForSerializer<ConsolidatedString> _typeRefIdHandler;
|
|
|
|
internal InternalSerializer(XmlWriter writer, SerializationContext context)
|
|
{
|
|
Dbg.Assert(writer != null, "caller should validate the parameter");
|
|
Dbg.Assert(context != null, "caller should validate the parameter");
|
|
_writer = writer;
|
|
_context = context;
|
|
|
|
IDictionary<object, ulong> objectRefIdDictionary = null;
|
|
if ((_context.options & SerializationOptions.NoObjectRefIds) == 0)
|
|
{
|
|
objectRefIdDictionary = new WeakReferenceDictionary<UInt64>();
|
|
}
|
|
|
|
_objectRefIdHandler = new ReferenceIdHandlerForSerializer<object>(objectRefIdDictionary);
|
|
|
|
_typeRefIdHandler = new ReferenceIdHandlerForSerializer<ConsolidatedString>(
|
|
new Dictionary<ConsolidatedString, UInt64>(ConsolidatedString.EqualityComparer));
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Used by Remoting infrastructure. This TypeTable instance
|
|
/// will be used by Serializer if ExecutionContext is not
|
|
/// available (to get the ExecutionContext's TypeTable)
|
|
/// </summary>
|
|
internal TypeTable TypeTable
|
|
{
|
|
get { return _typeTable; }
|
|
|
|
set { _typeTable = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes the start of root element.
|
|
/// </summary>
|
|
internal void Start()
|
|
{
|
|
if ((_context.options & SerializationOptions.NoRootElement) != SerializationOptions.NoRootElement)
|
|
{
|
|
this.WriteStartElement(SerializationStrings.RootElementTag);
|
|
this.WriteAttribute(SerializationStrings.VersionAttribute, InternalSerializer.DefaultVersion);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes the end of root element.
|
|
/// </summary>
|
|
internal void End()
|
|
{
|
|
if ((_context.options & SerializationOptions.NoRootElement) != SerializationOptions.NoRootElement)
|
|
{
|
|
_writer.WriteEndElement();
|
|
}
|
|
|
|
_writer.Flush();
|
|
}
|
|
|
|
private bool _isStopping = false;
|
|
|
|
/// <summary>
|
|
/// Called from a separate thread will stop the serialization process.
|
|
/// </summary>
|
|
internal void Stop()
|
|
{
|
|
_isStopping = true;
|
|
}
|
|
|
|
private void CheckIfStopping()
|
|
{
|
|
if (_isStopping)
|
|
{
|
|
throw PSTraceSource.NewInvalidOperationException(Serialization.Stopping);
|
|
}
|
|
}
|
|
|
|
internal static bool IsPrimitiveKnownType(Type input)
|
|
{
|
|
// Check if source is of primitive known type
|
|
TypeSerializationInfo pktInfo = KnownTypes.GetTypeSerializationInfo(input);
|
|
return (pktInfo != null);
|
|
}
|
|
|
|
/// <summary>
|
|
/// This writes one object.
|
|
/// </summary>
|
|
/// <param name="source">
|
|
/// source to be serialized.
|
|
/// </param>
|
|
/// <param name="streamName">
|
|
/// Stream to which source belongs
|
|
/// </param>
|
|
internal void WriteOneTopLevelObject
|
|
(
|
|
object source,
|
|
string streamName
|
|
)
|
|
{
|
|
Dbg.Assert(_depthBelowTopLevel == 0, "InternalSerializer.depthBelowTopLevel should be 0 at top-level");
|
|
WriteOneObject(source, streamName, null, _context.depth);
|
|
}
|
|
|
|
private void WriteOneObject
|
|
(
|
|
object source,
|
|
string streamName,
|
|
string property,
|
|
int depth
|
|
)
|
|
{
|
|
Dbg.Assert(depth >= 0, "depth should always be greater or equal to zero");
|
|
this.CheckIfStopping();
|
|
|
|
if (source == null)
|
|
{
|
|
WriteNull(streamName, property);
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
_depthBelowTopLevel++;
|
|
Dbg.Assert(_depthBelowTopLevel <= MaxDepthBelowTopLevel, "depthBelowTopLevel should be <= MaxDepthBelowTopLevel");
|
|
if (HandleMaxDepth(source, streamName, property))
|
|
{
|
|
return;
|
|
}
|
|
|
|
depth = GetDepthOfSerialization(source, depth);
|
|
|
|
if (HandlePrimitiveKnownTypeByConvertingToPSObject(source, streamName, property, depth))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Object is not of primitive known type. Check if this has
|
|
// already been serialized.
|
|
string refId = _objectRefIdHandler.GetRefId(source);
|
|
if (refId != null)
|
|
{
|
|
WritePSObjectReference(streamName, property, refId);
|
|
return;
|
|
}
|
|
|
|
if (HandlePrimitiveKnownTypePSObject(source, streamName, property, depth))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Note: We do not use containers in depth calculation. i.e even if the
|
|
// current depth is zero, we serialize the container. All contained items will
|
|
// get serialized with depth zero.
|
|
if (HandleKnownContainerTypes(source, streamName, property, depth))
|
|
{
|
|
return;
|
|
}
|
|
|
|
PSObject mshSource = PSObject.AsPSObject(source);
|
|
// If depth is zero, complex type should be serialized as string.
|
|
if (depth == 0 || SerializeAsString(mshSource))
|
|
{
|
|
HandlePSObjectAsString(mshSource, streamName, property, depth);
|
|
return;
|
|
}
|
|
|
|
HandleComplexTypePSObject(source, streamName, property, depth);
|
|
return;
|
|
}
|
|
finally
|
|
{
|
|
_depthBelowTopLevel--;
|
|
Dbg.Assert(_depthBelowTopLevel >= 0, "depthBelowTopLevel should be >= 0");
|
|
}
|
|
}
|
|
|
|
private bool HandleMaxDepth(object source, string streamName, string property)
|
|
{
|
|
if (_depthBelowTopLevel == MaxDepthBelowTopLevel)
|
|
{
|
|
// assert commented out because of clashes with Wei's tests
|
|
// Dbg.Assert(false, "We should never reach MaxDepthBelowTopLevel with non-malicious input");
|
|
|
|
PSEtwLog.LogAnalyticError(PSEventId.Serializer_MaxDepthWhenSerializing, PSOpcode.Exception,
|
|
PSTask.Serialization, PSKeyword.Serializer, source.GetType().AssemblyQualifiedName, property, _depthBelowTopLevel);
|
|
|
|
string content = Serialization.DeserializationTooDeep;
|
|
HandlePrimitiveKnownType(content, streamName, property);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serializes Primitive Known Types.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// true if source is handled, else false.
|
|
/// </returns>
|
|
private bool HandlePrimitiveKnownType
|
|
(
|
|
object source,
|
|
string streamName,
|
|
string property
|
|
)
|
|
{
|
|
Dbg.Assert(source != null, "caller should validate the parameter");
|
|
|
|
// Check if source is of primitive known type
|
|
TypeSerializationInfo pktInfo = KnownTypes.GetTypeSerializationInfo(source.GetType());
|
|
if (pktInfo != null)
|
|
{
|
|
WriteOnePrimitiveKnownType(this, streamName, property, source, pktInfo);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handles primitive known type by first converting it to a PSObject.In W8, extended
|
|
/// property data is stored external to PSObject. By converting to PSObject, we will
|
|
/// be able to retrieve and serialize the extended properties. This is tracked by
|
|
/// Win8: 414042.
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <param name="streamName"></param>
|
|
/// <param name="property"></param>
|
|
/// <param name="depth"></param>
|
|
/// <returns></returns>
|
|
private bool HandlePrimitiveKnownTypeByConvertingToPSObject(
|
|
object source,
|
|
string streamName,
|
|
string property,
|
|
int depth
|
|
)
|
|
{
|
|
Dbg.Assert(source != null, "caller should validate the parameter");
|
|
// Check if source is of primitive known type
|
|
TypeSerializationInfo pktInfo = KnownTypes.GetTypeSerializationInfo(source.GetType());
|
|
if (pktInfo != null)
|
|
{
|
|
PSObject pktInfoPSObject = PSObject.AsPSObject(source);
|
|
return HandlePrimitiveKnownTypePSObject(pktInfoPSObject, streamName, property, depth);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <param name="streamName"></param>
|
|
/// <param name="property"></param>
|
|
/// <returns></returns>
|
|
private bool HandleSecureString(object source, string streamName, string property)
|
|
{
|
|
Dbg.Assert(source != null, "caller should validate the parameter");
|
|
|
|
SecureString secureString = null;
|
|
secureString = source as SecureString;
|
|
PSObject moSource;
|
|
|
|
if (secureString != null)
|
|
{
|
|
moSource = PSObject.AsPSObject(secureString);
|
|
}
|
|
else
|
|
{
|
|
moSource = source as PSObject;
|
|
}
|
|
|
|
if (moSource != null && !moSource.ImmediateBaseObjectIsEmpty)
|
|
{
|
|
// check if source is of type secure string
|
|
secureString = moSource.ImmediateBaseObject as SecureString;
|
|
if (secureString != null)
|
|
{
|
|
// the principle used in serialization is that serialization
|
|
// never throws, and if something can't be serialized nothing
|
|
// is written. So we write the elements only if encryption succeeds.
|
|
// However, in the case for non-Windows where secure string encryption
|
|
// is not yet supported, a PSCryptoException will be thrown.
|
|
string encryptedString;
|
|
if (_context.cryptoHelper != null)
|
|
{
|
|
encryptedString = _context.cryptoHelper.EncryptSecureString(secureString);
|
|
}
|
|
else
|
|
{
|
|
encryptedString = Microsoft.PowerShell.SecureStringHelper.Protect(secureString);
|
|
}
|
|
|
|
if (property != null)
|
|
{
|
|
WriteStartElement(SerializationStrings.SecureStringTag);
|
|
WriteNameAttribute(property);
|
|
}
|
|
else
|
|
{
|
|
WriteStartElement(SerializationStrings.SecureStringTag);
|
|
}
|
|
|
|
if (streamName != null)
|
|
{
|
|
WriteAttribute(SerializationStrings.StreamNameAttribute, streamName);
|
|
}
|
|
|
|
// Note: We do not use WriteRaw for serializing secure string. WriteString
|
|
// does necessary escaping which may be needed for certain
|
|
// characters.
|
|
_writer.WriteString(encryptedString);
|
|
|
|
_writer.WriteEndElement();
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serializes PSObject whose base objects are of primitive known type.
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <param name="streamName"></param>
|
|
/// <param name="property"></param>
|
|
/// <param name="depth"></param>
|
|
/// <returns>
|
|
/// true if source is handled, else false.
|
|
/// </returns>
|
|
private bool HandlePrimitiveKnownTypePSObject
|
|
(
|
|
object source,
|
|
string streamName,
|
|
string property,
|
|
int depth
|
|
)
|
|
{
|
|
Dbg.Assert(source != null, "caller should validate the parameter");
|
|
|
|
bool sourceHandled = false;
|
|
PSObject moSource = source as PSObject;
|
|
if (moSource != null && !moSource.ImmediateBaseObjectIsEmpty)
|
|
{
|
|
// Check if baseObject is primitive known type
|
|
object baseObject = moSource.ImmediateBaseObject;
|
|
TypeSerializationInfo pktInfo = KnownTypes.GetTypeSerializationInfo(baseObject.GetType());
|
|
if (pktInfo != null)
|
|
{
|
|
WritePrimitiveTypePSObject(moSource, baseObject, pktInfo, streamName, property, depth);
|
|
sourceHandled = true;
|
|
}
|
|
}
|
|
|
|
return sourceHandled;
|
|
}
|
|
|
|
private bool HandleKnownContainerTypes
|
|
(
|
|
object source,
|
|
string streamName,
|
|
string property,
|
|
int depth
|
|
)
|
|
{
|
|
Dbg.Assert(source != null, "caller should validate the parameter");
|
|
|
|
ContainerType ct = ContainerType.None;
|
|
PSObject mshSource = source as PSObject;
|
|
IEnumerable enumerable = null;
|
|
IDictionary dictionary = null;
|
|
|
|
// If passed in object is PSObject with no baseobject, return false.
|
|
if (mshSource != null && mshSource.ImmediateBaseObjectIsEmpty)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check if source (or baseobject in mshSource) is known container type
|
|
SerializationUtilities.GetKnownContainerTypeInfo(mshSource != null ? mshSource.ImmediateBaseObject : source, out ct,
|
|
out dictionary, out enumerable);
|
|
|
|
if (ct == ContainerType.None)
|
|
return false;
|
|
|
|
string refId = _objectRefIdHandler.SetRefId(source);
|
|
WriteStartOfPSObject(
|
|
mshSource ?? PSObject.AsPSObject(source),
|
|
streamName,
|
|
property,
|
|
refId,
|
|
true, // always write TypeNames information for known container types
|
|
null); // never write ToString information for known container types
|
|
|
|
switch (ct)
|
|
{
|
|
case ContainerType.Dictionary:
|
|
WriteDictionary(dictionary, SerializationStrings.DictionaryTag, depth);
|
|
break;
|
|
case ContainerType.Stack:
|
|
WriteEnumerable(enumerable, SerializationStrings.StackTag, depth);
|
|
break;
|
|
case ContainerType.Queue:
|
|
WriteEnumerable(enumerable, SerializationStrings.QueueTag, depth);
|
|
break;
|
|
case ContainerType.List:
|
|
WriteEnumerable(enumerable, SerializationStrings.ListTag, depth);
|
|
break;
|
|
case ContainerType.Enumerable:
|
|
WriteEnumerable(enumerable, SerializationStrings.CollectionTag, depth);
|
|
break;
|
|
default:
|
|
Dbg.Assert(false, "All containers should be handled in the switch");
|
|
break;
|
|
}
|
|
|
|
if (depth != 0)
|
|
{
|
|
// An object which is original enumerable becomes an PSObject with ArrayList on deserialization.
|
|
// So on roundtrip it will show up as List.
|
|
// We serialize properties of enumerable and on deserialization mark the object as Deserialized.
|
|
// So if object is marked deserialized, we should write properties.
|
|
if (ct == ContainerType.Enumerable || (mshSource != null && mshSource.IsDeserialized))
|
|
{
|
|
PSObject sourceAsPSObject = PSObject.AsPSObject(source);
|
|
PSMemberInfoInternalCollection<PSPropertyInfo> specificPropertiesToSerialize = SerializationUtilities.GetSpecificPropertiesToSerialize(sourceAsPSObject, AllPropertiesCollection, _typeTable);
|
|
WritePSObjectProperties(sourceAsPSObject, depth, specificPropertiesToSerialize);
|
|
SerializeExtendedProperties(sourceAsPSObject, depth, specificPropertiesToSerialize);
|
|
}
|
|
// always serialize instance properties if there are any
|
|
else if (mshSource != null)
|
|
{
|
|
SerializeInstanceProperties(mshSource, depth);
|
|
}
|
|
}
|
|
|
|
_writer.WriteEndElement();
|
|
|
|
return true;
|
|
}
|
|
|
|
#region Write PSObject
|
|
|
|
/// <summary>
|
|
/// Writes PSObject Reference Element.
|
|
/// </summary>
|
|
private void WritePSObjectReference
|
|
(
|
|
string streamName,
|
|
string property,
|
|
string refId
|
|
)
|
|
{
|
|
Dbg.Assert(!string.IsNullOrEmpty(refId), "caller should validate the parameter");
|
|
|
|
WriteStartElement(SerializationStrings.ReferenceTag);
|
|
if (streamName != null)
|
|
{
|
|
WriteAttribute(SerializationStrings.StreamNameAttribute, streamName);
|
|
}
|
|
|
|
if (property != null)
|
|
{
|
|
WriteNameAttribute(property);
|
|
}
|
|
|
|
WriteAttribute(SerializationStrings.ReferenceIdAttribute, refId);
|
|
_writer.WriteEndElement();
|
|
}
|
|
|
|
private static bool PSObjectHasModifiedTypesCollection(PSObject pso)
|
|
{
|
|
ConsolidatedString currentTypes = pso.InternalTypeNames;
|
|
Collection<string> originalTypes = pso.InternalAdapter.BaseGetTypeNameHierarchy(pso.ImmediateBaseObject);
|
|
|
|
if (currentTypes.Count != originalTypes.Count)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
IEnumerator<string> currentEnumerator = currentTypes.GetEnumerator();
|
|
IEnumerator<string> originalEnumerator = originalTypes.GetEnumerator();
|
|
while (currentEnumerator.MoveNext() && originalEnumerator.MoveNext())
|
|
{
|
|
if (!currentEnumerator.Current.Equals(originalEnumerator.Current, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serializes an PSObject whose baseobject is of primitive type.
|
|
/// </summary>
|
|
/// <param name="source">
|
|
/// source from which notes are written
|
|
/// </param>
|
|
/// <param name="primitive">
|
|
/// primitive object which is written as base object. In most cases it
|
|
/// is same source.ImmediateBaseObject. When PSObject is serialized as string,
|
|
/// it can be different. <see cref="HandlePSObjectAsString"/> for more info.
|
|
/// </param>
|
|
/// <param name="pktInfo">
|
|
/// TypeSerializationInfo for the primitive.
|
|
/// </param>
|
|
/// <param name="streamName"></param>
|
|
/// <param name="property"></param>
|
|
/// <param name="depth"></param>
|
|
private void WritePrimitiveTypePSObject
|
|
(
|
|
PSObject source,
|
|
object primitive,
|
|
TypeSerializationInfo pktInfo,
|
|
string streamName,
|
|
string property,
|
|
int depth
|
|
)
|
|
{
|
|
Dbg.Assert(source != null, "Caller should validate source != null");
|
|
|
|
string toStringValue = SerializationUtilities.GetToStringForPrimitiveObject(source);
|
|
bool hasModifiedTypesCollection = PSObjectHasModifiedTypesCollection(source);
|
|
bool hasNotes = PSObjectHasNotes(source);
|
|
bool hasModifiedToString = (toStringValue != null);
|
|
|
|
if (hasNotes || hasModifiedTypesCollection || hasModifiedToString)
|
|
{
|
|
WritePrimitiveTypePSObjectWithNotes(
|
|
source,
|
|
primitive,
|
|
hasModifiedTypesCollection,
|
|
toStringValue,
|
|
pktInfo,
|
|
streamName,
|
|
property,
|
|
depth);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if (primitive != null)
|
|
{
|
|
WriteOnePrimitiveKnownType(this, streamName, property, primitive, pktInfo);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
WriteNull(streamName, property);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serializes an PSObject whose baseobject is of primitive type
|
|
/// and which has notes.
|
|
/// </summary>
|
|
/// <param name="source">
|
|
/// source from which notes are written
|
|
/// </param>
|
|
/// <param name="primitive">
|
|
/// primitive object which is written as base object. In most cases it
|
|
/// is same source.ImmediateBaseObject. When PSObject is serialized as string,
|
|
/// it can be different. <see cref="HandlePSObjectAsString"/> for more info.
|
|
/// </param>
|
|
/// <param name="hasModifiedTypesCollection"></param>
|
|
/// <param name="toStringValue"></param>
|
|
/// <param name="pktInfo">
|
|
/// TypeSerializationInfo for the primitive.
|
|
/// </param>
|
|
/// <param name="streamName"></param>
|
|
/// <param name="property"></param>
|
|
/// <param name="depth"></param>
|
|
private void WritePrimitiveTypePSObjectWithNotes
|
|
(
|
|
PSObject source,
|
|
object primitive,
|
|
bool hasModifiedTypesCollection,
|
|
string toStringValue,
|
|
TypeSerializationInfo pktInfo,
|
|
string streamName,
|
|
string property,
|
|
int depth
|
|
)
|
|
{
|
|
Dbg.Assert(source != null, "caller should validate the parameter");
|
|
Dbg.Assert(pktInfo != null, "Caller should validate pktInfo != null");
|
|
|
|
string refId = _objectRefIdHandler.SetRefId(source);
|
|
WriteStartOfPSObject(
|
|
source,
|
|
streamName,
|
|
property,
|
|
refId,
|
|
hasModifiedTypesCollection, // preserve TypeNames information if different from the primitive type
|
|
toStringValue); // preserve ToString information only if got it from deserialization or overridden by PSObject
|
|
// (example where preservation of TypeNames and ToString is needed: enums serialized as ints, help string with custom type names (HelpInfoShort))
|
|
|
|
if (pktInfo != null)
|
|
{
|
|
WriteOnePrimitiveKnownType(this, streamName, null, primitive, pktInfo);
|
|
}
|
|
|
|
// serialize only instance properties - members from type table are
|
|
// always going to be available for known primitive types
|
|
SerializeInstanceProperties(source, depth);
|
|
|
|
_writer.WriteEndElement();
|
|
}
|
|
|
|
private void HandleComplexTypePSObject
|
|
(
|
|
object source,
|
|
string streamName,
|
|
string property,
|
|
int depth
|
|
)
|
|
{
|
|
Dbg.Assert(source != null, "caller should validate the parameter");
|
|
PSObject mshSource = PSObject.AsPSObject(source);
|
|
|
|
// Figure out what kind of object we are dealing with
|
|
bool isErrorRecord = false;
|
|
bool isInformationalRecord = false;
|
|
bool isEnum = false;
|
|
bool isPSObject = false;
|
|
bool isCimInstance = false;
|
|
|
|
if (!mshSource.ImmediateBaseObjectIsEmpty)
|
|
{
|
|
do // false loop
|
|
{
|
|
CimInstance cimInstance = mshSource.ImmediateBaseObject as CimInstance;
|
|
if (cimInstance != null)
|
|
{
|
|
isCimInstance = true;
|
|
break;
|
|
}
|
|
|
|
ErrorRecord errorRecord = mshSource.ImmediateBaseObject as ErrorRecord;
|
|
if (errorRecord != null)
|
|
{
|
|
errorRecord.ToPSObjectForRemoting(mshSource);
|
|
isErrorRecord = true;
|
|
break;
|
|
}
|
|
|
|
InformationalRecord informationalRecord = mshSource.ImmediateBaseObject as InformationalRecord;
|
|
if (informationalRecord != null)
|
|
{
|
|
informationalRecord.ToPSObjectForRemoting(mshSource);
|
|
isInformationalRecord = true;
|
|
break;
|
|
}
|
|
|
|
isEnum = mshSource.ImmediateBaseObject is Enum;
|
|
isPSObject = mshSource.ImmediateBaseObject is PSObject;
|
|
} while (false);
|
|
}
|
|
|
|
bool writeToString = true;
|
|
if (mshSource.ToStringFromDeserialization == null) // continue to write ToString from deserialized objects, but...
|
|
{
|
|
if (mshSource.ImmediateBaseObjectIsEmpty) // ... don't write ToString for property bags
|
|
{
|
|
writeToString = false;
|
|
}
|
|
}
|
|
|
|
string refId = _objectRefIdHandler.SetRefId(source);
|
|
WriteStartOfPSObject(
|
|
mshSource,
|
|
streamName,
|
|
property,
|
|
refId,
|
|
true, // always write TypeNames for complex objects
|
|
writeToString ? SerializationUtilities.GetToString(mshSource) : null);
|
|
|
|
PSMemberInfoInternalCollection<PSPropertyInfo> specificPropertiesToSerialize = SerializationUtilities.GetSpecificPropertiesToSerialize(mshSource, AllPropertiesCollection, _typeTable);
|
|
|
|
if (isEnum)
|
|
{
|
|
object baseObject = mshSource.ImmediateBaseObject;
|
|
WriteOneObject(System.Convert.ChangeType(baseObject, Enum.GetUnderlyingType(baseObject.GetType()), System.Globalization.CultureInfo.InvariantCulture), null, null, depth);
|
|
}
|
|
else if (isPSObject)
|
|
{
|
|
WriteOneObject(mshSource.ImmediateBaseObject, null, null, depth);
|
|
}
|
|
else if (isErrorRecord || isInformationalRecord)
|
|
{
|
|
// nothing to do
|
|
}
|
|
else
|
|
{
|
|
WritePSObjectProperties(mshSource, depth, specificPropertiesToSerialize);
|
|
}
|
|
|
|
if (isCimInstance)
|
|
{
|
|
CimInstance cimInstance = mshSource.ImmediateBaseObject as CimInstance;
|
|
PrepareCimInstanceForSerialization(mshSource, cimInstance);
|
|
}
|
|
|
|
SerializeExtendedProperties(mshSource, depth, specificPropertiesToSerialize);
|
|
|
|
_writer.WriteEndElement();
|
|
}
|
|
|
|
private static readonly Lazy<CimSerializer> s_cimSerializer = new Lazy<CimSerializer>(CimSerializer.Create);
|
|
|
|
private void PrepareCimInstanceForSerialization(PSObject psObject, CimInstance cimInstance)
|
|
{
|
|
Queue<CimClassSerializationId> serializedClasses = new Queue<CimClassSerializationId>();
|
|
|
|
//
|
|
// CREATE SERIALIZED FORM OF THE CLASS METADATA
|
|
//
|
|
ArrayList psoClasses = new ArrayList();
|
|
for (CimClass cimClass = cimInstance.CimClass; cimClass != null; cimClass = cimClass.CimSuperClass)
|
|
{
|
|
PSObject psoClass = new PSObject();
|
|
psoClass.TypeNames.Clear();
|
|
psoClasses.Add(psoClass);
|
|
|
|
psoClass.Properties.Add(new PSNoteProperty(InternalDeserializer.CimClassNameProperty, cimClass.CimSystemProperties.ClassName));
|
|
psoClass.Properties.Add(new PSNoteProperty(InternalDeserializer.CimNamespaceProperty, cimClass.CimSystemProperties.Namespace));
|
|
psoClass.Properties.Add(new PSNoteProperty(InternalDeserializer.CimServerNameProperty, cimClass.CimSystemProperties.ServerName));
|
|
psoClass.Properties.Add(new PSNoteProperty(InternalDeserializer.CimHashCodeProperty, cimClass.GetHashCode()));
|
|
|
|
CimClassSerializationId cimClassSerializationId = new CimClassSerializationId(
|
|
cimClass.CimSystemProperties.ClassName,
|
|
cimClass.CimSystemProperties.Namespace,
|
|
cimClass.CimSystemProperties.ServerName,
|
|
cimClass.GetHashCode());
|
|
if (_context.cimClassSerializationIdCache.DoesDeserializerAlreadyHaveCimClass(cimClassSerializationId))
|
|
{
|
|
break;
|
|
}
|
|
|
|
serializedClasses.Enqueue(cimClassSerializationId);
|
|
byte[] miXmlBytes = s_cimSerializer.Value.Serialize(cimClass, ClassSerializationOptions.None);
|
|
string miXmlString = Encoding.Unicode.GetString(miXmlBytes, 0, miXmlBytes.Length);
|
|
psoClass.Properties.Add(new PSNoteProperty(InternalDeserializer.CimMiXmlProperty, miXmlString));
|
|
}
|
|
|
|
psoClasses.Reverse();
|
|
|
|
//
|
|
// UPDATE CLASSDECL CACHE
|
|
//
|
|
foreach (CimClassSerializationId serializedClassId in serializedClasses)
|
|
{
|
|
_context.cimClassSerializationIdCache.AddClassToCache(serializedClassId);
|
|
}
|
|
|
|
//
|
|
// ATTACH CLASS METADATA TO THE OBJECT BEING SERIALIZED
|
|
//
|
|
PSPropertyInfo classMetadataProperty = psObject.Properties[InternalDeserializer.CimClassMetadataProperty];
|
|
if (classMetadataProperty != null)
|
|
{
|
|
classMetadataProperty.Value = psoClasses;
|
|
}
|
|
else
|
|
{
|
|
PSNoteProperty classMetadataNote = new PSNoteProperty(
|
|
InternalDeserializer.CimClassMetadataProperty,
|
|
psoClasses);
|
|
classMetadataNote.IsHidden = true;
|
|
psObject.Properties.Add(classMetadataNote);
|
|
}
|
|
|
|
// ATTACH INSTANCE METADATA TO THE OBJECT BEING SERIALIZED
|
|
List<string> namesOfModifiedProperties = cimInstance
|
|
.CimInstanceProperties
|
|
.Where(static p => p.IsValueModified)
|
|
.Select(static p => p.Name)
|
|
.ToList();
|
|
if (namesOfModifiedProperties.Count != 0)
|
|
{
|
|
PSObject instanceMetadata = new PSObject();
|
|
PSPropertyInfo instanceMetadataProperty = psObject.Properties[InternalDeserializer.CimInstanceMetadataProperty];
|
|
if (instanceMetadataProperty != null)
|
|
{
|
|
instanceMetadataProperty.Value = instanceMetadata;
|
|
}
|
|
else
|
|
{
|
|
PSNoteProperty instanceMetadataNote = new PSNoteProperty(InternalDeserializer.CimInstanceMetadataProperty, instanceMetadata);
|
|
instanceMetadataNote.IsHidden = true;
|
|
psObject.Properties.Add(instanceMetadataNote);
|
|
}
|
|
|
|
instanceMetadata.InternalTypeNames = ConsolidatedString.Empty;
|
|
|
|
instanceMetadata.Properties.Add(
|
|
new PSNoteProperty(
|
|
InternalDeserializer.CimModifiedProperties,
|
|
string.Join(" ", namesOfModifiedProperties)));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes start element, attributes and typeNames for PSObject.
|
|
/// </summary>
|
|
/// <param name="mshObject"></param>
|
|
/// <param name="streamName"></param>
|
|
/// <param name="property"></param>
|
|
/// <param name="refId"></param>
|
|
/// <param name="writeTypeNames">If true, TypeName information is written, else not.</param>
|
|
/// <param name="toStringValue">If not null then ToString information is written.</param>
|
|
private void WriteStartOfPSObject
|
|
(
|
|
PSObject mshObject,
|
|
string streamName,
|
|
string property,
|
|
string refId,
|
|
bool writeTypeNames,
|
|
string toStringValue
|
|
)
|
|
{
|
|
Dbg.Assert(mshObject != null, "caller should validate the parameter");
|
|
|
|
// Write PSObject start element.
|
|
WriteStartElement(SerializationStrings.PSObjectTag);
|
|
|
|
if (streamName != null)
|
|
{
|
|
WriteAttribute(SerializationStrings.StreamNameAttribute, streamName);
|
|
}
|
|
|
|
if (property != null)
|
|
{
|
|
WriteNameAttribute(property);
|
|
}
|
|
|
|
if (refId != null)
|
|
{
|
|
WriteAttribute(SerializationStrings.ReferenceIdAttribute, refId);
|
|
}
|
|
|
|
if (writeTypeNames)
|
|
{
|
|
// Write TypeNames
|
|
ConsolidatedString typeNames = mshObject.InternalTypeNames;
|
|
if (typeNames.Count > 0)
|
|
{
|
|
string typeNameHierarchyReferenceId = _typeRefIdHandler.GetRefId(typeNames);
|
|
if (typeNameHierarchyReferenceId == null)
|
|
{
|
|
WriteStartElement(SerializationStrings.TypeNamesTag);
|
|
|
|
// Create a new refId and write it as attribute
|
|
string tnRefId = _typeRefIdHandler.SetRefId(typeNames);
|
|
Dbg.Assert(tnRefId != null, "SetRefId should always succeed for strings");
|
|
WriteAttribute(SerializationStrings.ReferenceIdAttribute, tnRefId);
|
|
|
|
foreach (string type in typeNames)
|
|
{
|
|
WriteEncodedElementString(SerializationStrings.TypeNamesItemTag, type);
|
|
}
|
|
|
|
_writer.WriteEndElement();
|
|
}
|
|
else
|
|
{
|
|
WriteStartElement(SerializationStrings.TypeNamesReferenceTag);
|
|
WriteAttribute(SerializationStrings.ReferenceIdAttribute, typeNameHierarchyReferenceId);
|
|
_writer.WriteEndElement();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (toStringValue != null)
|
|
{
|
|
WriteEncodedElementString(SerializationStrings.ToStringElementTag, toStringValue);
|
|
}
|
|
}
|
|
|
|
#region membersets
|
|
|
|
/// <summary>
|
|
/// Returns true if PSObject has notes.
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <returns>
|
|
/// </returns>
|
|
private static bool PSObjectHasNotes(PSObject source)
|
|
{
|
|
Dbg.Assert(source != null, "Caller should validate the parameter");
|
|
if (source.InstanceMembers != null && source.InstanceMembers.Count > 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private bool? _canUseDefaultRunspaceInThreadSafeManner;
|
|
|
|
private bool CanUseDefaultRunspaceInThreadSafeManner
|
|
{
|
|
get
|
|
{
|
|
if (!_canUseDefaultRunspaceInThreadSafeManner.HasValue)
|
|
{
|
|
_canUseDefaultRunspaceInThreadSafeManner = Runspace.CanUseDefaultRunspace;
|
|
}
|
|
|
|
return _canUseDefaultRunspaceInThreadSafeManner.Value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serialize member set. This method serializes without writing
|
|
/// enclosing tags and attributes.
|
|
/// </summary>
|
|
/// <param name="me">
|
|
/// enumerable containing members
|
|
/// </param>
|
|
/// <param name="depth"></param>
|
|
/// <param name="writeEnclosingMemberSetElementTag">
|
|
/// if this is true, write an enclosing "<memberset></memberset>" tag.
|
|
/// </param>
|
|
/// <returns></returns>
|
|
private void WriteMemberInfoCollection
|
|
(
|
|
IEnumerable<PSMemberInfo> me,
|
|
int depth,
|
|
bool writeEnclosingMemberSetElementTag
|
|
)
|
|
{
|
|
Dbg.Assert(me != null, "caller should validate the parameter");
|
|
|
|
bool enclosingTagWritten = false;
|
|
foreach (PSMemberInfo info in me)
|
|
{
|
|
if (!info.ShouldSerialize)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
int depthOfMember = info.IsInstance ? depth : depth - 1;
|
|
|
|
if (info.MemberType == (info.MemberType & PSMemberTypes.Properties))
|
|
{
|
|
bool gotValue;
|
|
object value = SerializationUtilities.GetPropertyValueInThreadSafeManner((PSPropertyInfo)info, this.CanUseDefaultRunspaceInThreadSafeManner, out gotValue);
|
|
if (gotValue)
|
|
{
|
|
if (writeEnclosingMemberSetElementTag && !enclosingTagWritten)
|
|
{
|
|
enclosingTagWritten = true;
|
|
WriteStartElement(SerializationStrings.MemberSet);
|
|
}
|
|
|
|
WriteOneObject(value, null, info.Name, depthOfMember);
|
|
}
|
|
}
|
|
else if (info.MemberType == PSMemberTypes.MemberSet)
|
|
{
|
|
if (writeEnclosingMemberSetElementTag && !enclosingTagWritten)
|
|
{
|
|
enclosingTagWritten = true;
|
|
WriteStartElement(SerializationStrings.MemberSet);
|
|
}
|
|
|
|
WriteMemberSet((PSMemberSet)info, depthOfMember);
|
|
}
|
|
}
|
|
|
|
if (enclosingTagWritten)
|
|
{
|
|
_writer.WriteEndElement();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serializes MemberSet.
|
|
/// </summary>
|
|
private void WriteMemberSet
|
|
(
|
|
PSMemberSet set,
|
|
int depth
|
|
)
|
|
{
|
|
Dbg.Assert(set != null, "Caller should validate the parameter");
|
|
|
|
if (!set.ShouldSerialize)
|
|
{
|
|
return;
|
|
}
|
|
|
|
WriteStartElement(SerializationStrings.MemberSet);
|
|
WriteNameAttribute(set.Name);
|
|
WriteMemberInfoCollection(set.Members, depth, false);
|
|
_writer.WriteEndElement();
|
|
}
|
|
|
|
#endregion membersets
|
|
|
|
#region properties
|
|
|
|
/// <summary>
|
|
/// Serializes properties of PSObject.
|
|
/// </summary>
|
|
private void WritePSObjectProperties
|
|
(
|
|
PSObject source,
|
|
int depth,
|
|
IEnumerable<PSPropertyInfo> specificPropertiesToSerialize
|
|
)
|
|
{
|
|
Dbg.Assert(source != null, "caller should validate the information");
|
|
|
|
// Depth available for each property is one less
|
|
--depth;
|
|
Dbg.Assert(depth >= 0, "depth should be greater or equal to zero");
|
|
|
|
if (specificPropertiesToSerialize != null)
|
|
{
|
|
SerializeProperties(specificPropertiesToSerialize, SerializationStrings.AdapterProperties, depth);
|
|
}
|
|
else
|
|
{
|
|
if (source.ShouldSerializeAdapter())
|
|
{
|
|
IEnumerable<PSPropertyInfo> adapterCollection = null;
|
|
adapterCollection = source.GetAdaptedProperties();
|
|
if (adapterCollection != null)
|
|
{
|
|
SerializeProperties(adapterCollection, SerializationStrings.AdapterProperties, depth);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void SerializeInstanceProperties
|
|
(
|
|
PSObject source,
|
|
int depth
|
|
)
|
|
{
|
|
// Serialize instanceMembers
|
|
Dbg.Assert(source != null, "caller should validate the information");
|
|
PSMemberInfoCollection<PSMemberInfo> instanceMembers = source.InstanceMembers;
|
|
if (instanceMembers != null)
|
|
{
|
|
WriteMemberInfoCollection(instanceMembers, depth, true);
|
|
}
|
|
}
|
|
|
|
private Collection<CollectionEntry<PSMemberInfo>> _extendedMembersCollection;
|
|
|
|
private Collection<CollectionEntry<PSMemberInfo>> ExtendedMembersCollection
|
|
{
|
|
get
|
|
{
|
|
return _extendedMembersCollection ??=
|
|
PSObject.GetMemberCollection(PSMemberViewTypes.Extended, _typeTable);
|
|
}
|
|
}
|
|
|
|
private Collection<CollectionEntry<PSPropertyInfo>> _allPropertiesCollection;
|
|
|
|
private Collection<CollectionEntry<PSPropertyInfo>> AllPropertiesCollection
|
|
{
|
|
get
|
|
{
|
|
return _allPropertiesCollection ??= PSObject.GetPropertyCollection(PSMemberViewTypes.All, _typeTable);
|
|
}
|
|
}
|
|
|
|
private void SerializeExtendedProperties
|
|
(
|
|
PSObject source,
|
|
int depth,
|
|
IEnumerable<PSPropertyInfo> specificPropertiesToSerialize
|
|
)
|
|
{
|
|
Dbg.Assert(source != null, "caller should validate the information");
|
|
|
|
IEnumerable<PSMemberInfo> extendedMembersEnumerable = null;
|
|
if (specificPropertiesToSerialize == null)
|
|
{
|
|
// Get only extended members including hidden members from the psobect source.
|
|
PSMemberInfoIntegratingCollection<PSMemberInfo> membersToSearch =
|
|
new PSMemberInfoIntegratingCollection<PSMemberInfo>(source, ExtendedMembersCollection);
|
|
extendedMembersEnumerable = membersToSearch.Match(
|
|
"*",
|
|
PSMemberTypes.Properties | PSMemberTypes.PropertySet | PSMemberTypes.MemberSet,
|
|
MshMemberMatchOptions.IncludeHidden | MshMemberMatchOptions.OnlySerializable);
|
|
}
|
|
else
|
|
{
|
|
List<PSMemberInfo> extendedMembersList = new List<PSMemberInfo>(source.InstanceMembers);
|
|
extendedMembersEnumerable = extendedMembersList;
|
|
|
|
foreach (PSMemberInfo member in specificPropertiesToSerialize)
|
|
{
|
|
if (member.IsInstance)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (member is PSProperty)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
extendedMembersList.Add(member);
|
|
}
|
|
}
|
|
|
|
if (extendedMembersEnumerable != null)
|
|
{
|
|
WriteMemberInfoCollection(extendedMembersEnumerable, depth, true);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serializes properties from collection.
|
|
/// </summary>
|
|
/// <param name="propertyCollection">
|
|
/// Collection of properties to serialize
|
|
/// </param>
|
|
/// <param name="name">
|
|
/// Name for enclosing element tag
|
|
/// </param>
|
|
/// <param name="depth">
|
|
/// depth to which each property should be
|
|
/// serialized
|
|
/// </param>
|
|
private void SerializeProperties
|
|
(
|
|
IEnumerable<PSPropertyInfo> propertyCollection,
|
|
string name,
|
|
int depth
|
|
)
|
|
{
|
|
Dbg.Assert(propertyCollection != null, "caller should validate the parameter");
|
|
bool startElementWritten = false;
|
|
|
|
foreach (PSMemberInfo info in propertyCollection)
|
|
{
|
|
if (!(info is PSProperty prop))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!startElementWritten)
|
|
{
|
|
WriteStartElement(name);
|
|
startElementWritten = true;
|
|
}
|
|
|
|
bool success;
|
|
object value = SerializationUtilities.GetPropertyValueInThreadSafeManner(prop, this.CanUseDefaultRunspaceInThreadSafeManner, out success);
|
|
if (success)
|
|
{
|
|
WriteOneObject(value, null, prop.Name, depth);
|
|
}
|
|
}
|
|
|
|
if (startElementWritten)
|
|
{
|
|
_writer.WriteEndElement();
|
|
}
|
|
}
|
|
|
|
#endregion base properties
|
|
|
|
#endregion WritePSObject
|
|
|
|
#region enumerable and dictionary
|
|
|
|
/// <summary>
|
|
/// Serializes IEnumerable.
|
|
/// </summary>
|
|
/// <param name="enumerable">
|
|
/// enumerable which is serialized
|
|
/// </param>
|
|
/// <param name="tag">
|
|
/// </param>
|
|
/// <param name="depth"></param>
|
|
private void WriteEnumerable
|
|
(
|
|
IEnumerable enumerable,
|
|
string tag,
|
|
int depth
|
|
)
|
|
{
|
|
Dbg.Assert(enumerable != null, "caller should validate the parameter");
|
|
Dbg.Assert(!string.IsNullOrEmpty(tag), "caller should validate the parameter");
|
|
|
|
// Start element
|
|
WriteStartElement(tag);
|
|
|
|
IEnumerator enumerator = null;
|
|
try
|
|
{
|
|
enumerator = enumerable.GetEnumerator();
|
|
try
|
|
{
|
|
enumerator.Reset();
|
|
}
|
|
catch (System.NotSupportedException)
|
|
{
|
|
// ignore exceptions thrown when the enumerator doesn't support Reset() method as in win8:948569
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
// Catch-all OK. This is a third-party call-out.
|
|
PSEtwLog.LogAnalyticWarning(
|
|
PSEventId.Serializer_EnumerationFailed, PSOpcode.Exception, PSTask.Serialization,
|
|
PSKeyword.Serializer | PSKeyword.UseAlwaysAnalytic,
|
|
enumerable.GetType().AssemblyQualifiedName,
|
|
exception.ToString());
|
|
|
|
enumerator = null;
|
|
}
|
|
|
|
// AD has incorrect implementation of IEnumerable where they returned null
|
|
// for GetEnumerator instead of empty enumerator
|
|
if (enumerator != null)
|
|
{
|
|
while (true)
|
|
{
|
|
object item = null;
|
|
try
|
|
{
|
|
if (!enumerator.MoveNext())
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
item = enumerator.Current;
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
// Catch-all OK. This is a third-party call-out.
|
|
PSEtwLog.LogAnalyticWarning(
|
|
PSEventId.Serializer_EnumerationFailed, PSOpcode.Exception, PSTask.Serialization,
|
|
PSKeyword.Serializer | PSKeyword.UseAlwaysAnalytic,
|
|
enumerable.GetType().AssemblyQualifiedName,
|
|
exception.ToString());
|
|
|
|
break;
|
|
}
|
|
|
|
WriteOneObject(item, null, null, depth);
|
|
}
|
|
}
|
|
// End element
|
|
_writer.WriteEndElement();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serializes IDictionary.
|
|
/// </summary>
|
|
/// <param name="dictionary">Dictionary which is serialized.</param>
|
|
/// <param name="tag"></param>
|
|
/// <param name="depth"></param>
|
|
private void WriteDictionary
|
|
(
|
|
IDictionary dictionary,
|
|
string tag,
|
|
int depth
|
|
)
|
|
{
|
|
Dbg.Assert(dictionary != null, "caller should validate the parameter");
|
|
Dbg.Assert(!string.IsNullOrEmpty(tag), "caller should validate the parameter");
|
|
|
|
// Start element
|
|
WriteStartElement(tag);
|
|
|
|
IDictionaryEnumerator dictionaryEnum = null;
|
|
try
|
|
{
|
|
dictionaryEnum = dictionary.GetEnumerator();
|
|
}
|
|
catch (Exception exception) // ignore non-severe exceptions
|
|
{
|
|
// Catch-all OK. This is a third-party call-out.
|
|
PSEtwLog.LogAnalyticWarning(
|
|
PSEventId.Serializer_EnumerationFailed, PSOpcode.Exception, PSTask.Serialization,
|
|
PSKeyword.Serializer | PSKeyword.UseAlwaysAnalytic,
|
|
dictionary.GetType().AssemblyQualifiedName,
|
|
exception.ToString());
|
|
}
|
|
|
|
if (dictionaryEnum != null)
|
|
{
|
|
while (true)
|
|
{
|
|
object key = null;
|
|
object value = null;
|
|
try
|
|
{
|
|
if (!dictionaryEnum.MoveNext())
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
key = dictionaryEnum.Key;
|
|
value = dictionaryEnum.Value;
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
// Catch-all OK. This is a third-party call-out.
|
|
PSEtwLog.LogAnalyticWarning(
|
|
PSEventId.Serializer_EnumerationFailed, PSOpcode.Exception, PSTask.Serialization,
|
|
PSKeyword.Serializer | PSKeyword.UseAlwaysAnalytic,
|
|
dictionary.GetType().AssemblyQualifiedName,
|
|
exception.ToString());
|
|
|
|
break;
|
|
}
|
|
|
|
Dbg.Assert(key != null, "Dictionary keys should never be null");
|
|
if (key == null) break;
|
|
|
|
WriteStartElement(SerializationStrings.DictionaryEntryTag);
|
|
WriteOneObject(key, null, SerializationStrings.DictionaryKey, depth);
|
|
WriteOneObject(value, null, SerializationStrings.DictionaryValue, depth);
|
|
_writer.WriteEndElement();
|
|
}
|
|
}
|
|
|
|
// End element
|
|
_writer.WriteEndElement();
|
|
}
|
|
|
|
#endregion enumerable and dictionary
|
|
|
|
#region serialize as string
|
|
|
|
private void HandlePSObjectAsString(
|
|
PSObject source,
|
|
string streamName,
|
|
string property,
|
|
int depth)
|
|
{
|
|
Dbg.Assert(source != null, "caller should validate the information");
|
|
|
|
string value = GetSerializationString(source);
|
|
|
|
TypeSerializationInfo pktInfo = null;
|
|
if (value != null)
|
|
{
|
|
pktInfo = KnownTypes.GetTypeSerializationInfo(value.GetType());
|
|
}
|
|
|
|
WritePrimitiveTypePSObject(source, value, pktInfo, streamName, property, depth);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the string from PSObject using the information from
|
|
/// types.ps1xml.
|
|
/// This string is used for serializing the PSObject at depth 0
|
|
/// or when pso.SerializationMethod == SerializationMethod.String.
|
|
/// </summary>
|
|
/// <param name="source">
|
|
/// PSObject to be converted to string
|
|
/// </param>
|
|
/// <returns>
|
|
/// string value to use for serializing this PSObject.
|
|
/// </returns>
|
|
private string GetSerializationString(PSObject source)
|
|
{
|
|
Dbg.Assert(source != null, "caller should have validated the information");
|
|
|
|
PSPropertyInfo serializationProperty = null;
|
|
try
|
|
{
|
|
serializationProperty = source.GetStringSerializationSource(_typeTable);
|
|
}
|
|
catch (ExtendedTypeSystemException e)
|
|
{
|
|
PSEtwLog.LogAnalyticWarning(
|
|
PSEventId.Serializer_ToStringFailed, PSOpcode.Exception, PSTask.Serialization,
|
|
PSKeyword.Serializer | PSKeyword.UseAlwaysAnalytic,
|
|
source.GetType().AssemblyQualifiedName,
|
|
e.InnerException != null ? e.InnerException.ToString() : e.ToString());
|
|
}
|
|
|
|
string result = null;
|
|
if (serializationProperty != null)
|
|
{
|
|
bool success;
|
|
object val = SerializationUtilities.GetPropertyValueInThreadSafeManner(serializationProperty, this.CanUseDefaultRunspaceInThreadSafeManner, out success);
|
|
if (success && (val != null))
|
|
{
|
|
result = SerializationUtilities.GetToString(val);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result = SerializationUtilities.GetToString(source);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reads the information the PSObject
|
|
/// and returns true if this object should be serialized as
|
|
/// string.
|
|
/// </summary>
|
|
/// <param name="source">PSObject to be serialized.</param>
|
|
/// <returns>True if the object needs to be serialized as a string.</returns>
|
|
private bool SerializeAsString(PSObject source)
|
|
{
|
|
SerializationMethod method = source.GetSerializationMethod(_typeTable);
|
|
if (method == SerializationMethod.String)
|
|
{
|
|
PSEtwLog.LogAnalyticVerbose(
|
|
PSEventId.Serializer_ModeOverride, PSOpcode.SerializationSettings, PSTask.Serialization,
|
|
PSKeyword.Serializer | PSKeyword.UseAlwaysAnalytic,
|
|
source.InternalTypeNames.Key,
|
|
(UInt32)(SerializationMethod.String));
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#endregion serialize as string
|
|
|
|
/// <summary>
|
|
/// Compute the serialization depth for an PSObject instance subtree.
|
|
/// </summary>
|
|
/// <param name="source">PSObject whose serialization depth has to be computed.</param>
|
|
/// <param name="depth">Current depth.</param>
|
|
/// <returns></returns>
|
|
private int GetDepthOfSerialization(object source, int depth)
|
|
{
|
|
Dbg.Assert(source != null, "Caller should verify source != null");
|
|
|
|
PSObject pso = PSObject.AsPSObject(source);
|
|
if (pso == null)
|
|
{
|
|
return depth;
|
|
}
|
|
|
|
if (pso.BaseObject is CimInstance)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
if (pso.BaseObject is PSCredential)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
if (pso.BaseObject is PSSenderInfo)
|
|
{
|
|
return 4;
|
|
}
|
|
|
|
if (pso.BaseObject is SwitchParameter)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
if ((_context.options & SerializationOptions.UseDepthFromTypes) != 0)
|
|
{
|
|
// get the depth from the PSObject
|
|
// NOTE: we assume that the depth out of the PSObject is > 0
|
|
// else we consider it not set in types.ps1xml
|
|
int typesPs1xmlDepth = pso.GetSerializationDepth(_typeTable);
|
|
if (typesPs1xmlDepth > 0)
|
|
{
|
|
if (typesPs1xmlDepth != depth)
|
|
{
|
|
PSEtwLog.LogAnalyticVerbose(
|
|
PSEventId.Serializer_DepthOverride, PSOpcode.SerializationSettings, PSTask.Serialization,
|
|
PSKeyword.Serializer | PSKeyword.UseAlwaysAnalytic,
|
|
pso.InternalTypeNames.Key, depth, typesPs1xmlDepth, _depthBelowTopLevel);
|
|
|
|
return typesPs1xmlDepth;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((_context.options & SerializationOptions.PreserveSerializationSettingOfOriginal) != 0)
|
|
{
|
|
if ((pso.IsDeserialized) && (depth <= 0))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return depth;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes null.
|
|
/// </summary>
|
|
/// <param name="streamName"></param>
|
|
/// <param name="property"></param>
|
|
private void WriteNull(string streamName, string property)
|
|
{
|
|
WriteStartElement(SerializationStrings.NilTag);
|
|
|
|
if (streamName != null)
|
|
{
|
|
WriteAttribute(SerializationStrings.StreamNameAttribute, streamName);
|
|
}
|
|
|
|
if (property != null)
|
|
{
|
|
WriteNameAttribute(property);
|
|
}
|
|
|
|
_writer.WriteEndElement();
|
|
}
|
|
|
|
#region known type serialization
|
|
|
|
/// <summary>
|
|
/// Writes raw string as item or property in Monad namespace.
|
|
/// </summary>
|
|
/// <param name="serializer">The serializer to which the object is serialized.</param>
|
|
/// <param name="streamName">Name of the stream to write. Do not write if null.</param>
|
|
/// <param name="property">Name of property. Pass null for item.</param>
|
|
/// <param name="raw">String to write.</param>
|
|
/// <param name="entry">Serialization information.</param>
|
|
private static void WriteRawString
|
|
(
|
|
InternalSerializer serializer,
|
|
string streamName,
|
|
string property,
|
|
string raw,
|
|
TypeSerializationInfo entry
|
|
)
|
|
{
|
|
Dbg.Assert(serializer != null, "caller should have validated the information");
|
|
Dbg.Assert(raw != null, "caller should have validated the information");
|
|
Dbg.Assert(entry != null, "caller should have validated the information");
|
|
|
|
if (property != null)
|
|
{
|
|
serializer.WriteStartElement(entry.PropertyTag);
|
|
serializer.WriteNameAttribute(property);
|
|
}
|
|
else
|
|
{
|
|
serializer.WriteStartElement(entry.ItemTag);
|
|
}
|
|
|
|
if (streamName != null)
|
|
{
|
|
serializer.WriteAttribute(SerializationStrings.StreamNameAttribute, streamName);
|
|
}
|
|
|
|
serializer._writer.WriteRaw(raw);
|
|
serializer._writer.WriteEndElement();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes an item or property in Monad namespace.
|
|
/// </summary>
|
|
/// <param name="serializer">The serializer to which the object is serialized.</param>
|
|
/// <param name="streamName"></param>
|
|
/// <param name="property">Name of property. Pass null for item.</param>
|
|
/// <param name="source">Object to be written.</param>
|
|
/// <param name="entry">Serialization information about source.</param>
|
|
private static void WriteOnePrimitiveKnownType
|
|
(
|
|
InternalSerializer serializer,
|
|
string streamName,
|
|
string property,
|
|
object source,
|
|
TypeSerializationInfo entry
|
|
)
|
|
{
|
|
Dbg.Assert(serializer != null, "caller should have validated the information");
|
|
Dbg.Assert(source != null, "caller should have validated the information");
|
|
Dbg.Assert(entry != null, "caller should have validated the information");
|
|
|
|
if (entry.Serializer == null)
|
|
{
|
|
// we are not using GetToString, because we assume that
|
|
// ToString() for primitive types never throws
|
|
string value = Convert.ToString(source, CultureInfo.InvariantCulture);
|
|
Dbg.Assert(value != null, "ToString shouldn't return null for primitive types");
|
|
WriteRawString(serializer, streamName, property, value, entry);
|
|
}
|
|
else
|
|
{
|
|
entry.Serializer(serializer, streamName, property, source, entry);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes DateTime as item or property.
|
|
/// </summary>
|
|
/// <param name="serializer">The serializer to which the object is serialized.</param>
|
|
/// <param name="streamName"></param>
|
|
/// <param name="property">Name of property. pass null for item.</param>
|
|
/// <param name="source">DateTime to write.</param>
|
|
/// <param name="entry">Serialization information about source.</param>
|
|
internal static void WriteDateTime(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry)
|
|
{
|
|
Dbg.Assert(serializer != null, "caller should have validated the information");
|
|
Dbg.Assert(source != null, "caller should have validated the information");
|
|
Dbg.Assert(entry != null, "caller should have validated the information");
|
|
|
|
WriteRawString(serializer, streamName, property, XmlConvert.ToString((DateTime)source, XmlDateTimeSerializationMode.RoundtripKind), entry);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes Version.
|
|
/// </summary>
|
|
/// <param name="serializer">The serializer to which the object is serialized.</param>
|
|
/// <param name="streamName"></param>
|
|
/// <param name="property">Name of property. pass null for item.</param>
|
|
/// <param name="source">Version to write.</param>
|
|
/// <param name="entry">Serialization information about source.</param>
|
|
internal static void WriteVersion(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry)
|
|
{
|
|
Dbg.Assert(serializer != null, "caller should have validated the information");
|
|
Dbg.Assert(source != null, "caller should have validated the information");
|
|
Dbg.Assert(source is Version, "Caller should verify that typeof(source) is Version");
|
|
Dbg.Assert(entry != null, "caller should have validated the information");
|
|
|
|
WriteRawString(serializer, streamName, property, Convert.ToString(source, CultureInfo.InvariantCulture), entry);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes SemanticVersion.
|
|
/// </summary>
|
|
/// <param name="serializer">The serializer to which the object is serialized.</param>
|
|
/// <param name="streamName"></param>
|
|
/// <param name="property">Name of property. pass null for item.</param>
|
|
/// <param name="source">Version to write.</param>
|
|
/// <param name="entry">Serialization information about source.</param>
|
|
internal static void WriteSemanticVersion(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry)
|
|
{
|
|
Dbg.Assert(serializer != null, "caller should have validated the information");
|
|
Dbg.Assert(source != null, "caller should have validated the information");
|
|
Dbg.Assert(source is SemanticVersion, "Caller should verify that typeof(source) is Version");
|
|
Dbg.Assert(entry != null, "caller should have validated the information");
|
|
|
|
WriteRawString(serializer, streamName, property, Convert.ToString(source, CultureInfo.InvariantCulture), entry);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serialize scriptblock as item or property.
|
|
/// </summary>
|
|
/// <param name="serializer">The serializer to which the object is serialized.</param>
|
|
/// <param name="streamName"></param>
|
|
/// <param name="property">Name of property. pass null for item.</param>
|
|
/// <param name="source">Scriptblock to write.</param>
|
|
/// <param name="entry">Serialization information about source.</param>
|
|
internal static void WriteScriptBlock(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry)
|
|
{
|
|
Dbg.Assert(serializer != null, "caller should have validated the information");
|
|
Dbg.Assert(source != null, "caller should have validated the information");
|
|
Dbg.Assert(source is ScriptBlock, "Caller should verify that typeof(source) is ScriptBlock");
|
|
Dbg.Assert(entry != null, "caller should have validated the information");
|
|
|
|
WriteEncodedString(serializer, streamName, property, Convert.ToString(source, CultureInfo.InvariantCulture), entry);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serialize URI as item or property.
|
|
/// </summary>
|
|
/// <param name="serializer">The serializer to which the object is serialized.</param>
|
|
/// <param name="streamName"></param>
|
|
/// <param name="property">Name of property. pass null for item.</param>
|
|
/// <param name="source">URI to write.</param>
|
|
/// <param name="entry">Serialization information about source.</param>
|
|
internal static void WriteUri(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry)
|
|
{
|
|
Dbg.Assert(serializer != null, "caller should have validated the information");
|
|
Dbg.Assert(source != null, "caller should have validated the information");
|
|
Dbg.Assert(source is Uri, "Caller should verify that typeof(source) is Uri");
|
|
Dbg.Assert(entry != null, "caller should have validated the information");
|
|
|
|
WriteEncodedString(serializer, streamName, property, Convert.ToString(source, CultureInfo.InvariantCulture), entry);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serialize string as item or property.
|
|
/// </summary>
|
|
/// <param name="serializer">The serializer to which the object is serialized.</param>
|
|
/// <param name="streamName"></param>
|
|
/// <param name="property">Name of property. pass null for item.</param>
|
|
/// <param name="source">String to write.</param>
|
|
/// <param name="entry">Serialization information about source.</param>
|
|
internal static void WriteEncodedString(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry)
|
|
{
|
|
Dbg.Assert(serializer != null, "caller should have validated the information");
|
|
Dbg.Assert(source != null, "caller should have validated the information");
|
|
Dbg.Assert(source is string, "caller should have validated the information");
|
|
Dbg.Assert(entry != null, "caller should have validated the information");
|
|
|
|
if (property != null)
|
|
{
|
|
serializer.WriteStartElement(entry.PropertyTag);
|
|
serializer.WriteNameAttribute(property);
|
|
}
|
|
else
|
|
{
|
|
serializer.WriteStartElement(entry.ItemTag);
|
|
}
|
|
|
|
if (streamName != null)
|
|
{
|
|
serializer.WriteAttribute(SerializationStrings.StreamNameAttribute, streamName);
|
|
}
|
|
|
|
// Note: We do not use WriteRaw for serializing string. WriteString
|
|
// does necessary escaping which may be needed for certain
|
|
// characters.
|
|
Dbg.Assert(source is string, "Caller should verify that typeof(source) is String");
|
|
string s = (string)source;
|
|
string encoded = EncodeString(s);
|
|
serializer._writer.WriteString(encoded);
|
|
|
|
serializer._writer.WriteEndElement();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes Double as item or property.
|
|
/// </summary>
|
|
/// <param name="serializer">The serializer to which the object is serialized.</param>
|
|
/// <param name="streamName"></param>
|
|
/// <param name="property">Name of property. pass null for item.</param>
|
|
/// <param name="source">Double to write.</param>
|
|
/// <param name="entry">Serialization information about source.</param>
|
|
internal static void WriteDouble(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry)
|
|
{
|
|
Dbg.Assert(serializer != null, "caller should have validated the information");
|
|
Dbg.Assert(source != null, "caller should have validated the information");
|
|
Dbg.Assert(entry != null, "caller should have validated the information");
|
|
|
|
WriteRawString(serializer, streamName, property, XmlConvert.ToString((double)source), entry);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes Char as item or property.
|
|
/// </summary>
|
|
/// <param name="serializer">The serializer to which the object is serialized.</param>
|
|
/// <param name="streamName"></param>
|
|
/// <param name="property">Name of property. pass null for item.</param>
|
|
/// <param name="source">Char to write.</param>
|
|
/// <param name="entry">Serialization information about source.</param>
|
|
internal static void WriteChar(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry)
|
|
{
|
|
Dbg.Assert(serializer != null, "caller should have validated the information");
|
|
Dbg.Assert(source != null, "caller should have validated the information");
|
|
Dbg.Assert(entry != null, "caller should have validated the information");
|
|
|
|
// Char is defined as unsigned short in schema
|
|
WriteRawString(serializer, streamName, property, XmlConvert.ToString((UInt16)(char)source), entry);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes Boolean as item or property.
|
|
/// </summary>
|
|
/// <param name="serializer">The serializer to which the object is serialized.</param>
|
|
/// <param name="streamName"></param>
|
|
/// <param name="property">Name of property. pass null for item.</param>
|
|
/// <param name="source">Boolean to write.</param>
|
|
/// <param name="entry">Serialization information about source.</param>
|
|
internal static void WriteBoolean(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry)
|
|
{
|
|
Dbg.Assert(serializer != null, "caller should have validated the information");
|
|
Dbg.Assert(source != null, "caller should have validated the information");
|
|
Dbg.Assert(entry != null, "caller should have validated the information");
|
|
|
|
WriteRawString(serializer, streamName, property, XmlConvert.ToString((bool)source), entry);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes Single as item or property.
|
|
/// </summary>
|
|
/// <param name="serializer">The serializer to which the object is serialized.</param>
|
|
/// <param name="streamName"></param>
|
|
/// <param name="property">Name of property. pass null for item.</param>
|
|
/// <param name="source">Single to write.</param>
|
|
/// <param name="entry">Serialization information about source.</param>
|
|
internal static void WriteSingle(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry)
|
|
{
|
|
Dbg.Assert(serializer != null, "caller should have validated the information");
|
|
Dbg.Assert(source != null, "caller should have validated the information");
|
|
Dbg.Assert(entry != null, "caller should have validated the information");
|
|
|
|
WriteRawString(serializer, streamName, property, XmlConvert.ToString((Single)source), entry);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes TimeSpan as item or property.
|
|
/// </summary>
|
|
/// <param name="serializer">The serializer to which the object is serialized.</param>
|
|
/// <param name="streamName"></param>
|
|
/// <param name="property">Name of property. pass null for item.</param>
|
|
/// <param name="source">DateTime to write.</param>
|
|
/// <param name="entry">Serialization information about source.</param>
|
|
internal static void WriteTimeSpan(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry)
|
|
{
|
|
Dbg.Assert(serializer != null, "caller should have validated the information");
|
|
Dbg.Assert(source != null, "caller should have validated the information");
|
|
Dbg.Assert(entry != null, "caller should have validated the information");
|
|
|
|
WriteRawString(serializer, streamName, property, XmlConvert.ToString((TimeSpan)source), entry);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes Single as item or property.
|
|
/// </summary>
|
|
/// <param name="serializer">The serializer to which the object is serialized.</param>
|
|
/// <param name="streamName"></param>
|
|
/// <param name="property">Name of property. pass null for item.</param>
|
|
/// <param name="source">Bytearray to write.</param>
|
|
/// <param name="entry">Serialization information about source.</param>
|
|
internal static void WriteByteArray(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry)
|
|
{
|
|
Dbg.Assert(serializer != null, "caller should have validated the information");
|
|
Dbg.Assert(source != null, "caller should have validated the information");
|
|
Dbg.Assert(entry != null, "caller should have validated the information");
|
|
|
|
byte[] bytes = (byte[])source;
|
|
if (property != null)
|
|
{
|
|
serializer.WriteStartElement(entry.PropertyTag);
|
|
serializer.WriteNameAttribute(property);
|
|
}
|
|
else
|
|
{
|
|
serializer.WriteStartElement(entry.ItemTag);
|
|
}
|
|
|
|
if (streamName != null)
|
|
{
|
|
serializer.WriteAttribute(SerializationStrings.StreamNameAttribute, streamName);
|
|
}
|
|
|
|
serializer._writer.WriteBase64(bytes, 0, bytes.Length);
|
|
serializer._writer.WriteEndElement();
|
|
}
|
|
|
|
internal static void WriteXmlDocument(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry)
|
|
{
|
|
Dbg.Assert(serializer != null, "caller should have validated the information");
|
|
Dbg.Assert(source != null, "caller should have validated the information");
|
|
Dbg.Assert(entry != null, "caller should have validated the information");
|
|
|
|
string xml = ((XmlDocument)source).OuterXml;
|
|
WriteEncodedString(serializer, streamName, property, xml, entry);
|
|
}
|
|
|
|
internal static void WriteProgressRecord(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry)
|
|
{
|
|
Dbg.Assert(serializer != null, "caller should have validated the information");
|
|
Dbg.Assert(source != null, "caller should have validated the information");
|
|
Dbg.Assert(entry != null, "caller should have validated the information");
|
|
|
|
ProgressRecord rec = (ProgressRecord)source;
|
|
serializer.WriteStartElement(entry.PropertyTag);
|
|
if (property != null)
|
|
{
|
|
serializer.WriteNameAttribute(property);
|
|
}
|
|
|
|
if (streamName != null)
|
|
{
|
|
serializer.WriteAttribute(SerializationStrings.StreamNameAttribute, streamName);
|
|
}
|
|
|
|
serializer.WriteEncodedElementString(SerializationStrings.ProgressRecordActivity, rec.Activity);
|
|
serializer.WriteEncodedElementString(SerializationStrings.ProgressRecordActivityId, rec.ActivityId.ToString(CultureInfo.InvariantCulture));
|
|
serializer.WriteOneObject(rec.CurrentOperation, null, null, 1);
|
|
serializer.WriteEncodedElementString(SerializationStrings.ProgressRecordParentActivityId, rec.ParentActivityId.ToString(CultureInfo.InvariantCulture));
|
|
serializer.WriteEncodedElementString(SerializationStrings.ProgressRecordPercentComplete, rec.PercentComplete.ToString(CultureInfo.InvariantCulture));
|
|
serializer.WriteEncodedElementString(SerializationStrings.ProgressRecordType, rec.RecordType.ToString());
|
|
serializer.WriteEncodedElementString(SerializationStrings.ProgressRecordSecondsRemaining, rec.SecondsRemaining.ToString(CultureInfo.InvariantCulture));
|
|
serializer.WriteEncodedElementString(SerializationStrings.ProgressRecordStatusDescription, rec.StatusDescription);
|
|
|
|
serializer._writer.WriteEndElement();
|
|
}
|
|
|
|
internal static void WriteSecureString(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry)
|
|
{
|
|
Dbg.Assert(serializer != null, "caller should have validated the information");
|
|
Dbg.Assert(source != null, "caller should have validated the information");
|
|
Dbg.Assert(entry != null, "caller should have validated the information");
|
|
|
|
serializer.HandleSecureString(source, streamName, property);
|
|
}
|
|
|
|
#endregion known type serialization
|
|
|
|
#region misc
|
|
|
|
/// <summary>
|
|
/// Writes start element in Monad namespace.
|
|
/// </summary>
|
|
/// <param name="elementTag">Tag of element.</param>
|
|
private void WriteStartElement(string elementTag)
|
|
{
|
|
Dbg.Assert(!string.IsNullOrEmpty(elementTag), "Caller should validate the parameter");
|
|
if ((_context.options & SerializationOptions.NoNamespace) == SerializationOptions.NoNamespace)
|
|
{
|
|
_writer.WriteStartElement(elementTag);
|
|
}
|
|
else
|
|
{
|
|
_writer.WriteStartElement(elementTag, SerializationStrings.MonadNamespace);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes attribute in monad namespace.
|
|
/// </summary>
|
|
/// <param name="name">Name of attribute.</param>
|
|
/// <param name="value">Value of attribute.</param>
|
|
private void WriteAttribute(string name, string value)
|
|
{
|
|
Dbg.Assert(!string.IsNullOrEmpty(name), "Caller should validate the parameter");
|
|
Dbg.Assert(value != null, "Caller should validate the parameter");
|
|
_writer.WriteAttributeString(name, value);
|
|
}
|
|
|
|
private void WriteNameAttribute(string value)
|
|
{
|
|
Dbg.Assert(!string.IsNullOrEmpty(value), "Caller should validate the parameter");
|
|
WriteAttribute(
|
|
SerializationStrings.NameAttribute,
|
|
EncodeString(value));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Encodes the string to escape characters which would make XmlWriter.WriteString throw an exception.
|
|
/// </summary>
|
|
/// <param name="s">String to encode.</param>
|
|
/// <returns>Encoded string.</returns>
|
|
/// <remarks>
|
|
/// Output from this method can be reverted using XmlConvert.DecodeName method
|
|
/// (or InternalDeserializer.DecodeString).
|
|
/// This method has been introduced to produce shorter output than XmlConvert.EncodeName
|
|
/// (which escapes everything that can't be part of an xml name - whitespace, punctuation).
|
|
///
|
|
/// This method has been split into 2 parts to optimize its performance:
|
|
/// 1) part1 (this method) checks if there are any encodable characters and
|
|
/// if there aren't it simply (and efficiently) returns the original string
|
|
/// 2) part2 (EncodeString(string, int)) picks up when part1 detects the first encodable
|
|
/// character. It avoids looking at the characters already verified by part1
|
|
/// and copies those already verified characters and then starts encoding
|
|
/// the rest of the string.
|
|
/// </remarks>
|
|
internal static string EncodeString(string s)
|
|
{
|
|
Dbg.Assert(s != null, "Caller should validate the parameter");
|
|
|
|
int slen = s.Length;
|
|
for (int i = 0; i < slen; ++i)
|
|
{
|
|
char c = s[i];
|
|
// A control character is in ranges 0x00-0x1F or 0x7F-0x9F
|
|
// The escape character is 0x5F ('_') if followed by an 'x'
|
|
// A surrogate character is in range 0xD800-0xDFFF
|
|
if (c <= 0x1F
|
|
|| (c >= 0x7F && c <= 0x9F)
|
|
|| (c >= 0xD800 && c <= 0xDFFF)
|
|
|| (c == 0x5F && (i + 1 < slen) &&
|
|
((s[i + 1] == 'x') || (s[i + 1] == 'X'))
|
|
))
|
|
{
|
|
return EncodeString(s, i);
|
|
}
|
|
}
|
|
// No encodable characters were found above - simply return the original string
|
|
return s;
|
|
}
|
|
|
|
private static readonly char[] s_hexlookup = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
|
|
|
|
/// <summary>
|
|
/// This is the real workhorse that encodes strings.
|
|
/// See <see cref="EncodeString(string)"/> for more information.
|
|
/// </summary>
|
|
/// <param name="s">String to encode.</param>
|
|
/// <param name="indexOfFirstEncodableCharacter">IndexOfFirstEncodableCharacter.</param>
|
|
/// <returns>Encoded string.</returns>
|
|
private static string EncodeString(string s, int indexOfFirstEncodableCharacter)
|
|
{
|
|
Dbg.Assert(s != null, "Caller should validate the 's' parameter");
|
|
Dbg.Assert(indexOfFirstEncodableCharacter >= 0, "Caller should verify validity of indexOfFirstEncodableCharacter");
|
|
Dbg.Assert(indexOfFirstEncodableCharacter < s.Length, "Caller should verify validity of indexOfFirstEncodableCharacter");
|
|
|
|
int slen = s.Length;
|
|
char[] result = new char[indexOfFirstEncodableCharacter + (slen - indexOfFirstEncodableCharacter) * 7];
|
|
|
|
s.CopyTo(0, result, 0, indexOfFirstEncodableCharacter);
|
|
int rlen = indexOfFirstEncodableCharacter;
|
|
|
|
for (int i = indexOfFirstEncodableCharacter; i < slen; ++i)
|
|
{
|
|
char c = s[i];
|
|
// A control character is in ranges 0x00-0x1F or 0x7F-0x9F
|
|
// The escape character is 0x5F ('_') if followed by an 'x'
|
|
// A surrogate character is in range 0xD800-0xDFFF
|
|
if (c > 0x1F
|
|
&& (c < 0x7F || c > 0x9F)
|
|
&& (c < 0xD800 || c > 0xDFFF)
|
|
&& (c != 0x5F || ((i + 1 >= slen) ||
|
|
((s[i + 1] != 'x') && (s[i + 1] != 'X'))
|
|
)))
|
|
{
|
|
result[rlen++] = c;
|
|
}
|
|
else if (c == 0x5F)
|
|
{
|
|
// Special case the escape character, encode _
|
|
result[rlen + 0] = '_';
|
|
result[rlen + 1] = 'x';
|
|
result[rlen + 2] = '0';
|
|
result[rlen + 3] = '0';
|
|
result[rlen + 4] = '5';
|
|
result[rlen + 5] = 'F';
|
|
result[rlen + 6] = '_';
|
|
rlen += 7;
|
|
}
|
|
else
|
|
{
|
|
// It is a control character or a unicode surrogate
|
|
result[rlen + 0] = '_';
|
|
result[rlen + 1] = 'x';
|
|
|
|
result[rlen + 2 + 3] = s_hexlookup[c & 0x0F];
|
|
c >>= 4;
|
|
result[rlen + 2 + 2] = s_hexlookup[c & 0x0F];
|
|
c >>= 4;
|
|
result[rlen + 2 + 1] = s_hexlookup[c & 0x0F];
|
|
c >>= 4;
|
|
result[rlen + 2 + 0] = s_hexlookup[c & 0x0F];
|
|
|
|
result[rlen + 6] = '_';
|
|
|
|
rlen += 7;
|
|
}
|
|
}
|
|
|
|
return new string(result, 0, rlen);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes element string in monad namespace.
|
|
/// </summary>
|
|
/// <param name="name"></param>
|
|
/// <param name="value"></param>
|
|
private void WriteEncodedElementString(string name, string value)
|
|
{
|
|
Dbg.Assert(!string.IsNullOrEmpty(name), "Caller should validate the parameter");
|
|
Dbg.Assert(value != null, "Caller should validate the parameter");
|
|
this.CheckIfStopping();
|
|
|
|
value = EncodeString(value);
|
|
|
|
if ((_context.options & SerializationOptions.NoNamespace) == SerializationOptions.NoNamespace)
|
|
{
|
|
_writer.WriteElementString(name, value);
|
|
}
|
|
else
|
|
{
|
|
_writer.WriteElementString(name, SerializationStrings.MonadNamespace, value);
|
|
}
|
|
}
|
|
|
|
#endregion misc
|
|
}
|
|
|
|
/// <summary>
|
|
/// This internal class provides methods for de-serializing mshObject.
|
|
/// </summary>
|
|
internal class InternalDeserializer
|
|
{
|
|
#region constructor
|
|
|
|
/// <summary>
|
|
/// XmlReader from which object is deserialized.
|
|
/// </summary>
|
|
private readonly XmlReader _reader;
|
|
|
|
/// <summary>
|
|
/// Deserialization context.
|
|
/// </summary>
|
|
private readonly DeserializationContext _context;
|
|
|
|
/// Used by Remoting infrastructure. This TypeTable instance
|
|
/// will be used by Serializer if ExecutionContext is not
|
|
/// available (to get the ExecutionContext's TypeTable)
|
|
private TypeTable _typeTable;
|
|
|
|
/// <summary>
|
|
/// If true, unknowntags are allowed inside PSObject.
|
|
/// </summary>
|
|
private bool UnknownTagsAllowed
|
|
{
|
|
get
|
|
{
|
|
Dbg.Assert(_version.Major <= 1, "Deserializer assumes clixml version is <= 1.1");
|
|
// If minor version is greater than 1, it means that there can be
|
|
// some unknown tags in xml. Deserialization should ignore such element.
|
|
return (_version.Minor > 1);
|
|
}
|
|
}
|
|
|
|
[SuppressMessage(
|
|
"Performance",
|
|
"CA1822:Mark members as static",
|
|
Justification = "Accesses instance members in preprocessor branch.")]
|
|
private bool DuplicateRefIdsAllowed
|
|
{
|
|
get
|
|
{
|
|
#if DEBUG
|
|
Dbg.Assert(_version.Major <= 1, "Deserializer assumes clixml version is <= 1.1");
|
|
Version boundaryVersion = new Version(1, 1, 0, 1);
|
|
return (_version < boundaryVersion);
|
|
#else
|
|
return true; // handle v1 stuff gracefully
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Depth below top level - used to prevent stack overflow during deserialization.
|
|
/// </summary>
|
|
private int _depthBelowTopLevel;
|
|
|
|
/// <summary>
|
|
/// Version declared by the clixml being read.
|
|
/// </summary>
|
|
private Version _version;
|
|
|
|
private const int MaxDepthBelowTopLevel = 50;
|
|
|
|
private readonly ReferenceIdHandlerForDeserializer<object> _objectRefIdHandler;
|
|
private readonly ReferenceIdHandlerForDeserializer<ConsolidatedString> _typeRefIdHandler;
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
/// <param name="reader"></param>
|
|
/// <param name="context"></param>
|
|
internal InternalDeserializer(XmlReader reader, DeserializationContext context)
|
|
{
|
|
Dbg.Assert(reader != null, "caller should validate the parameter");
|
|
|
|
_reader = reader;
|
|
_context = context;
|
|
|
|
_objectRefIdHandler = new ReferenceIdHandlerForDeserializer<object>();
|
|
_typeRefIdHandler = new ReferenceIdHandlerForDeserializer<ConsolidatedString>();
|
|
}
|
|
|
|
#endregion constructor
|
|
|
|
#region Known CIMTypes
|
|
|
|
private static readonly Lazy<HashSet<Type>> s_knownCimArrayTypes = new Lazy<HashSet<Type>>(
|
|
() =>
|
|
new HashSet<Type>
|
|
{
|
|
typeof(bool),
|
|
typeof(byte),
|
|
typeof(char),
|
|
typeof(DateTime),
|
|
typeof(decimal),
|
|
typeof(double),
|
|
typeof(Int16),
|
|
typeof(Int32),
|
|
typeof(Int64),
|
|
typeof(sbyte),
|
|
typeof(Single),
|
|
typeof(string),
|
|
typeof(TimeSpan),
|
|
typeof(UInt16),
|
|
typeof(UInt32),
|
|
typeof(UInt64),
|
|
typeof(object),
|
|
typeof(CimInstance)
|
|
}
|
|
);
|
|
|
|
#endregion
|
|
|
|
#region deserialization
|
|
/// <summary>
|
|
/// Used by Remoting infrastructure. This TypeTable instance
|
|
/// will be used by Deserializer if ExecutionContext is not
|
|
/// available (to get the ExecutionContext's TypeTable)
|
|
/// </summary>
|
|
internal TypeTable TypeTable
|
|
{
|
|
get { return _typeTable; }
|
|
|
|
set { _typeTable = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validates the version for correctness. Also validates that deserializer
|
|
/// can deserialize this version.
|
|
/// </summary>
|
|
/// <param name="version">
|
|
/// version in string format
|
|
/// </param>
|
|
internal void ValidateVersion(string version)
|
|
{
|
|
Dbg.Assert(version != null, "Caller should validate the parameter");
|
|
|
|
_version = null;
|
|
Exception exceptionToRethrow = null;
|
|
try
|
|
{
|
|
_version = new Version(version);
|
|
}
|
|
catch (ArgumentException e)
|
|
{
|
|
exceptionToRethrow = e;
|
|
}
|
|
catch (FormatException e)
|
|
{
|
|
exceptionToRethrow = e;
|
|
}
|
|
|
|
if (exceptionToRethrow != null)
|
|
{
|
|
throw NewXmlException(Serialization.InvalidVersion, exceptionToRethrow);
|
|
}
|
|
|
|
// Versioning Note:Future version of serialization can add new known types.
|
|
// This version will ignore those known types, if they are base object.
|
|
// It is expected that future version will still put information in base
|
|
// and adapter properties which this serializer can read and use.
|
|
// For example, assume the version 2 serialization engine supports a new known
|
|
// type IPAddress. The version 1 deserializer doesn't know IPAddress as known
|
|
// type and it must retrieve it as an PSObject. The version 2 serializer
|
|
// can serialize this as follows:
|
|
// <PSObject Version=1.2 Was=Deserialized.IPAddress >
|
|
// <TypeNames>...</TypeNames>
|
|
// <BaseObject>
|
|
// <IPAddress>120.23.35.53</IPAddress>
|
|
// </BaseObject>
|
|
// <Properties>
|
|
// <string name=Address>120.23.34.53</string>
|
|
// <string name=class>A</string>
|
|
// </Properties>
|
|
// </PSObject>
|
|
// In above example, V1 serializer will ignore <IPAddress> element and read
|
|
// properties from <Properties>
|
|
// V2 serializer can read <IPAddress> tag and ignore properties.
|
|
// Read serialization note doc for information.
|
|
|
|
// Now validate the major version number is 1
|
|
if (_version.Major != 1)
|
|
{
|
|
throw NewXmlException(Serialization.UnexpectedVersion, null, _version.Major);
|
|
}
|
|
}
|
|
|
|
private object ReadOneDeserializedObject(out string streamName, out bool isKnownPrimitiveType)
|
|
{
|
|
if (_reader.NodeType != XmlNodeType.Element)
|
|
{
|
|
throw NewXmlException(Serialization.InvalidNodeType, null,
|
|
_reader.NodeType.ToString(), nameof(XmlNodeType.Element));
|
|
}
|
|
|
|
s_trace.WriteLine("Processing start node {0}", _reader.LocalName);
|
|
|
|
streamName = _reader.GetAttribute(SerializationStrings.StreamNameAttribute);
|
|
isKnownPrimitiveType = false;
|
|
|
|
// handle nil node
|
|
if (IsNextElement(SerializationStrings.NilTag))
|
|
{
|
|
Skip();
|
|
return null;
|
|
}
|
|
|
|
// Handle reference to previous deserialized object.
|
|
if (IsNextElement(SerializationStrings.ReferenceTag))
|
|
{
|
|
string refId = _reader.GetAttribute(SerializationStrings.ReferenceIdAttribute);
|
|
|
|
if (refId == null)
|
|
{
|
|
throw NewXmlException(Serialization.AttributeExpected, null, SerializationStrings.ReferenceIdAttribute);
|
|
}
|
|
|
|
object duplicate = _objectRefIdHandler.GetReferencedObject(refId);
|
|
if (duplicate == null)
|
|
{
|
|
throw NewXmlException(Serialization.InvalidReferenceId, null, refId);
|
|
}
|
|
|
|
Skip();
|
|
return duplicate;
|
|
}
|
|
|
|
// Handle primitive known types
|
|
TypeSerializationInfo pktInfo = KnownTypes.GetTypeSerializationInfoFromItemTag(_reader.LocalName);
|
|
if (pktInfo != null)
|
|
{
|
|
s_trace.WriteLine("Primitive Knowntype Element {0}", pktInfo.ItemTag);
|
|
isKnownPrimitiveType = true;
|
|
return ReadPrimaryKnownType(pktInfo);
|
|
}
|
|
|
|
// Handle PSObject
|
|
if (IsNextElement(SerializationStrings.PSObjectTag))
|
|
{
|
|
s_trace.WriteLine("PSObject Element");
|
|
return ReadPSObject();
|
|
}
|
|
|
|
// If we are here, we have an unknown node. Unknown nodes may
|
|
// be allowed inside PSObject. We do not allow them at top level.
|
|
|
|
s_trace.TraceError("Invalid element {0} tag found", _reader.LocalName);
|
|
throw NewXmlException(Serialization.InvalidElementTag, null, _reader.LocalName);
|
|
}
|
|
|
|
private bool _isStopping = false;
|
|
|
|
/// <summary>
|
|
/// Called from a separate thread will stop the serialization process.
|
|
/// </summary>
|
|
internal void Stop()
|
|
{
|
|
_isStopping = true;
|
|
}
|
|
|
|
private void CheckIfStopping()
|
|
{
|
|
if (_isStopping)
|
|
{
|
|
throw PSTraceSource.NewInvalidOperationException(Serialization.Stopping);
|
|
}
|
|
}
|
|
|
|
internal const string CimInstanceMetadataProperty = "__InstanceMetadata";
|
|
internal const string CimModifiedProperties = "Modified";
|
|
internal const string CimClassMetadataProperty = "__ClassMetadata";
|
|
internal const string CimClassNameProperty = "ClassName";
|
|
internal const string CimNamespaceProperty = "Namespace";
|
|
internal const string CimServerNameProperty = "ServerName";
|
|
internal const string CimHashCodeProperty = "Hash";
|
|
internal const string CimMiXmlProperty = "MiXml";
|
|
|
|
private static bool RehydrateCimInstanceProperty(
|
|
CimInstance cimInstance,
|
|
PSPropertyInfo deserializedProperty,
|
|
HashSet<string> namesOfModifiedProperties)
|
|
{
|
|
Dbg.Assert(cimInstance != null, "Caller should make sure cimInstance != null");
|
|
Dbg.Assert(deserializedProperty != null, "Caller should make sure deserializedProperty != null");
|
|
|
|
if (deserializedProperty.Name.Equals(RemotingConstants.ComputerNameNoteProperty, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
string psComputerNameValue = deserializedProperty.Value as string;
|
|
if (psComputerNameValue != null)
|
|
{
|
|
cimInstance.SetCimSessionComputerName(psComputerNameValue);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
CimProperty cimProperty = cimInstance.CimInstanceProperties[deserializedProperty.Name];
|
|
if (cimProperty == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// TODO/FIXME - think if it is possible to do the array handling in a more efficient way
|
|
object propertyValue = deserializedProperty.Value;
|
|
if (propertyValue != null)
|
|
{
|
|
PSObject psoPropertyValue = PSObject.AsPSObject(propertyValue);
|
|
if (psoPropertyValue.BaseObject is ArrayList)
|
|
{
|
|
if ((psoPropertyValue.InternalTypeNames == null) || (psoPropertyValue.InternalTypeNames.Count == 0))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
string originalArrayTypeName = Deserializer.MaskDeserializationPrefix(psoPropertyValue.InternalTypeNames[0]);
|
|
if (originalArrayTypeName == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Type originalArrayType;
|
|
if (!LanguagePrimitives.TryConvertTo(originalArrayTypeName, CultureInfo.InvariantCulture, out originalArrayType))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!originalArrayType.IsArray || !s_knownCimArrayTypes.Value.Contains(originalArrayType.GetElementType()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
object newPropertyValue;
|
|
if (!LanguagePrimitives.TryConvertTo(propertyValue, originalArrayType, CultureInfo.InvariantCulture, out newPropertyValue))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
psoPropertyValue = PSObject.AsPSObject(newPropertyValue);
|
|
}
|
|
|
|
propertyValue = psoPropertyValue.BaseObject;
|
|
}
|
|
|
|
try
|
|
{
|
|
cimProperty.Value = propertyValue;
|
|
|
|
if (!namesOfModifiedProperties.Contains(deserializedProperty.Name))
|
|
{
|
|
cimProperty.IsValueModified = false;
|
|
}
|
|
#if DEBUG
|
|
else
|
|
{
|
|
Dbg.Assert(cimProperty.IsValueModified, "Deserialized CIM properties should by default be marked as 'modified' ");
|
|
}
|
|
#endif
|
|
}
|
|
catch (FormatException)
|
|
{
|
|
return false;
|
|
}
|
|
catch (InvalidCastException)
|
|
{
|
|
return false;
|
|
}
|
|
catch (ArgumentException)
|
|
{
|
|
return false;
|
|
}
|
|
catch (CimException)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private static readonly Lazy<CimDeserializer> s_cimDeserializer = new Lazy<CimDeserializer>(CimDeserializer.Create);
|
|
|
|
private CimClass RehydrateCimClass(PSPropertyInfo classMetadataProperty)
|
|
{
|
|
if ((classMetadataProperty == null) || (classMetadataProperty.Value == null))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
IEnumerable deserializedClasses = LanguagePrimitives.GetEnumerable(classMetadataProperty.Value);
|
|
if (deserializedClasses == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
Stack<KeyValuePair<CimClassSerializationId, CimClass>> cimClassesToAddToCache = new Stack<KeyValuePair<CimClassSerializationId, CimClass>>();
|
|
|
|
//
|
|
// REHYDRATE CLASS METADATA
|
|
//
|
|
CimClass parentClass = null;
|
|
CimClass currentClass = null;
|
|
foreach (var deserializedClass in deserializedClasses)
|
|
{
|
|
parentClass = currentClass;
|
|
|
|
if (deserializedClass == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
PSObject psoDeserializedClass = PSObject.AsPSObject(deserializedClass);
|
|
|
|
if (!(psoDeserializedClass.InstanceMembers[InternalDeserializer.CimNamespaceProperty] is PSPropertyInfo namespaceProperty))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
string cimNamespace = namespaceProperty.Value as string;
|
|
|
|
if (!(psoDeserializedClass.InstanceMembers[InternalDeserializer.CimClassNameProperty] is PSPropertyInfo classNameProperty))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
string cimClassName = classNameProperty.Value as string;
|
|
|
|
if (!(psoDeserializedClass.InstanceMembers[InternalDeserializer.CimServerNameProperty] is PSPropertyInfo computerNameProperty))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
string computerName = computerNameProperty.Value as string;
|
|
|
|
if (!(psoDeserializedClass.InstanceMembers[InternalDeserializer.CimHashCodeProperty] is PSPropertyInfo hashCodeProperty))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var hashCodeObject = hashCodeProperty.Value;
|
|
if (hashCodeObject == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (hashCodeObject is PSObject)
|
|
{
|
|
hashCodeObject = ((PSObject)hashCodeObject).BaseObject;
|
|
}
|
|
|
|
if (hashCodeObject is not int)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
int hashCode = (int)hashCodeObject;
|
|
|
|
CimClassSerializationId cimClassSerializationId = new CimClassSerializationId(cimClassName, cimNamespace, computerName, hashCode);
|
|
currentClass = _context.cimClassSerializationIdCache.GetCimClassFromCache(cimClassSerializationId);
|
|
if (currentClass != null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
PSPropertyInfo miXmlProperty = psoDeserializedClass.InstanceMembers[InternalDeserializer.CimMiXmlProperty] as PSPropertyInfo;
|
|
if ((miXmlProperty == null) || (miXmlProperty.Value == null))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
string miXmlString = miXmlProperty.Value.ToString();
|
|
byte[] miXmlBytes = Encoding.Unicode.GetBytes(miXmlString);
|
|
uint offset = 0;
|
|
try
|
|
{
|
|
currentClass = s_cimDeserializer.Value.DeserializeClass(
|
|
miXmlBytes,
|
|
ref offset,
|
|
parentClass,
|
|
computerName: computerName,
|
|
namespaceName: cimNamespace);
|
|
cimClassesToAddToCache.Push(new KeyValuePair<CimClassSerializationId, CimClass>(cimClassSerializationId, currentClass));
|
|
}
|
|
catch (CimException)
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
//
|
|
// UPDATE CLASSDECL DACHE
|
|
//
|
|
foreach (var cacheEntry in cimClassesToAddToCache)
|
|
{
|
|
_context.cimClassSerializationIdCache.AddCimClassToCache(cacheEntry.Key, cacheEntry.Value);
|
|
}
|
|
|
|
return currentClass;
|
|
}
|
|
|
|
// NOTE: Win7 change for refid-s that span multiple xml documents: ADMIN: changelist #226414
|
|
|
|
private PSObject RehydrateCimInstance(PSObject deserializedObject)
|
|
{
|
|
if (deserializedObject.BaseObject is not PSCustomObject)
|
|
{
|
|
return deserializedObject;
|
|
}
|
|
|
|
PSPropertyInfo classMetadataProperty = deserializedObject.InstanceMembers[CimClassMetadataProperty] as PSPropertyInfo;
|
|
CimClass cimClass = RehydrateCimClass(classMetadataProperty);
|
|
if (cimClass == null)
|
|
{
|
|
return deserializedObject;
|
|
}
|
|
|
|
CimInstance cimInstance;
|
|
try
|
|
{
|
|
cimInstance = new CimInstance(cimClass);
|
|
}
|
|
catch (CimException)
|
|
{
|
|
return deserializedObject;
|
|
}
|
|
|
|
PSObject psoCimInstance = PSObject.AsPSObject(cimInstance);
|
|
|
|
// process __InstanceMetadata
|
|
HashSet<string> namesOfModifiedProperties = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
PSPropertyInfo instanceMetadataProperty = deserializedObject.InstanceMembers[CimInstanceMetadataProperty] as PSPropertyInfo;
|
|
if ((instanceMetadataProperty != null) && (instanceMetadataProperty.Value != null))
|
|
{
|
|
PSObject instanceMetadata = PSObject.AsPSObject(instanceMetadataProperty.Value);
|
|
|
|
PSPropertyInfo modifiedPropertiesProperty = instanceMetadata.InstanceMembers[CimModifiedProperties] as PSPropertyInfo;
|
|
if ((modifiedPropertiesProperty != null) && (modifiedPropertiesProperty.Value != null))
|
|
{
|
|
string modifiedPropertiesString = modifiedPropertiesProperty.Value.ToString();
|
|
foreach (string nameOfModifiedProperty in modifiedPropertiesString.Split(Utils.Separators.Space))
|
|
{
|
|
namesOfModifiedProperties.Add(nameOfModifiedProperty);
|
|
}
|
|
}
|
|
}
|
|
|
|
// process properties that were originally "adapted" properties
|
|
if (deserializedObject.AdaptedMembers != null)
|
|
{
|
|
foreach (PSMemberInfo deserializedMemberInfo in deserializedObject.AdaptedMembers)
|
|
{
|
|
if (!(deserializedMemberInfo is PSPropertyInfo deserializedProperty))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
bool propertyHandledSuccessfully = RehydrateCimInstanceProperty(
|
|
cimInstance,
|
|
deserializedProperty,
|
|
namesOfModifiedProperties);
|
|
|
|
if (!propertyHandledSuccessfully)
|
|
{
|
|
return deserializedObject;
|
|
}
|
|
}
|
|
}
|
|
|
|
// process properties that were originally "extended" properties
|
|
foreach (PSMemberInfo deserializedMemberInfo in deserializedObject.InstanceMembers)
|
|
{
|
|
if (!(deserializedMemberInfo is PSPropertyInfo deserializedProperty))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// skip adapted properties
|
|
if ((deserializedObject.AdaptedMembers != null) && (deserializedObject.AdaptedMembers[deserializedProperty.Name] != null))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// skip metadata introduced by CliXml/CimInstance serialization
|
|
if (deserializedProperty.Name.Equals(CimClassMetadataProperty, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// skip properties re-added by the client (i.e. through types.ps1xml)
|
|
if (psoCimInstance.Properties[deserializedProperty.Name] != null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
PSNoteProperty noteProperty = new PSNoteProperty(deserializedProperty.Name, deserializedProperty.Value);
|
|
psoCimInstance.Properties.Add(noteProperty);
|
|
}
|
|
|
|
return psoCimInstance;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reads one object. At this point reader should be positioned
|
|
/// at the start tag of object.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// Deserialized Object.
|
|
/// </returns>
|
|
internal object ReadOneObject(out string streamName)
|
|
{
|
|
this.CheckIfStopping();
|
|
|
|
try
|
|
{
|
|
_depthBelowTopLevel++;
|
|
|
|
Dbg.Assert(_depthBelowTopLevel <= MaxDepthBelowTopLevel, "depthBelowTopLevel should be <= MaxDepthBelowTopLevel");
|
|
if (_depthBelowTopLevel == MaxDepthBelowTopLevel)
|
|
{
|
|
throw NewXmlException(Serialization.DeserializationTooDeep, null);
|
|
}
|
|
|
|
bool isKnownPrimitiveType;
|
|
object result = ReadOneDeserializedObject(out streamName, out isKnownPrimitiveType);
|
|
if (result == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (!isKnownPrimitiveType)
|
|
{
|
|
PSObject mshSource = PSObject.AsPSObject(result);
|
|
if (Deserializer.IsDeserializedInstanceOfType(mshSource, typeof(CimInstance)))
|
|
{
|
|
return RehydrateCimInstance(mshSource);
|
|
}
|
|
|
|
// Convert deserialized object to a user-defined type (specified in a types.ps1xml file)
|
|
Type targetType = mshSource.GetTargetTypeForDeserialization(_typeTable);
|
|
if (targetType != null)
|
|
{
|
|
Exception rehydrationException = null;
|
|
try
|
|
{
|
|
object rehydratedResult = LanguagePrimitives.ConvertTo(
|
|
result, targetType, true /* recurse */, CultureInfo.InvariantCulture, _typeTable);
|
|
|
|
PSEtwLog.LogAnalyticVerbose(PSEventId.Serializer_RehydrationSuccess,
|
|
PSOpcode.Rehydration, PSTask.Serialization, PSKeyword.Serializer,
|
|
mshSource.InternalTypeNames.Key, targetType.FullName,
|
|
rehydratedResult.GetType().FullName);
|
|
|
|
return rehydratedResult;
|
|
}
|
|
catch (InvalidCastException e)
|
|
{
|
|
rehydrationException = e;
|
|
}
|
|
catch (ArgumentException e)
|
|
{
|
|
rehydrationException = e;
|
|
}
|
|
|
|
Dbg.Assert(rehydrationException != null,
|
|
"The only way to get here is with rehydrationException != null");
|
|
|
|
PSEtwLog.LogAnalyticError(PSEventId.Serializer_RehydrationFailure,
|
|
PSOpcode.Rehydration, PSTask.Serialization, PSKeyword.Serializer,
|
|
mshSource.InternalTypeNames.Key,
|
|
targetType.FullName,
|
|
rehydrationException.ToString(),
|
|
rehydrationException.InnerException == null
|
|
? string.Empty
|
|
: rehydrationException.InnerException.ToString());
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
finally
|
|
{
|
|
_depthBelowTopLevel--;
|
|
Dbg.Assert(_depthBelowTopLevel >= 0, "depthBelowTopLevel should be >= 0");
|
|
}
|
|
}
|
|
|
|
private object ReadOneObject()
|
|
{
|
|
string ignore;
|
|
return ReadOneObject(out ignore);
|
|
}
|
|
|
|
// Reads one PSObject
|
|
private PSObject ReadPSObject()
|
|
{
|
|
PSObject dso = ReadAttributeAndCreatePSObject();
|
|
|
|
// Read start element tag
|
|
if (!ReadStartElementAndHandleEmpty(SerializationStrings.PSObjectTag))
|
|
{
|
|
// Empty element.
|
|
return dso;
|
|
}
|
|
|
|
bool overrideTypeInfo = true;
|
|
// Process all the child nodes
|
|
while (_reader.NodeType == XmlNodeType.Element)
|
|
{
|
|
if (IsNextElement(SerializationStrings.TypeNamesTag) ||
|
|
IsNextElement(SerializationStrings.TypeNamesReferenceTag))
|
|
{
|
|
ReadTypeNames(dso);
|
|
overrideTypeInfo = false;
|
|
}
|
|
else if (IsNextElement(SerializationStrings.AdapterProperties))
|
|
{
|
|
ReadProperties(dso);
|
|
}
|
|
else if (IsNextElement(SerializationStrings.MemberSet))
|
|
{
|
|
ReadMemberSet(dso.InstanceMembers);
|
|
}
|
|
else if (IsNextElement(SerializationStrings.ToStringElementTag))
|
|
{
|
|
dso.ToStringFromDeserialization = ReadDecodedElementString(SerializationStrings.ToStringElementTag);
|
|
dso.InstanceMembers.Add(PSObject.DotNetInstanceAdapter.GetDotNetMethod<PSMemberInfo>(dso, "ToString"));
|
|
PSGetMemberBinder.SetHasInstanceMember("ToString");
|
|
// Fix for Win8:75437
|
|
// The TokenText property is used in type conversion and it is not being populated during deserialization
|
|
// As a result, parameter binding fails in the following case on a remote session
|
|
// register-psssessionconfiguration -Name foo -psversion 3.0
|
|
// The value "3.0" is treated as a double and since the TokenText property holds null, the type converter tries to convert
|
|
// from System.Double to System.Version using Parse method of System.Version and fails
|
|
dso.TokenText = dso.ToStringFromDeserialization;
|
|
}
|
|
else
|
|
{
|
|
// Handle BaseObject
|
|
object baseObject = null;
|
|
ContainerType ct = ContainerType.None;
|
|
|
|
// Check if tag is PrimaryKnownType.
|
|
TypeSerializationInfo pktInfo = KnownTypes.GetTypeSerializationInfoFromItemTag(_reader.LocalName);
|
|
if (pktInfo != null)
|
|
{
|
|
s_trace.WriteLine("Primitive Knowntype Element {0}", pktInfo.ItemTag);
|
|
baseObject = ReadPrimaryKnownType(pktInfo);
|
|
}
|
|
else if (IsKnownContainerTag(out ct))
|
|
{
|
|
s_trace.WriteLine("Found container node {0}", ct);
|
|
baseObject = ReadKnownContainer(ct);
|
|
}
|
|
else if (IsNextElement(SerializationStrings.PSObjectTag))
|
|
{
|
|
s_trace.WriteLine("Found PSObject node");
|
|
baseObject = ReadOneObject();
|
|
}
|
|
else
|
|
{
|
|
// We have an unknown tag
|
|
s_trace.WriteLine("Unknown tag {0} encountered", _reader.LocalName);
|
|
if (UnknownTagsAllowed)
|
|
{
|
|
Skip();
|
|
}
|
|
else
|
|
{
|
|
throw NewXmlException(Serialization.InvalidElementTag, null, _reader.LocalName);
|
|
}
|
|
}
|
|
|
|
if (baseObject != null)
|
|
{
|
|
dso.SetCoreOnDeserialization(baseObject, overrideTypeInfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
ReadEndElement();
|
|
|
|
PSObject immediateBasePso = dso.ImmediateBaseObject as PSObject;
|
|
if (immediateBasePso != null)
|
|
{
|
|
PSObject.CopyDeserializerFields(source: immediateBasePso, target: dso);
|
|
}
|
|
|
|
return dso;
|
|
}
|
|
|
|
/// <summary>
|
|
/// This function reads the refId attribute and creates a
|
|
/// mshObject for that attribute.
|
|
/// </summary>
|
|
/// <returns>MshObject which is created for refId.</returns>
|
|
private PSObject ReadAttributeAndCreatePSObject()
|
|
{
|
|
string refId = _reader.GetAttribute(SerializationStrings.ReferenceIdAttribute);
|
|
PSObject sh = new PSObject();
|
|
|
|
// RefId is not mandatory attribute
|
|
if (refId != null)
|
|
{
|
|
s_trace.WriteLine("Read PSObject with refId: {0}", refId);
|
|
_objectRefIdHandler.SetRefId(sh, refId, this.DuplicateRefIdsAllowed);
|
|
}
|
|
|
|
return sh;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Read type names.
|
|
/// </summary>
|
|
/// <param name="dso">
|
|
/// PSObject to which TypeNames are added
|
|
/// </param>
|
|
private void ReadTypeNames(PSObject dso)
|
|
{
|
|
Dbg.Assert(dso != null, "caller should validate the parameter");
|
|
Dbg.Assert(_reader.NodeType == XmlNodeType.Element, "NodeType should be Element");
|
|
|
|
if (IsNextElement(SerializationStrings.TypeNamesTag))
|
|
{
|
|
Collection<string> typeNames = new Collection<string>();
|
|
|
|
// Read refId attribute if available
|
|
string refId = _reader.GetAttribute(SerializationStrings.ReferenceIdAttribute);
|
|
|
|
s_trace.WriteLine("Processing TypeNamesTag with refId {0}", refId);
|
|
|
|
if (ReadStartElementAndHandleEmpty(SerializationStrings.TypeNamesTag))
|
|
{
|
|
while (_reader.NodeType == XmlNodeType.Element)
|
|
{
|
|
if (IsNextElement(SerializationStrings.TypeNamesItemTag))
|
|
{
|
|
string item = ReadDecodedElementString(SerializationStrings.TypeNamesItemTag);
|
|
if (!string.IsNullOrEmpty(item))
|
|
{
|
|
Deserializer.AddDeserializationPrefix(ref item);
|
|
typeNames.Add(item);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw NewXmlException(Serialization.InvalidElementTag, null, _reader.LocalName);
|
|
}
|
|
}
|
|
|
|
ReadEndElement();
|
|
}
|
|
|
|
dso.InternalTypeNames = new ConsolidatedString(typeNames);
|
|
|
|
if (refId != null)
|
|
{
|
|
_typeRefIdHandler.SetRefId(dso.InternalTypeNames, refId, this.DuplicateRefIdsAllowed);
|
|
}
|
|
}
|
|
else if (IsNextElement(SerializationStrings.TypeNamesReferenceTag))
|
|
{
|
|
string refId = _reader.GetAttribute(SerializationStrings.ReferenceIdAttribute);
|
|
s_trace.WriteLine("Processing TypeNamesReferenceTag with refId {0}", refId);
|
|
if (refId == null)
|
|
{
|
|
throw NewXmlException(Serialization.AttributeExpected, null, SerializationStrings.ReferenceIdAttribute);
|
|
}
|
|
|
|
ConsolidatedString typeNames = _typeRefIdHandler.GetReferencedObject(refId);
|
|
if (typeNames == null)
|
|
{
|
|
throw NewXmlException(Serialization.InvalidTypeHierarchyReferenceId, null, refId);
|
|
}
|
|
|
|
// At this point we know that we will clone the ConsolidatedString object, so we might end up
|
|
// allocating much more memory than the length of the xml string
|
|
// We have to account for that to limit that to remoting quota and protect against OOM.
|
|
_context.LogExtraMemoryUsage(
|
|
typeNames.Key.Length * sizeof(char) // Key is shared among the cloned and original object
|
|
// but the list of strings isn't. The expression to the left
|
|
// is roughly the size of memory the list of strings occupies
|
|
- 29 // size of <Obj><TNRef RefId="0"/></Obj> in UTF8 encoding
|
|
);
|
|
dso.InternalTypeNames = new ConsolidatedString(typeNames);
|
|
|
|
// Skip the node
|
|
Skip();
|
|
}
|
|
else
|
|
{
|
|
Dbg.Assert(false, "caller should validate that we do no reach here");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Read properties.
|
|
/// </summary>
|
|
private void ReadProperties(PSObject dso)
|
|
{
|
|
Dbg.Assert(dso != null, "caller should validate the parameter");
|
|
Dbg.Assert(_reader.NodeType == XmlNodeType.Element, "NodeType should be Element");
|
|
|
|
// Since we are adding baseobject properties as propertybag,
|
|
// mark the object as deserialized.
|
|
dso.IsDeserialized = true;
|
|
dso.AdaptedMembers = new PSMemberInfoInternalCollection<PSPropertyInfo>();
|
|
|
|
// Add the GetType method to the instance members, so that it works on deserialized psobjects
|
|
dso.InstanceMembers.Add(PSObject.DotNetInstanceAdapter.GetDotNetMethod<PSMemberInfo>(dso, "GetType"));
|
|
PSGetMemberBinder.SetHasInstanceMember("GetType");
|
|
|
|
// Set Clr members to a collection which is empty
|
|
dso.ClrMembers = new PSMemberInfoInternalCollection<PSPropertyInfo>();
|
|
|
|
if (ReadStartElementAndHandleEmpty(SerializationStrings.AdapterProperties))
|
|
{
|
|
// Read one or more property elements
|
|
while (_reader.NodeType == XmlNodeType.Element)
|
|
{
|
|
string property = ReadNameAttribute();
|
|
object value = ReadOneObject();
|
|
PSProperty prop = new PSProperty(property, value);
|
|
dso.AdaptedMembers.Add(prop);
|
|
}
|
|
|
|
ReadEndElement();
|
|
}
|
|
}
|
|
|
|
#region memberset
|
|
|
|
/// <summary>
|
|
/// Read memberset.
|
|
/// </summary>
|
|
/// <param name="collection">
|
|
/// collection to which members are added
|
|
/// </param>
|
|
private void ReadMemberSet(PSMemberInfoCollection<PSMemberInfo> collection)
|
|
{
|
|
Dbg.Assert(collection != null, "caller should validate the value");
|
|
|
|
if (ReadStartElementAndHandleEmpty(SerializationStrings.MemberSet))
|
|
{
|
|
while (_reader.NodeType == XmlNodeType.Element)
|
|
{
|
|
if (IsNextElement(SerializationStrings.MemberSet))
|
|
{
|
|
string name = ReadNameAttribute();
|
|
PSMemberSet set = new PSMemberSet(name);
|
|
collection.Add(set);
|
|
ReadMemberSet(set.Members);
|
|
|
|
PSGetMemberBinder.SetHasInstanceMember(name);
|
|
}
|
|
else
|
|
{
|
|
PSNoteProperty note = ReadNoteProperty();
|
|
collection.Add(note);
|
|
|
|
PSGetMemberBinder.SetHasInstanceMember(note.Name);
|
|
}
|
|
}
|
|
|
|
ReadEndElement();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Read note.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private PSNoteProperty ReadNoteProperty()
|
|
{
|
|
string name = ReadNameAttribute();
|
|
object value = ReadOneObject();
|
|
PSNoteProperty note = new PSNoteProperty(name, value);
|
|
return note;
|
|
}
|
|
|
|
#endregion memberset
|
|
|
|
#region known container
|
|
|
|
private bool IsKnownContainerTag(out ContainerType ct)
|
|
{
|
|
Dbg.Assert(_reader.NodeType == XmlNodeType.Element, "Expected node type is element");
|
|
|
|
if (IsNextElement(SerializationStrings.DictionaryTag))
|
|
{
|
|
ct = ContainerType.Dictionary;
|
|
}
|
|
else if (IsNextElement(SerializationStrings.QueueTag))
|
|
{
|
|
ct = ContainerType.Queue;
|
|
}
|
|
else if (IsNextElement(SerializationStrings.StackTag))
|
|
{
|
|
ct = ContainerType.Stack;
|
|
}
|
|
else if (IsNextElement(SerializationStrings.ListTag))
|
|
{
|
|
ct = ContainerType.List;
|
|
}
|
|
else if (IsNextElement(SerializationStrings.CollectionTag))
|
|
{
|
|
ct = ContainerType.Enumerable;
|
|
}
|
|
else
|
|
{
|
|
ct = ContainerType.None;
|
|
}
|
|
|
|
return ct != ContainerType.None;
|
|
}
|
|
|
|
private object ReadKnownContainer(ContainerType ct)
|
|
{
|
|
switch (ct)
|
|
{
|
|
case ContainerType.Dictionary:
|
|
return ReadDictionary(ct);
|
|
|
|
case ContainerType.Enumerable:
|
|
case ContainerType.List:
|
|
case ContainerType.Queue:
|
|
case ContainerType.Stack:
|
|
return ReadListContainer(ct);
|
|
|
|
default:
|
|
Dbg.Assert(false, "Unrecognized ContainerType enum");
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Read List Containers.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private object ReadListContainer(ContainerType ct)
|
|
{
|
|
Dbg.Assert(ct == ContainerType.Enumerable ||
|
|
ct == ContainerType.List ||
|
|
ct == ContainerType.Queue ||
|
|
ct == ContainerType.Stack, "ct should be queue, stack, enumerable or list");
|
|
|
|
ArrayList list = new ArrayList();
|
|
if (ReadStartElementAndHandleEmpty(_reader.LocalName))
|
|
{
|
|
while (_reader.NodeType == XmlNodeType.Element)
|
|
{
|
|
list.Add(ReadOneObject());
|
|
}
|
|
|
|
ReadEndElement();
|
|
}
|
|
|
|
if (ct == ContainerType.Stack)
|
|
{
|
|
list.Reverse();
|
|
return new Stack(list);
|
|
}
|
|
else if (ct == ContainerType.Queue)
|
|
{
|
|
return new Queue(list);
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deserialize Dictionary.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private object ReadDictionary(ContainerType ct)
|
|
{
|
|
Dbg.Assert(ct == ContainerType.Dictionary, "Unrecognized ContainerType enum");
|
|
|
|
// We assume the hash table is a PowerShell hash table and hence uses
|
|
// a case insensitive string comparer. If we discover a key collision,
|
|
// we'll revert back to the default comparer.
|
|
Hashtable table = new Hashtable(StringComparer.CurrentCultureIgnoreCase);
|
|
int keyClashFoundIteration = 0;
|
|
if (ReadStartElementAndHandleEmpty(SerializationStrings.DictionaryTag))
|
|
{
|
|
while (_reader.NodeType == XmlNodeType.Element)
|
|
{
|
|
ReadStartElement(SerializationStrings.DictionaryEntryTag);
|
|
|
|
// Read Key
|
|
if (_reader.NodeType != XmlNodeType.Element)
|
|
{
|
|
throw NewXmlException(Serialization.DictionaryKeyNotSpecified, null);
|
|
}
|
|
|
|
string name = ReadNameAttribute();
|
|
if (!string.Equals(name, SerializationStrings.DictionaryKey, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
throw NewXmlException(Serialization.InvalidDictionaryKeyName, null);
|
|
}
|
|
|
|
object key = ReadOneObject();
|
|
|
|
if (key == null)
|
|
{
|
|
throw NewXmlException(Serialization.NullAsDictionaryKey, null);
|
|
}
|
|
// Read Value
|
|
if (_reader.NodeType != XmlNodeType.Element)
|
|
{
|
|
throw NewXmlException(Serialization.DictionaryValueNotSpecified, null);
|
|
}
|
|
|
|
name = ReadNameAttribute();
|
|
if (!string.Equals(name, SerializationStrings.DictionaryValue, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
throw NewXmlException(Serialization.InvalidDictionaryValueName, null);
|
|
}
|
|
|
|
object value = ReadOneObject();
|
|
|
|
// On the first collision, copy the hash table to one that uses the default comparer.
|
|
if (table.ContainsKey(key) && (keyClashFoundIteration == 0))
|
|
{
|
|
keyClashFoundIteration++;
|
|
Hashtable newHashTable = new Hashtable();
|
|
foreach (DictionaryEntry entry in table)
|
|
{
|
|
newHashTable.Add(entry.Key, entry.Value);
|
|
}
|
|
|
|
table = newHashTable;
|
|
}
|
|
|
|
// win8: 389060. If there are still collisions even with case-sensitive default comparer,
|
|
// use an IEqualityComparer that does object ref equality.
|
|
if (table.ContainsKey(key) && (keyClashFoundIteration == 1))
|
|
{
|
|
keyClashFoundIteration++;
|
|
IEqualityComparer equalityComparer = new ReferenceEqualityComparer();
|
|
Hashtable newHashTable = new Hashtable(equalityComparer);
|
|
foreach (DictionaryEntry entry in table)
|
|
{
|
|
newHashTable.Add(entry.Key, entry.Value);
|
|
}
|
|
|
|
table = newHashTable;
|
|
}
|
|
|
|
try
|
|
{
|
|
// Add entry to hashtable
|
|
table.Add(key, value);
|
|
}
|
|
catch (ArgumentException e)
|
|
{
|
|
throw this.NewXmlException(Serialization.InvalidPrimitiveType, e, typeof(Hashtable));
|
|
}
|
|
|
|
ReadEndElement();
|
|
}
|
|
|
|
ReadEndElement();
|
|
}
|
|
|
|
return table;
|
|
}
|
|
|
|
#endregion known containers
|
|
|
|
#endregion deserialization
|
|
|
|
#region Getting XmlReaderSettings
|
|
|
|
internal static XmlReaderSettings XmlReaderSettingsForCliXml { get; } = GetXmlReaderSettingsForCliXml();
|
|
|
|
private static XmlReaderSettings GetXmlReaderSettingsForCliXml()
|
|
{
|
|
XmlReaderSettings xrs = new XmlReaderSettings();
|
|
|
|
xrs.CheckCharacters = false;
|
|
xrs.CloseInput = false;
|
|
|
|
// The XML data needs to be in conformance to the rules for a well-formed XML 1.0 document.
|
|
xrs.ConformanceLevel = ConformanceLevel.Document;
|
|
xrs.IgnoreComments = true;
|
|
xrs.IgnoreProcessingInstructions = true;
|
|
xrs.IgnoreWhitespace = false;
|
|
xrs.MaxCharactersFromEntities = 1024;
|
|
xrs.XmlResolver = null;
|
|
xrs.DtdProcessing = DtdProcessing.Prohibit;
|
|
xrs.Schemas = null;
|
|
xrs.ValidationFlags = System.Xml.Schema.XmlSchemaValidationFlags.None;
|
|
xrs.ValidationType = ValidationType.None;
|
|
return xrs;
|
|
}
|
|
|
|
internal static XmlReaderSettings XmlReaderSettingsForUntrustedXmlDocument { get; } = GetXmlReaderSettingsForUntrustedXmlDocument();
|
|
|
|
private static XmlReaderSettings GetXmlReaderSettingsForUntrustedXmlDocument()
|
|
{
|
|
XmlReaderSettings settings = new XmlReaderSettings();
|
|
|
|
settings.CheckCharacters = false;
|
|
settings.ConformanceLevel = ConformanceLevel.Auto;
|
|
settings.IgnoreComments = true;
|
|
settings.IgnoreProcessingInstructions = true;
|
|
settings.IgnoreWhitespace = true;
|
|
settings.MaxCharactersFromEntities = 1024;
|
|
settings.MaxCharactersInDocument = 512 * 1024 * 1024; // 512M characters = 1GB
|
|
settings.XmlResolver = null;
|
|
settings.DtdProcessing = DtdProcessing.Parse; // Allowing DTD parsing with limits of MaxCharactersFromEntities/MaxCharactersInDocument
|
|
settings.ValidationFlags = System.Xml.Schema.XmlSchemaValidationFlags.None;
|
|
settings.ValidationType = ValidationType.None;
|
|
return settings;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region known type deserialization
|
|
|
|
internal static object DeserializeBoolean(InternalDeserializer deserializer)
|
|
{
|
|
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
|
|
try
|
|
{
|
|
return XmlConvert.ToBoolean(deserializer._reader.ReadElementContentAsString());
|
|
}
|
|
catch (FormatException e)
|
|
{
|
|
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, e, typeof(bool).FullName);
|
|
}
|
|
}
|
|
|
|
internal static object DeserializeByte(InternalDeserializer deserializer)
|
|
{
|
|
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
|
|
Exception recognizedException = null;
|
|
try
|
|
{
|
|
return XmlConvert.ToByte(deserializer._reader.ReadElementContentAsString());
|
|
}
|
|
catch (FormatException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
catch (OverflowException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
|
|
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(byte).FullName);
|
|
}
|
|
|
|
internal static object DeserializeChar(InternalDeserializer deserializer)
|
|
{
|
|
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
|
|
Exception recognizedException = null;
|
|
try
|
|
{
|
|
return (char)XmlConvert.ToUInt16(deserializer._reader.ReadElementContentAsString());
|
|
}
|
|
catch (FormatException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
catch (OverflowException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
|
|
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(char).FullName);
|
|
}
|
|
|
|
internal static object DeserializeDateTime(InternalDeserializer deserializer)
|
|
{
|
|
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
|
|
try
|
|
{
|
|
return XmlConvert.ToDateTime(deserializer._reader.ReadElementContentAsString(), XmlDateTimeSerializationMode.RoundtripKind);
|
|
}
|
|
catch (FormatException e)
|
|
{
|
|
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, e, typeof(DateTime).FullName);
|
|
}
|
|
}
|
|
|
|
internal static object DeserializeDecimal(InternalDeserializer deserializer)
|
|
{
|
|
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
|
|
Exception recognizedException = null;
|
|
try
|
|
{
|
|
return XmlConvert.ToDecimal(deserializer._reader.ReadElementContentAsString());
|
|
}
|
|
catch (FormatException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
catch (OverflowException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
|
|
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(decimal).FullName);
|
|
}
|
|
|
|
internal static object DeserializeDouble(InternalDeserializer deserializer)
|
|
{
|
|
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
|
|
Exception recognizedException = null;
|
|
try
|
|
{
|
|
return XmlConvert.ToDouble(deserializer._reader.ReadElementContentAsString());
|
|
}
|
|
catch (FormatException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
catch (OverflowException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
|
|
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(double).FullName);
|
|
}
|
|
|
|
internal static object DeserializeGuid(InternalDeserializer deserializer)
|
|
{
|
|
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
|
|
Exception recognizedException = null;
|
|
try
|
|
{
|
|
return XmlConvert.ToGuid(deserializer._reader.ReadElementContentAsString());
|
|
}
|
|
// MSDN for XmlConvert.ToGuid doesn't list any exceptions, but
|
|
// Reflector shows that this just calls to new Guid(string)
|
|
// which MSDN documents can throw Format/OverflowException
|
|
catch (FormatException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
catch (OverflowException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
|
|
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(Guid).FullName);
|
|
}
|
|
|
|
internal static object DeserializeVersion(InternalDeserializer deserializer)
|
|
{
|
|
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
|
|
Exception recognizedException = null;
|
|
try
|
|
{
|
|
return new Version(deserializer._reader.ReadElementContentAsString());
|
|
}
|
|
catch (ArgumentException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
catch (FormatException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
catch (OverflowException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
|
|
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(Version).FullName);
|
|
}
|
|
|
|
internal static object DeserializeSemanticVersion(InternalDeserializer deserializer)
|
|
{
|
|
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
|
|
Exception recognizedException = null;
|
|
try
|
|
{
|
|
return new SemanticVersion(deserializer._reader.ReadElementContentAsString());
|
|
}
|
|
catch (ArgumentException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
catch (FormatException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
catch (OverflowException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
|
|
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(Version).FullName);
|
|
}
|
|
|
|
internal static object DeserializeInt16(InternalDeserializer deserializer)
|
|
{
|
|
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
|
|
Exception recognizedException = null;
|
|
try
|
|
{
|
|
return XmlConvert.ToInt16(deserializer._reader.ReadElementContentAsString());
|
|
}
|
|
catch (FormatException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
catch (OverflowException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
|
|
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(Int16).FullName);
|
|
}
|
|
|
|
internal static object DeserializeInt32(InternalDeserializer deserializer)
|
|
{
|
|
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
|
|
Exception recognizedException = null;
|
|
try
|
|
{
|
|
return XmlConvert.ToInt32(deserializer._reader.ReadElementContentAsString());
|
|
}
|
|
catch (FormatException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
catch (OverflowException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
|
|
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(Int32).FullName);
|
|
}
|
|
|
|
internal static object DeserializeInt64(InternalDeserializer deserializer)
|
|
{
|
|
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
|
|
Exception recognizedException = null;
|
|
try
|
|
{
|
|
return XmlConvert.ToInt64(deserializer._reader.ReadElementContentAsString());
|
|
}
|
|
catch (FormatException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
catch (OverflowException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
|
|
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(Int64).FullName);
|
|
}
|
|
|
|
internal static object DeserializeSByte(InternalDeserializer deserializer)
|
|
{
|
|
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
|
|
Exception recognizedException = null;
|
|
try
|
|
{
|
|
return XmlConvert.ToSByte(deserializer._reader.ReadElementContentAsString());
|
|
}
|
|
catch (FormatException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
catch (OverflowException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
|
|
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(sbyte).FullName);
|
|
}
|
|
|
|
internal static object DeserializeSingle(InternalDeserializer deserializer)
|
|
{
|
|
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
|
|
Exception recognizedException = null;
|
|
try
|
|
{
|
|
return XmlConvert.ToSingle(deserializer._reader.ReadElementContentAsString());
|
|
}
|
|
catch (FormatException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
catch (OverflowException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
|
|
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(float).FullName);
|
|
}
|
|
|
|
internal static object DeserializeScriptBlock(InternalDeserializer deserializer)
|
|
{
|
|
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
|
|
string scriptBlockBody = deserializer.ReadDecodedElementString(SerializationStrings.ScriptBlockTag);
|
|
if ((deserializer._context.options & DeserializationOptions.DeserializeScriptBlocks) == DeserializationOptions.DeserializeScriptBlocks)
|
|
{
|
|
return ScriptBlock.Create(scriptBlockBody);
|
|
}
|
|
else
|
|
{
|
|
// Scriptblock is deserialized as string
|
|
return scriptBlockBody;
|
|
}
|
|
}
|
|
|
|
internal static object DeserializeString(InternalDeserializer deserializer)
|
|
{
|
|
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
|
|
return deserializer.ReadDecodedElementString(SerializationStrings.StringTag);
|
|
}
|
|
|
|
internal static object DeserializeTimeSpan(InternalDeserializer deserializer)
|
|
{
|
|
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
|
|
try
|
|
{
|
|
return XmlConvert.ToTimeSpan(deserializer._reader.ReadElementContentAsString());
|
|
}
|
|
catch (FormatException e)
|
|
{
|
|
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, e, typeof(TimeSpan).FullName);
|
|
}
|
|
}
|
|
|
|
internal static object DeserializeUInt16(InternalDeserializer deserializer)
|
|
{
|
|
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
|
|
Exception recognizedException = null;
|
|
try
|
|
{
|
|
return XmlConvert.ToUInt16(deserializer._reader.ReadElementContentAsString());
|
|
}
|
|
catch (FormatException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
catch (OverflowException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
|
|
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(UInt16).FullName);
|
|
}
|
|
|
|
internal static object DeserializeUInt32(InternalDeserializer deserializer)
|
|
{
|
|
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
|
|
Exception recognizedException = null;
|
|
try
|
|
{
|
|
return XmlConvert.ToUInt32(deserializer._reader.ReadElementContentAsString());
|
|
}
|
|
catch (FormatException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
catch (OverflowException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
|
|
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(UInt32).FullName);
|
|
}
|
|
|
|
internal static object DeserializeUInt64(InternalDeserializer deserializer)
|
|
{
|
|
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
|
|
Exception recognizedException = null;
|
|
try
|
|
{
|
|
return XmlConvert.ToUInt64(deserializer._reader.ReadElementContentAsString());
|
|
}
|
|
catch (FormatException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
catch (OverflowException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
|
|
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(UInt64).FullName);
|
|
}
|
|
|
|
internal static object DeserializeUri(InternalDeserializer deserializer)
|
|
{
|
|
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
|
|
try
|
|
{
|
|
string uriString = deserializer.ReadDecodedElementString(SerializationStrings.AnyUriTag);
|
|
return new Uri(uriString, UriKind.RelativeOrAbsolute);
|
|
}
|
|
catch (UriFormatException e)
|
|
{
|
|
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, e, typeof(Uri).FullName);
|
|
}
|
|
}
|
|
|
|
internal static object DeserializeByteArray(InternalDeserializer deserializer)
|
|
{
|
|
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
|
|
try
|
|
{
|
|
return Convert.FromBase64String(deserializer._reader.ReadElementContentAsString());
|
|
}
|
|
catch (FormatException e)
|
|
{
|
|
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, e, typeof(byte[]).FullName);
|
|
}
|
|
}
|
|
|
|
/// <exception cref="System.Xml.XmlException"></exception>
|
|
internal static XmlDocument LoadUnsafeXmlDocument(FileInfo xmlPath, bool preserveNonElements, int? maxCharactersInDocument)
|
|
{
|
|
XmlDocument doc = null;
|
|
// same FileStream options as Reflector shows for XmlDocument.Load(path) / XmlDownloadManager.GetStream:
|
|
using (Stream stream = new FileStream(xmlPath.FullName, FileMode.Open, FileAccess.Read, FileShare.Read))
|
|
{
|
|
doc = LoadUnsafeXmlDocument(stream, preserveNonElements, maxCharactersInDocument);
|
|
}
|
|
|
|
return doc;
|
|
}
|
|
|
|
/// <exception cref="System.Xml.XmlException"></exception>
|
|
internal static XmlDocument LoadUnsafeXmlDocument(string xmlContents, bool preserveNonElements, int? maxCharactersInDocument)
|
|
{
|
|
using (TextReader textReader = new StringReader(xmlContents))
|
|
{
|
|
return LoadUnsafeXmlDocument(textReader, preserveNonElements, maxCharactersInDocument);
|
|
}
|
|
}
|
|
|
|
/// <exception cref="System.Xml.XmlException"></exception>
|
|
internal static XmlDocument LoadUnsafeXmlDocument(Stream stream, bool preserveNonElements, int? maxCharactersInDocument)
|
|
{
|
|
using (TextReader textReader = new StreamReader(stream))
|
|
{
|
|
return LoadUnsafeXmlDocument(textReader, preserveNonElements, maxCharactersInDocument);
|
|
}
|
|
}
|
|
|
|
/// <exception cref="System.Xml.XmlException"></exception>
|
|
internal static XmlDocument LoadUnsafeXmlDocument(TextReader textReader, bool preserveNonElements, int? maxCharactersInDocument)
|
|
{
|
|
XmlReaderSettings settings;
|
|
if (maxCharactersInDocument.HasValue || preserveNonElements)
|
|
{
|
|
settings = InternalDeserializer.XmlReaderSettingsForUntrustedXmlDocument.Clone();
|
|
if (maxCharactersInDocument.HasValue)
|
|
{
|
|
settings.MaxCharactersInDocument = maxCharactersInDocument.Value;
|
|
}
|
|
|
|
if (preserveNonElements)
|
|
{
|
|
settings.IgnoreWhitespace = false;
|
|
settings.IgnoreProcessingInstructions = false;
|
|
settings.IgnoreComments = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
settings = InternalDeserializer.XmlReaderSettingsForUntrustedXmlDocument;
|
|
}
|
|
|
|
try
|
|
{
|
|
XmlReader xmlReader = XmlReader.Create(textReader, settings);
|
|
XmlDocument xmlDocument = new XmlDocument();
|
|
xmlDocument.PreserveWhitespace = preserveNonElements;
|
|
xmlDocument.Load(xmlReader);
|
|
return xmlDocument;
|
|
}
|
|
catch (InvalidOperationException invalidOperationException)
|
|
{
|
|
throw new XmlException(invalidOperationException.Message, invalidOperationException);
|
|
}
|
|
}
|
|
|
|
internal static object DeserializeXmlDocument(InternalDeserializer deserializer)
|
|
{
|
|
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
|
|
string docAsString = deserializer.ReadDecodedElementString(SerializationStrings.XmlDocumentTag);
|
|
|
|
try
|
|
{
|
|
int? maxCharactersInDocument = null;
|
|
if (deserializer._context.MaximumAllowedMemory.HasValue)
|
|
{
|
|
maxCharactersInDocument = deserializer._context.MaximumAllowedMemory.Value / sizeof(char);
|
|
}
|
|
|
|
XmlDocument doc = InternalDeserializer.LoadUnsafeXmlDocument(
|
|
docAsString,
|
|
true, /* preserve whitespace, comments, etc. */
|
|
maxCharactersInDocument);
|
|
|
|
deserializer._context.LogExtraMemoryUsage((docAsString.Length - doc.OuterXml.Length) * sizeof(char));
|
|
|
|
return doc;
|
|
}
|
|
catch (XmlException e)
|
|
{
|
|
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, e, typeof(XmlDocument).FullName);
|
|
}
|
|
}
|
|
|
|
internal static object DeserializeProgressRecord(InternalDeserializer deserializer)
|
|
{
|
|
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
|
|
|
|
//
|
|
// read deserialized elements of a progress record
|
|
//
|
|
|
|
deserializer.ReadStartElement(SerializationStrings.ProgressRecord);
|
|
|
|
string activity = null, currentOperation = null, prt = null, statusDescription = null;
|
|
int activityId = 0, parentActivityId = 0, percentComplete = 0, secondsRemaining = 0;
|
|
|
|
Exception recognizedException = null;
|
|
try
|
|
{
|
|
activity = deserializer.ReadDecodedElementString(SerializationStrings.ProgressRecordActivity);
|
|
activityId = int.Parse(deserializer.ReadDecodedElementString(SerializationStrings.ProgressRecordActivityId), CultureInfo.InvariantCulture);
|
|
|
|
object tmp = deserializer.ReadOneObject();
|
|
currentOperation = tmp?.ToString();
|
|
|
|
parentActivityId = int.Parse(deserializer.ReadDecodedElementString(SerializationStrings.ProgressRecordParentActivityId), CultureInfo.InvariantCulture);
|
|
percentComplete = int.Parse(deserializer.ReadDecodedElementString(SerializationStrings.ProgressRecordPercentComplete), CultureInfo.InvariantCulture);
|
|
prt = deserializer.ReadDecodedElementString(SerializationStrings.ProgressRecordType);
|
|
secondsRemaining = int.Parse(deserializer.ReadDecodedElementString(SerializationStrings.ProgressRecordSecondsRemaining), CultureInfo.InvariantCulture);
|
|
statusDescription = deserializer.ReadDecodedElementString(SerializationStrings.ProgressRecordStatusDescription);
|
|
}
|
|
catch (FormatException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
catch (OverflowException e)
|
|
{
|
|
recognizedException = e;
|
|
}
|
|
|
|
if (recognizedException != null)
|
|
{
|
|
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(UInt64).FullName);
|
|
}
|
|
|
|
deserializer.ReadEndElement();
|
|
|
|
//
|
|
// Build the progress record
|
|
//
|
|
|
|
ProgressRecordType type;
|
|
try
|
|
{
|
|
type = (ProgressRecordType)Enum.Parse(typeof(ProgressRecordType), prt, true);
|
|
}
|
|
catch (ArgumentException e)
|
|
{
|
|
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, e, typeof(ProgressRecord).FullName);
|
|
}
|
|
|
|
try
|
|
{
|
|
ProgressRecord record = new ProgressRecord(activityId, activity, statusDescription);
|
|
|
|
if (!string.IsNullOrEmpty(currentOperation))
|
|
{
|
|
record.CurrentOperation = currentOperation;
|
|
}
|
|
|
|
record.ParentActivityId = parentActivityId;
|
|
record.PercentComplete = percentComplete;
|
|
record.RecordType = type;
|
|
record.SecondsRemaining = secondsRemaining;
|
|
|
|
return record;
|
|
}
|
|
catch (ArgumentException e)
|
|
{
|
|
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, e, typeof(ProgressRecord).FullName);
|
|
}
|
|
}
|
|
|
|
internal static object DeserializeSecureString(InternalDeserializer deserializer)
|
|
{
|
|
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
|
|
|
|
//
|
|
// read deserialized elements of a Secure String
|
|
//
|
|
return deserializer.ReadSecureString();
|
|
}
|
|
|
|
#endregion known type deserialization
|
|
|
|
#region misc
|
|
|
|
/// <summary>
|
|
/// Check if LocalName of next element is "tag"
|
|
/// </summary>
|
|
/// <param name="tag"></param>
|
|
/// <returns></returns>
|
|
private bool IsNextElement(string tag)
|
|
{
|
|
Dbg.Assert(!string.IsNullOrEmpty(tag), "Caller should validate the parameter");
|
|
return (_reader.LocalName == tag) &&
|
|
(((_context.options & DeserializationOptions.NoNamespace) != 0) ||
|
|
(_reader.NamespaceURI == SerializationStrings.MonadNamespace));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Read start element in monad namespace.
|
|
/// </summary>
|
|
/// <param name="element">Element tag to read.</param>
|
|
/// <returns>True if not an empty element else false.</returns>
|
|
internal bool ReadStartElementAndHandleEmpty(string element)
|
|
{
|
|
Dbg.Assert(!string.IsNullOrEmpty(element), "Caller should validate the parameter");
|
|
|
|
// IsEmpty is set to true when element is of the form <tag/>
|
|
bool isEmpty = _reader.IsEmptyElement;
|
|
|
|
this.ReadStartElement(element);
|
|
|
|
// This takes care of the case: <tag></tag> or <tag> </tag>. In
|
|
// this case isEmpty is false.
|
|
if (!isEmpty && _reader.NodeType == XmlNodeType.EndElement)
|
|
{
|
|
ReadEndElement();
|
|
isEmpty = true;
|
|
}
|
|
|
|
return !isEmpty;
|
|
}
|
|
|
|
private void ReadStartElement(string element)
|
|
{
|
|
Dbg.Assert(!string.IsNullOrEmpty(element), "Caller should validate the parameter");
|
|
|
|
if ((_context.options & DeserializationOptions.NoNamespace) == DeserializationOptions.NoNamespace)
|
|
{
|
|
_reader.ReadStartElement(element);
|
|
}
|
|
else
|
|
{
|
|
_reader.ReadStartElement(element, SerializationStrings.MonadNamespace);
|
|
}
|
|
|
|
_reader.MoveToContent();
|
|
}
|
|
|
|
private void ReadEndElement()
|
|
{
|
|
_reader.ReadEndElement();
|
|
_reader.MoveToContent();
|
|
}
|
|
|
|
private string ReadDecodedElementString(string element)
|
|
{
|
|
Dbg.Assert(!string.IsNullOrEmpty(element), "Caller should validate the parameter");
|
|
this.CheckIfStopping();
|
|
|
|
string temp = null;
|
|
if ((_context.options & DeserializationOptions.NoNamespace) == DeserializationOptions.NoNamespace)
|
|
{
|
|
temp = _reader.ReadElementContentAsString(element, string.Empty);
|
|
}
|
|
else
|
|
{
|
|
temp = _reader.ReadElementContentAsString(element, SerializationStrings.MonadNamespace);
|
|
}
|
|
|
|
_reader.MoveToContent();
|
|
temp = DecodeString(temp);
|
|
return temp;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Skips an element and all its child elements.
|
|
/// Moves cursor to next content Node.
|
|
/// </summary>
|
|
private void Skip()
|
|
{
|
|
_reader.Skip();
|
|
_reader.MoveToContent();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reads Primary known type.
|
|
/// </summary>
|
|
/// <param name="pktInfo"></param>
|
|
/// <returns></returns>
|
|
private object ReadPrimaryKnownType(TypeSerializationInfo pktInfo)
|
|
{
|
|
Dbg.Assert(pktInfo != null, "Deserializer should be available");
|
|
Dbg.Assert(pktInfo.Deserializer != null, "Deserializer should be available");
|
|
object result = pktInfo.Deserializer(this);
|
|
_reader.MoveToContent();
|
|
return result;
|
|
}
|
|
|
|
private object ReadSecureString()
|
|
{
|
|
string encryptedString = _reader.ReadElementContentAsString();
|
|
|
|
try
|
|
{
|
|
object result;
|
|
if (_context.cryptoHelper != null)
|
|
{
|
|
result = _context.cryptoHelper.DecryptSecureString(encryptedString);
|
|
}
|
|
else
|
|
{
|
|
result = Microsoft.PowerShell.SecureStringHelper.Unprotect(encryptedString);
|
|
}
|
|
|
|
_reader.MoveToContent();
|
|
return result;
|
|
}
|
|
catch (PSCryptoException)
|
|
{
|
|
throw NewXmlException(Serialization.DeserializeSecureStringFailed, null);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper function for building XmlException.
|
|
/// </summary>
|
|
/// <param name="resourceString">
|
|
/// resource String
|
|
/// </param>
|
|
/// <param name="innerException"></param>
|
|
/// <param name="args">
|
|
/// params for format string obtained from resourceId
|
|
/// </param>
|
|
private XmlException NewXmlException
|
|
(
|
|
string resourceString,
|
|
Exception innerException,
|
|
params object[] args
|
|
)
|
|
{
|
|
Dbg.Assert(!string.IsNullOrEmpty(resourceString), "Caller should validate the parameter");
|
|
|
|
string message = StringUtil.Format(resourceString, args);
|
|
|
|
XmlException ex = null;
|
|
IXmlLineInfo xmlLineInfo = _reader as IXmlLineInfo;
|
|
if (xmlLineInfo != null)
|
|
{
|
|
if (xmlLineInfo.HasLineInfo())
|
|
{
|
|
ex = new XmlException
|
|
(
|
|
message,
|
|
innerException,
|
|
xmlLineInfo.LineNumber,
|
|
xmlLineInfo.LinePosition
|
|
);
|
|
}
|
|
}
|
|
|
|
return ex ?? new XmlException(message, innerException);
|
|
}
|
|
|
|
private string ReadNameAttribute()
|
|
{
|
|
string encodedName = _reader.GetAttribute(SerializationStrings.NameAttribute);
|
|
if (encodedName == null)
|
|
{
|
|
throw NewXmlException(Serialization.AttributeExpected, null, SerializationStrings.NameAttribute);
|
|
}
|
|
|
|
return DecodeString(encodedName);
|
|
}
|
|
|
|
private static string DecodeString(string s)
|
|
{
|
|
Dbg.Assert(s != null, "Caller should validate the parameter");
|
|
return XmlConvert.DecodeName(s);
|
|
}
|
|
|
|
#endregion misc
|
|
|
|
[TraceSourceAttribute("InternalDeserializer", "InternalDeserializer class")]
|
|
private static readonly PSTraceSource s_trace = PSTraceSource.GetTracer("InternalDeserializer", "InternalDeserializer class");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper class for generating reference id.
|
|
/// </summary>
|
|
internal class ReferenceIdHandlerForSerializer<T> where T : class
|
|
{
|
|
/// <summary>
|
|
/// Get new reference id.
|
|
/// </summary>
|
|
/// <returns>New reference id.</returns>
|
|
private UInt64 GetNewReferenceId()
|
|
{
|
|
UInt64 refId = _seed++;
|
|
return refId;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Seed is incremented by one after each reference generation.
|
|
/// </summary>
|
|
private UInt64 _seed;
|
|
|
|
// note:
|
|
// any boxed UInt64 takes 16 bytes on the heap
|
|
// one-character string (i.e. "7") takes 20 bytes on the heap
|
|
private readonly IDictionary<T, UInt64> _object2refId;
|
|
|
|
internal ReferenceIdHandlerForSerializer(IDictionary<T, UInt64> dictionary)
|
|
{
|
|
_object2refId = dictionary;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Assigns a RefId to the given object.
|
|
/// </summary>
|
|
/// <param name="t">Object to assign a RefId to.</param>
|
|
/// <returns>RefId assigned to the object.</returns>
|
|
internal string SetRefId(T t)
|
|
{
|
|
if (_object2refId != null)
|
|
{
|
|
Dbg.Assert(!_object2refId.ContainsKey(t), "SetRefId shouldn't be called when the object is already assigned a ref id");
|
|
UInt64 refId = GetNewReferenceId();
|
|
_object2refId.Add(t, refId);
|
|
return refId.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a RefId already assigned for the given object or <see langword="null"/> if there is no associated ref id.
|
|
/// </summary>
|
|
/// <param name="t"></param>
|
|
/// <returns></returns>
|
|
internal string GetRefId(T t)
|
|
{
|
|
UInt64 refId;
|
|
if ((_object2refId != null) && (_object2refId.TryGetValue(t, out refId)))
|
|
{
|
|
return refId.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal class ReferenceIdHandlerForDeserializer<T> where T : class
|
|
{
|
|
private readonly Dictionary<string, T> _refId2object = new Dictionary<string, T>();
|
|
|
|
internal void SetRefId(T o, string refId, bool duplicateRefIdsAllowed)
|
|
{
|
|
#if DEBUG
|
|
if (!duplicateRefIdsAllowed)
|
|
{
|
|
Dbg.Assert(!_refId2object.ContainsKey(refId), "You can't change refId association");
|
|
}
|
|
#endif
|
|
_refId2object[refId] = o;
|
|
}
|
|
|
|
internal T GetReferencedObject(string refId)
|
|
{
|
|
Dbg.Assert(_refId2object.ContainsKey(refId), "Reference id wasn't seen earlier");
|
|
T t;
|
|
if (_refId2object.TryGetValue(refId, out t))
|
|
{
|
|
return t;
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// A delegate for serializing known type.
|
|
/// </summary>
|
|
internal delegate void TypeSerializerDelegate(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry);
|
|
/// <summary>
|
|
/// A delegate for deserializing known type.
|
|
/// </summary>
|
|
internal delegate object TypeDeserializerDelegate(InternalDeserializer deserializer);
|
|
|
|
/// <summary>
|
|
/// This class contains serialization information about a type.
|
|
/// </summary>
|
|
internal class TypeSerializationInfo
|
|
{
|
|
/// <summary>
|
|
/// Constructor.
|
|
/// </summary>
|
|
/// <param name="type">Type for which this entry is created.</param>
|
|
/// <param name="itemTag">ItemTag for the type.</param>
|
|
/// <param name="propertyTag">PropertyTag for the type.</param>
|
|
/// <param name="serializer">TypeSerializerDelegate for serializing the type.</param>
|
|
/// <param name="deserializer">TypeDeserializerDelegate for deserializing the type.</param>
|
|
internal TypeSerializationInfo(Type type, string itemTag, string propertyTag, TypeSerializerDelegate serializer, TypeDeserializerDelegate deserializer)
|
|
{
|
|
Type = type;
|
|
Serializer = serializer;
|
|
Deserializer = deserializer;
|
|
ItemTag = itemTag;
|
|
PropertyTag = propertyTag;
|
|
}
|
|
|
|
#region properties
|
|
|
|
/// <summary>
|
|
/// Get the type for which this TypeSerializationInfo is created.
|
|
/// </summary>
|
|
internal Type Type { get; }
|
|
|
|
/// <summary>
|
|
/// Get the item tag for this type.
|
|
/// </summary>
|
|
internal string ItemTag { get; }
|
|
|
|
/// <summary>
|
|
/// Get the Property tag for this type.
|
|
/// </summary>
|
|
internal string PropertyTag { get; }
|
|
|
|
/// <summary>
|
|
/// Gets the delegate to serialize this type.
|
|
/// </summary>
|
|
internal TypeSerializerDelegate Serializer { get; }
|
|
|
|
/// <summary>
|
|
/// Gets the delegate to deserialize this type.
|
|
/// </summary>
|
|
internal TypeDeserializerDelegate Deserializer { get; }
|
|
|
|
#endregion properties
|
|
|
|
#region private
|
|
|
|
#endregion private
|
|
}
|
|
|
|
/// <summary>
|
|
/// A class for identifying types which are treated as KnownType by Monad.
|
|
/// A KnownType is guranteed to be available on machine on which monad is
|
|
/// running.
|
|
/// </summary>
|
|
internal static class KnownTypes
|
|
{
|
|
/// <summary>
|
|
/// Static constructor.
|
|
/// </summary>
|
|
static KnownTypes()
|
|
{
|
|
for (int i = 0; i < s_typeSerializationInfo.Length; i++)
|
|
{
|
|
s_knownTableKeyType.Add(s_typeSerializationInfo[i].Type.FullName, s_typeSerializationInfo[i]);
|
|
s_knownTableKeyItemTag.Add(s_typeSerializationInfo[i].ItemTag, s_typeSerializationInfo[i]);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the type serialization information about a type.
|
|
/// </summary>
|
|
/// <param name="type">Type for which information is retrieved.</param>
|
|
/// <returns>TypeSerializationInfo for the type, null if it doesn't exist.</returns>
|
|
internal static TypeSerializationInfo GetTypeSerializationInfo(Type type)
|
|
{
|
|
TypeSerializationInfo temp;
|
|
if (!s_knownTableKeyType.TryGetValue(type.FullName, out temp) && typeof(XmlDocument).IsAssignableFrom(type))
|
|
{
|
|
temp = s_xdInfo;
|
|
}
|
|
|
|
return temp;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get TypeSerializationInfo using ItemTag as key.
|
|
/// </summary>
|
|
/// <param name="itemTag">ItemTag for which TypeSerializationInfo is to be fetched.</param>
|
|
/// <returns>TypeSerializationInfo entry, null if no entry exist for the tag.</returns>
|
|
internal static TypeSerializationInfo GetTypeSerializationInfoFromItemTag(string itemTag)
|
|
{
|
|
TypeSerializationInfo temp;
|
|
s_knownTableKeyItemTag.TryGetValue(itemTag, out temp);
|
|
return temp;
|
|
}
|
|
|
|
#region private_fields
|
|
|
|
// TypeSerializationInfo for XmlDocument
|
|
private static readonly TypeSerializationInfo s_xdInfo =
|
|
new TypeSerializationInfo(typeof(XmlDocument),
|
|
SerializationStrings.XmlDocumentTag,
|
|
SerializationStrings.XmlDocumentTag,
|
|
InternalSerializer.WriteXmlDocument,
|
|
InternalDeserializer.DeserializeXmlDocument);
|
|
|
|
/// <summary>
|
|
/// Array of known types.
|
|
/// </summary>
|
|
private static readonly TypeSerializationInfo[] s_typeSerializationInfo = new TypeSerializationInfo[]
|
|
{
|
|
new TypeSerializationInfo(typeof(bool),
|
|
SerializationStrings.BooleanTag,
|
|
SerializationStrings.BooleanTag,
|
|
InternalSerializer.WriteBoolean,
|
|
InternalDeserializer.DeserializeBoolean),
|
|
|
|
new TypeSerializationInfo(typeof(byte),
|
|
SerializationStrings.UnsignedByteTag,
|
|
SerializationStrings.UnsignedByteTag,
|
|
null,
|
|
InternalDeserializer.DeserializeByte),
|
|
|
|
new TypeSerializationInfo(typeof(char),
|
|
SerializationStrings.CharTag,
|
|
SerializationStrings.CharTag,
|
|
InternalSerializer.WriteChar,
|
|
InternalDeserializer.DeserializeChar),
|
|
|
|
new TypeSerializationInfo(typeof(DateTime),
|
|
SerializationStrings.DateTimeTag,
|
|
SerializationStrings.DateTimeTag,
|
|
InternalSerializer.WriteDateTime,
|
|
InternalDeserializer.DeserializeDateTime),
|
|
|
|
new TypeSerializationInfo(typeof(decimal),
|
|
SerializationStrings.DecimalTag,
|
|
SerializationStrings.DecimalTag,
|
|
null,
|
|
InternalDeserializer.DeserializeDecimal),
|
|
|
|
new TypeSerializationInfo(typeof(double),
|
|
SerializationStrings.DoubleTag,
|
|
SerializationStrings.DoubleTag,
|
|
InternalSerializer.WriteDouble,
|
|
InternalDeserializer.DeserializeDouble),
|
|
|
|
new TypeSerializationInfo(typeof(Guid),
|
|
SerializationStrings.GuidTag,
|
|
SerializationStrings.GuidTag,
|
|
null,
|
|
InternalDeserializer.DeserializeGuid),
|
|
new TypeSerializationInfo(typeof(Int16),
|
|
SerializationStrings.ShortTag,
|
|
SerializationStrings.ShortTag,
|
|
null,
|
|
InternalDeserializer.DeserializeInt16),
|
|
|
|
new TypeSerializationInfo(typeof(Int32),
|
|
SerializationStrings.IntTag,
|
|
SerializationStrings.IntTag,
|
|
null,
|
|
InternalDeserializer.DeserializeInt32),
|
|
|
|
new TypeSerializationInfo(typeof(Int64),
|
|
SerializationStrings.LongTag,
|
|
SerializationStrings.LongTag,
|
|
null,
|
|
InternalDeserializer.DeserializeInt64),
|
|
|
|
new TypeSerializationInfo(typeof(sbyte),
|
|
SerializationStrings.ByteTag,
|
|
SerializationStrings.ByteTag,
|
|
null,
|
|
InternalDeserializer.DeserializeSByte),
|
|
|
|
new TypeSerializationInfo(typeof(Single),
|
|
SerializationStrings.FloatTag,
|
|
SerializationStrings.FloatTag,
|
|
InternalSerializer.WriteSingle,
|
|
InternalDeserializer.DeserializeSingle),
|
|
|
|
new TypeSerializationInfo(typeof(ScriptBlock),
|
|
SerializationStrings.ScriptBlockTag,
|
|
SerializationStrings.ScriptBlockTag,
|
|
InternalSerializer.WriteScriptBlock,
|
|
InternalDeserializer.DeserializeScriptBlock),
|
|
|
|
new TypeSerializationInfo(typeof(string),
|
|
SerializationStrings.StringTag,
|
|
SerializationStrings.StringTag,
|
|
InternalSerializer.WriteEncodedString,
|
|
InternalDeserializer.DeserializeString),
|
|
|
|
new TypeSerializationInfo(typeof(TimeSpan),
|
|
SerializationStrings.DurationTag,
|
|
SerializationStrings.DurationTag,
|
|
InternalSerializer.WriteTimeSpan,
|
|
InternalDeserializer.DeserializeTimeSpan),
|
|
|
|
new TypeSerializationInfo(typeof(UInt16),
|
|
SerializationStrings.UnsignedShortTag,
|
|
SerializationStrings.UnsignedShortTag,
|
|
null,
|
|
InternalDeserializer.DeserializeUInt16),
|
|
|
|
new TypeSerializationInfo(typeof(UInt32),
|
|
SerializationStrings.UnsignedIntTag,
|
|
SerializationStrings.UnsignedIntTag,
|
|
null,
|
|
InternalDeserializer.DeserializeUInt32),
|
|
|
|
new TypeSerializationInfo(typeof(UInt64),
|
|
SerializationStrings.UnsignedLongTag,
|
|
SerializationStrings.UnsignedLongTag,
|
|
null,
|
|
InternalDeserializer.DeserializeUInt64),
|
|
|
|
new TypeSerializationInfo(typeof(Uri),
|
|
SerializationStrings.AnyUriTag,
|
|
SerializationStrings.AnyUriTag,
|
|
InternalSerializer.WriteUri,
|
|
InternalDeserializer.DeserializeUri),
|
|
|
|
new TypeSerializationInfo(typeof(byte[]),
|
|
SerializationStrings.Base64BinaryTag,
|
|
SerializationStrings.Base64BinaryTag,
|
|
InternalSerializer.WriteByteArray,
|
|
InternalDeserializer.DeserializeByteArray),
|
|
|
|
new TypeSerializationInfo(typeof(System.Version),
|
|
SerializationStrings.VersionTag,
|
|
SerializationStrings.VersionTag,
|
|
InternalSerializer.WriteVersion,
|
|
InternalDeserializer.DeserializeVersion),
|
|
|
|
s_xdInfo,
|
|
|
|
new TypeSerializationInfo(typeof(ProgressRecord),
|
|
SerializationStrings.ProgressRecord,
|
|
SerializationStrings.ProgressRecord,
|
|
InternalSerializer.WriteProgressRecord,
|
|
InternalDeserializer.DeserializeProgressRecord),
|
|
|
|
new TypeSerializationInfo(typeof(SecureString),
|
|
SerializationStrings.SecureStringTag,
|
|
SerializationStrings.SecureStringTag,
|
|
InternalSerializer.WriteSecureString,
|
|
InternalDeserializer.DeserializeSecureString),
|
|
};
|
|
|
|
/// <summary>
|
|
/// Hashtable of knowntypes.
|
|
/// Key is Type.FullName and value is Type object.
|
|
/// </summary>
|
|
private static readonly Dictionary<string, TypeSerializationInfo> s_knownTableKeyType = new Dictionary<string, TypeSerializationInfo>();
|
|
|
|
/// <summary>
|
|
/// Hashtable of knowntypes. Key is ItemTag.
|
|
/// </summary>
|
|
private static readonly Dictionary<string, TypeSerializationInfo> s_knownTableKeyItemTag = new Dictionary<string, TypeSerializationInfo>();
|
|
|
|
#endregion private_fields
|
|
}
|
|
|
|
/// <summary>
|
|
/// This class contains helper routined for serialization/deserialization.
|
|
/// </summary>
|
|
internal static class SerializationUtilities
|
|
{
|
|
/// <summary>
|
|
/// Extracts the value of a note property from a PSObject; returns null if the property does not exist.
|
|
/// </summary>
|
|
internal static object GetPropertyValue(PSObject psObject, string propertyName)
|
|
{
|
|
PSNoteProperty property = (PSNoteProperty)psObject.Properties[propertyName];
|
|
|
|
if (property == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return property.Value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the BaseObject of a note property encoded as a PSObject; returns null if the property does not exist.
|
|
/// </summary>
|
|
internal static object GetPsObjectPropertyBaseObject(PSObject psObject, string propertyName)
|
|
{
|
|
PSObject propertyPsObject = (PSObject)GetPropertyValue(psObject, propertyName);
|
|
|
|
if (propertyPsObject == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return propertyPsObject.BaseObject;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if source is known container type and returns appropriate
|
|
/// information.
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <param name="ct"></param>
|
|
/// <param name="dictionary"></param>
|
|
/// <param name="enumerable"></param>
|
|
internal static void GetKnownContainerTypeInfo(
|
|
object source,
|
|
out ContainerType ct,
|
|
out IDictionary dictionary,
|
|
out IEnumerable enumerable)
|
|
{
|
|
Dbg.Assert(source != null, "caller should validate the parameter");
|
|
|
|
ct = ContainerType.None;
|
|
dictionary = null;
|
|
enumerable = null;
|
|
|
|
dictionary = source as IDictionary;
|
|
if (dictionary != null)
|
|
{
|
|
ct = ContainerType.Dictionary;
|
|
}
|
|
else if (source is Stack)
|
|
{
|
|
ct = ContainerType.Stack;
|
|
enumerable = LanguagePrimitives.GetEnumerable(source);
|
|
Dbg.Assert(enumerable != null, "Stack is enumerable");
|
|
}
|
|
else if (source is Queue)
|
|
{
|
|
ct = ContainerType.Queue;
|
|
enumerable = LanguagePrimitives.GetEnumerable(source);
|
|
Dbg.Assert(enumerable != null, "Queue is enumerable");
|
|
}
|
|
else if (source is IList)
|
|
{
|
|
ct = ContainerType.List;
|
|
enumerable = LanguagePrimitives.GetEnumerable(source);
|
|
Dbg.Assert(enumerable != null, "IList is enumerable");
|
|
}
|
|
else
|
|
{
|
|
Type gt = source.GetType();
|
|
if (gt.IsGenericType)
|
|
{
|
|
if (DerivesFromGenericType(gt, typeof(Stack<>)))
|
|
{
|
|
ct = ContainerType.Stack;
|
|
enumerable = LanguagePrimitives.GetEnumerable(source);
|
|
Dbg.Assert(enumerable != null, "Stack is enumerable");
|
|
}
|
|
else if (DerivesFromGenericType(gt, typeof(Queue<>)))
|
|
{
|
|
ct = ContainerType.Queue;
|
|
enumerable = LanguagePrimitives.GetEnumerable(source);
|
|
Dbg.Assert(enumerable != null, "Queue is enumerable");
|
|
}
|
|
else if (DerivesFromGenericType(gt, typeof(List<>)))
|
|
{
|
|
ct = ContainerType.List;
|
|
enumerable = LanguagePrimitives.GetEnumerable(source);
|
|
Dbg.Assert(enumerable != null, "Queue is enumerable");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if LanguagePrimitive.GetEnumerable can do some magic to get IEnumerable instance
|
|
if (ct == ContainerType.None)
|
|
{
|
|
try
|
|
{
|
|
enumerable = LanguagePrimitives.GetEnumerable(source);
|
|
if (enumerable != null)
|
|
{
|
|
ct = ContainerType.Enumerable;
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
// Catch-all OK. This is a third-party call-out.
|
|
PSEtwLog.LogAnalyticWarning(PSEventId.Serializer_EnumerationFailed, PSOpcode.Exception,
|
|
PSTask.Serialization, PSKeyword.Serializer, source.GetType().AssemblyQualifiedName,
|
|
exception.ToString());
|
|
}
|
|
}
|
|
|
|
// Check if type is IEnumerable
|
|
// (LanguagePrimitives.GetEnumerable above should be enough - the check below is to preserve
|
|
// backcompatibility in some corner-cases (see bugs in Windows7 - #372562 and #372563))
|
|
if (ct == ContainerType.None)
|
|
{
|
|
enumerable = source as IEnumerable;
|
|
if (enumerable != null)
|
|
{
|
|
// WinBlue: 206515 - There are no elements in the source. The source is of type XmlLinkedNode (which derives from XmlNode which implements IEnumerable).
|
|
// So, adding an additional check to see if this contains any elements
|
|
IEnumerator enumerator = enumerable.GetEnumerator();
|
|
if (enumerator != null && enumerator.MoveNext())
|
|
{
|
|
ct = ContainerType.Enumerable;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if derived is of type baseType or a type derived from baseType.
|
|
/// </summary>
|
|
/// <param name="derived"></param>
|
|
/// <param name="baseType"></param>
|
|
/// <returns></returns>
|
|
private static bool DerivesFromGenericType(Type derived, Type baseType)
|
|
{
|
|
Dbg.Assert(derived != null, "caller should validate the parameter");
|
|
Dbg.Assert(baseType != null, "caller should validate the parameter");
|
|
while (derived != null)
|
|
{
|
|
if (derived.IsGenericType)
|
|
derived = derived.GetGenericTypeDefinition();
|
|
|
|
if (derived == baseType)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
derived = derived.BaseType;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the "ToString" from PSObject.
|
|
/// </summary>
|
|
/// <param name="source">
|
|
/// PSObject to be converted to string
|
|
/// </param>
|
|
/// <returns>
|
|
/// "ToString" value
|
|
/// </returns>
|
|
internal static string GetToString(object source)
|
|
{
|
|
Dbg.Assert(source != null, "caller should have validated the information");
|
|
|
|
// fall back value
|
|
string result = null;
|
|
|
|
try
|
|
{
|
|
result = Convert.ToString(source, CultureInfo.InvariantCulture);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
// Catch-all OK. This is a third-party call-out.
|
|
PSEtwLog.LogAnalyticWarning(
|
|
PSEventId.Serializer_ToStringFailed, PSOpcode.Exception, PSTask.Serialization,
|
|
PSKeyword.Serializer | PSKeyword.UseAlwaysAnalytic,
|
|
source.GetType().AssemblyQualifiedName,
|
|
e.ToString());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
internal static string GetToStringForPrimitiveObject(PSObject pso)
|
|
{
|
|
// if object is not wrapped in a PSObject, then nothing modifies the ToString value of the primitive object
|
|
if (pso == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
// preserve ToString throughout deserialization/*re*serialization
|
|
if (pso.ToStringFromDeserialization != null)
|
|
{
|
|
return pso.ToStringFromDeserialization;
|
|
}
|
|
|
|
// preserve token text (i.e. double: 0E1517567410; see Windows 7 bug #694057 for more details)
|
|
string token = pso.TokenText;
|
|
if (token != null)
|
|
{
|
|
string originalToString = GetToString(pso.BaseObject);
|
|
if (originalToString == null || !string.Equals(token, originalToString, StringComparison.Ordinal))
|
|
{
|
|
return token;
|
|
}
|
|
}
|
|
|
|
// no need to write <ToString> element otherwise - the ToString method of a deserialized, live primitive object will return the right value
|
|
return null;
|
|
}
|
|
|
|
internal static PSMemberInfoInternalCollection<PSPropertyInfo> GetSpecificPropertiesToSerialize(PSObject source, Collection<CollectionEntry<PSPropertyInfo>> allPropertiesCollection, TypeTable typeTable)
|
|
{
|
|
if (source == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (source.GetSerializationMethod(typeTable) == SerializationMethod.SpecificProperties)
|
|
{
|
|
PSEtwLog.LogAnalyticVerbose(
|
|
PSEventId.Serializer_ModeOverride, PSOpcode.SerializationSettings, PSTask.Serialization,
|
|
PSKeyword.Serializer | PSKeyword.UseAlwaysAnalytic,
|
|
source.InternalTypeNames.Key,
|
|
(UInt32)(SerializationMethod.SpecificProperties));
|
|
|
|
PSMemberInfoInternalCollection<PSPropertyInfo> specificProperties =
|
|
new PSMemberInfoInternalCollection<PSPropertyInfo>();
|
|
PSMemberInfoIntegratingCollection<PSPropertyInfo> allProperties =
|
|
new PSMemberInfoIntegratingCollection<PSPropertyInfo>(
|
|
source,
|
|
allPropertiesCollection);
|
|
|
|
Collection<string> namesOfPropertiesToSerialize = source.GetSpecificPropertiesToSerialize(typeTable);
|
|
foreach (string propertyName in namesOfPropertiesToSerialize)
|
|
{
|
|
PSPropertyInfo property = allProperties[propertyName];
|
|
if (property == null)
|
|
{
|
|
PSEtwLog.LogAnalyticWarning(
|
|
PSEventId.Serializer_SpecificPropertyMissing, PSOpcode.Exception, PSTask.Serialization,
|
|
PSKeyword.Serializer | PSKeyword.UseAlwaysAnalytic,
|
|
source.InternalTypeNames.Key,
|
|
propertyName);
|
|
}
|
|
else
|
|
{
|
|
specificProperties.Add(property);
|
|
}
|
|
}
|
|
|
|
return specificProperties;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
internal static object GetPropertyValueInThreadSafeManner(PSPropertyInfo property, bool canUseDefaultRunspaceInThreadSafeManner, out bool success)
|
|
{
|
|
Dbg.Assert(property != null, "Caller should validate the parameter");
|
|
|
|
if (!property.IsGettable)
|
|
{
|
|
success = false;
|
|
return null;
|
|
}
|
|
|
|
PSAliasProperty alias = property as PSAliasProperty;
|
|
if (alias != null)
|
|
{
|
|
property = alias.ReferencedMember as PSPropertyInfo;
|
|
}
|
|
|
|
PSScriptProperty script = property as PSScriptProperty;
|
|
Dbg.Assert(script == null || script.GetterScript != null, "scriptProperty.IsGettable => (scriptProperty.GetterScript != null)");
|
|
if ((script != null) && (!canUseDefaultRunspaceInThreadSafeManner))
|
|
{
|
|
PSEtwLog.LogAnalyticWarning(
|
|
PSEventId.Serializer_ScriptPropertyWithoutRunspace, PSOpcode.Exception, PSTask.Serialization,
|
|
PSKeyword.Serializer | PSKeyword.UseAlwaysAnalytic,
|
|
property.Name,
|
|
property.instance == null ? string.Empty : PSObject.GetTypeNames(property.instance).Key,
|
|
script.GetterScript.ToString());
|
|
|
|
success = false;
|
|
return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
object value = property.Value;
|
|
success = true;
|
|
return value;
|
|
}
|
|
catch (ExtendedTypeSystemException e)
|
|
{
|
|
PSEtwLog.LogAnalyticWarning(
|
|
PSEventId.Serializer_PropertyGetterFailed, PSOpcode.Exception, PSTask.Serialization,
|
|
PSKeyword.Serializer | PSKeyword.UseAlwaysAnalytic,
|
|
property.Name,
|
|
property.instance == null ? string.Empty : PSObject.GetTypeNames(property.instance).Key,
|
|
e.ToString(),
|
|
e.InnerException == null ? string.Empty : e.InnerException.ToString());
|
|
|
|
success = false;
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// A dictionary from object to T where
|
|
/// 1) keys are objects,
|
|
/// 2) keys use reference equality,
|
|
/// 3) dictionary keeps only weak references to keys.
|
|
/// </summary>
|
|
/// <typeparam name="T">type of dictionary values</typeparam>
|
|
internal class WeakReferenceDictionary<T> : IDictionary<object, T>
|
|
{
|
|
private sealed class WeakReferenceEqualityComparer : IEqualityComparer<WeakReference>
|
|
{
|
|
public bool Equals(WeakReference x, WeakReference y)
|
|
{
|
|
object tx = x.Target;
|
|
if (tx == null)
|
|
{
|
|
return false; // collected object is not equal to anything (object.ReferenceEquals(null, null) == true)
|
|
}
|
|
|
|
object ty = y.Target;
|
|
if (ty == null)
|
|
{
|
|
return false; // collected object is not equal to anything (object.ReferenceEquals(null, null) == true)
|
|
}
|
|
|
|
return object.ReferenceEquals(tx, ty);
|
|
}
|
|
|
|
public int GetHashCode(WeakReference obj)
|
|
{
|
|
object t = obj.Target;
|
|
if (t == null)
|
|
{
|
|
// collected object doesn't have a hash code
|
|
// return an arbitrary hashcode here and fall back on Equal method for comparison
|
|
return RuntimeHelpers.GetHashCode(obj); // RuntimeHelpers.GetHashCode(null) returns 0 - this would cause many hashtable collisions for WeakReferences to dead objects
|
|
}
|
|
else
|
|
{
|
|
return RuntimeHelpers.GetHashCode(t);
|
|
}
|
|
}
|
|
}
|
|
|
|
private readonly IEqualityComparer<WeakReference> _weakEqualityComparer;
|
|
private Dictionary<WeakReference, T> _dictionary;
|
|
|
|
public WeakReferenceDictionary()
|
|
{
|
|
_weakEqualityComparer = new WeakReferenceEqualityComparer();
|
|
_dictionary = new Dictionary<WeakReference, T>(_weakEqualityComparer);
|
|
}
|
|
|
|
#if DEBUG
|
|
private const int initialCleanupTriggerSize = 2; // 2 will stress this code more
|
|
#else
|
|
private const int initialCleanupTriggerSize = 1000;
|
|
#endif
|
|
private int _cleanupTriggerSize = initialCleanupTriggerSize;
|
|
|
|
private void CleanUp()
|
|
{
|
|
if (this.Count > _cleanupTriggerSize)
|
|
{
|
|
Dictionary<WeakReference, T> alive = new Dictionary<WeakReference, T>(_weakEqualityComparer);
|
|
foreach (KeyValuePair<WeakReference, T> weakKeyValuePair in _dictionary)
|
|
{
|
|
object key = weakKeyValuePair.Key.Target;
|
|
if (key != null)
|
|
{
|
|
alive.Add(weakKeyValuePair.Key, weakKeyValuePair.Value);
|
|
}
|
|
}
|
|
|
|
_dictionary = alive;
|
|
_cleanupTriggerSize = initialCleanupTriggerSize + this.Count * 2;
|
|
}
|
|
}
|
|
|
|
#region IDictionary<object,T> Members
|
|
|
|
public void Add(object key, T value)
|
|
{
|
|
_dictionary.Add(new WeakReference(key), value);
|
|
this.CleanUp();
|
|
}
|
|
|
|
public bool ContainsKey(object key)
|
|
{
|
|
return _dictionary.ContainsKey(new WeakReference(key));
|
|
}
|
|
|
|
public ICollection<object> Keys
|
|
{
|
|
get
|
|
{
|
|
List<object> keys = new List<object>(_dictionary.Keys.Count);
|
|
foreach (WeakReference weakKey in _dictionary.Keys)
|
|
{
|
|
object key = weakKey.Target;
|
|
if (key != null)
|
|
{
|
|
keys.Add(key);
|
|
}
|
|
}
|
|
|
|
return keys;
|
|
}
|
|
}
|
|
|
|
public bool Remove(object key)
|
|
{
|
|
return _dictionary.Remove(new WeakReference(key));
|
|
}
|
|
|
|
public bool TryGetValue(object key, out T value)
|
|
{
|
|
WeakReference weakKey = new WeakReference(key);
|
|
return _dictionary.TryGetValue(weakKey, out value);
|
|
}
|
|
|
|
public ICollection<T> Values
|
|
{
|
|
get
|
|
{
|
|
return _dictionary.Values;
|
|
}
|
|
}
|
|
|
|
public T this[object key]
|
|
{
|
|
get
|
|
{
|
|
return _dictionary[new WeakReference(key)];
|
|
}
|
|
|
|
set
|
|
{
|
|
_dictionary[new WeakReference(key)] = value;
|
|
this.CleanUp();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ICollection<KeyValuePair<object,T>> Members
|
|
|
|
private ICollection<KeyValuePair<WeakReference, T>> WeakCollection
|
|
{
|
|
get
|
|
{
|
|
return _dictionary;
|
|
}
|
|
}
|
|
|
|
private static KeyValuePair<WeakReference, T> WeakKeyValuePair(KeyValuePair<object, T> publicKeyValuePair)
|
|
{
|
|
return new KeyValuePair<WeakReference, T>(new WeakReference(publicKeyValuePair.Key), publicKeyValuePair.Value);
|
|
}
|
|
|
|
public void Add(KeyValuePair<object, T> item)
|
|
{
|
|
this.WeakCollection.Add(WeakKeyValuePair(item));
|
|
this.CleanUp();
|
|
}
|
|
|
|
public void Clear()
|
|
{
|
|
this.WeakCollection.Clear();
|
|
}
|
|
|
|
public bool Contains(KeyValuePair<object, T> item)
|
|
{
|
|
return this.WeakCollection.Contains(WeakKeyValuePair(item));
|
|
}
|
|
|
|
public void CopyTo(KeyValuePair<object, T>[] array, int arrayIndex)
|
|
{
|
|
List<KeyValuePair<object, T>> rawList = new List<KeyValuePair<object, T>>(this.WeakCollection.Count);
|
|
foreach (KeyValuePair<object, T> keyValuePair in this)
|
|
{
|
|
rawList.Add(keyValuePair);
|
|
}
|
|
|
|
rawList.CopyTo(array, arrayIndex);
|
|
}
|
|
|
|
public int Count
|
|
{
|
|
get
|
|
{
|
|
return this.WeakCollection.Count;
|
|
}
|
|
}
|
|
|
|
public bool IsReadOnly
|
|
{
|
|
get
|
|
{
|
|
return this.WeakCollection.IsReadOnly;
|
|
}
|
|
}
|
|
|
|
public bool Remove(KeyValuePair<object, T> item)
|
|
{
|
|
return this.WeakCollection.Remove(WeakKeyValuePair(item));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IEnumerable<KeyValuePair<object,T>> Members
|
|
|
|
public IEnumerator<KeyValuePair<object, T>> GetEnumerator()
|
|
{
|
|
foreach (KeyValuePair<WeakReference, T> weakKeyValuePair in this.WeakCollection)
|
|
{
|
|
object key = weakKeyValuePair.Key.Target;
|
|
if (key != null)
|
|
{
|
|
yield return new KeyValuePair<object, T>(key, weakKeyValuePair.Value);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IEnumerable Members
|
|
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
{
|
|
IEnumerable<KeyValuePair<object, T>> enumerable = this;
|
|
IEnumerator<KeyValuePair<object, T>> enumerator = enumerable.GetEnumerator();
|
|
return enumerator;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
/// <summary>
|
|
/// <see cref="PSPrimitiveDictionary"/> is a <see cref="Hashtable"/> that is limited to
|
|
/// 1) case-insensitive strings as keys and
|
|
/// 2) values that can be serialized and deserialized during PowerShell remoting handshake
|
|
/// (in major-version compatible versions of PowerShell remoting)
|
|
/// </summary>
|
|
[Serializable]
|
|
public sealed class PSPrimitiveDictionary : Hashtable
|
|
{
|
|
#region Constructors
|
|
|
|
/// <summary>
|
|
/// Initializes a new empty instance of the <see cref="PSPrimitiveDictionary"/> class.
|
|
/// </summary>
|
|
public PSPrimitiveDictionary()
|
|
: base(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="PSPrimitiveDictionary"/> class with contents
|
|
/// copied from the <paramref name="other"/> hashtable.
|
|
/// </summary>
|
|
/// <param name="other">Hashtable to copy into the new instance of <see cref="PSPrimitiveDictionary"/></param>
|
|
/// <exception cref="ArgumentException">
|
|
/// This constructor will throw if the <paramref name="other"/> hashtable contains keys that are not a strings
|
|
/// or values that are not one of primitive types that will work during PowerShell remoting handshake.
|
|
/// </exception>
|
|
[SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors", Justification = "The class is sealed")]
|
|
public PSPrimitiveDictionary(Hashtable other)
|
|
: base(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
if (other == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(other));
|
|
}
|
|
|
|
foreach (DictionaryEntry entry in other)
|
|
{
|
|
Hashtable valueAsHashtable = PSObject.Base(entry.Value) as Hashtable;
|
|
if (valueAsHashtable != null)
|
|
{
|
|
this.Add(entry.Key, new PSPrimitiveDictionary(valueAsHashtable));
|
|
}
|
|
else
|
|
{
|
|
this.Add(entry.Key, entry.Value);
|
|
}
|
|
}
|
|
}
|
|
|
|
#if !CORECLR // No .NET Serialization In CoreCLR
|
|
/// <summary>
|
|
/// Support for .NET serialization.
|
|
/// </summary>
|
|
private PSPrimitiveDictionary(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
|
|
: base(info, context)
|
|
{
|
|
}
|
|
#endif
|
|
#endregion
|
|
|
|
#region Plumbing to make Hashtable reject all non-primitive types
|
|
|
|
private static string VerifyKey(object key)
|
|
{
|
|
key = PSObject.Base(key);
|
|
string keyAsString = key as string;
|
|
if (keyAsString == null)
|
|
{
|
|
string message = StringUtil.Format(Serialization.PrimitiveHashtableInvalidKey,
|
|
key.GetType().FullName);
|
|
throw new ArgumentException(message);
|
|
}
|
|
else
|
|
{
|
|
return keyAsString;
|
|
}
|
|
}
|
|
|
|
private static readonly Type[] s_handshakeFriendlyTypes = new Type[] {
|
|
typeof(bool),
|
|
typeof(byte),
|
|
typeof(char),
|
|
typeof(DateTime),
|
|
typeof(decimal),
|
|
typeof(double),
|
|
typeof(Guid),
|
|
typeof(Int32),
|
|
typeof(Int64),
|
|
typeof(sbyte),
|
|
typeof(Single),
|
|
// typeof(ScriptBlock) - don't want ScriptBlocks, because they are deserialized into strings
|
|
typeof(string),
|
|
typeof(TimeSpan),
|
|
typeof(UInt16),
|
|
typeof(UInt32),
|
|
typeof(UInt64),
|
|
typeof(Uri),
|
|
typeof(byte[]),
|
|
typeof(Version),
|
|
typeof(ProgressRecord),
|
|
typeof(XmlDocument),
|
|
|
|
typeof(PSPrimitiveDictionary)
|
|
};
|
|
|
|
private void VerifyValue(object value)
|
|
{
|
|
// null is a primitive type
|
|
if (value == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
value = PSObject.Base(value);
|
|
|
|
// this list is based on the list inside KnownTypes
|
|
// it is copied here to make sure that a list of "handshake friendly types"
|
|
// won't change even if we add new primitive types in v.next
|
|
|
|
Type valueType = value.GetType();
|
|
|
|
// if "value" is a "primitiveType" then we are good
|
|
foreach (Type goodType in s_handshakeFriendlyTypes)
|
|
{
|
|
if (valueType == goodType)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
// if "value" is an array of "primitiveType" items then we are good
|
|
// (note: we could have used IEnumerable<> or ICollection<> (covariance/contravariance concerns),
|
|
// but it is safer to limit the types to arrays.
|
|
// (one concern is that in v.next we might allow overriding SerializationMethod for
|
|
// types [i.e. check SerializationMethod *before* HandleKnownContainerTypes)
|
|
if ((valueType.IsArray) || valueType == typeof(ArrayList))
|
|
{
|
|
foreach (object o in (IEnumerable)value)
|
|
{
|
|
VerifyValue(o);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
string message = StringUtil.Format(Serialization.PrimitiveHashtableInvalidValue,
|
|
value.GetType().FullName);
|
|
throw new ArgumentException(message);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
/// <exception cref="ArgumentException">
|
|
/// This method will throw if the <paramref name="key"/> is not a string and the <paramref name="value"/>
|
|
/// is not one of primitive types that will work during PowerShell remoting handshake.
|
|
/// Use of strongly-typed overloads of this method is suggested if throwing an exception is not acceptable.
|
|
/// </exception>
|
|
public override void Add(object key, object value)
|
|
{
|
|
string keyAsString = VerifyKey(key);
|
|
this.VerifyValue(value);
|
|
base.Add(keyAsString, value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the value associated with the specified key.
|
|
/// </summary>
|
|
/// <param name="key">The key whose value to get or set.</param>
|
|
/// <returns>The value associated with the specified key.</returns>
|
|
/// <remarks>
|
|
/// If the specified key is not found, attempting to get it returns <see langword="null"/>
|
|
/// and attempting to set it creates a new element using the specified key.
|
|
/// </remarks>
|
|
/// <exception cref="ArgumentException">
|
|
/// The setter will throw if the <paramref name="key"/> is not a string and the value
|
|
/// is not one of primitive types that will work during PowerShell remoting handshake.
|
|
/// Use of strongly-typed overloads of Add method is suggested if throwing an exception is not acceptable.
|
|
/// </exception>
|
|
public override object this[object key]
|
|
{
|
|
get
|
|
{
|
|
return base[key];
|
|
}
|
|
|
|
set
|
|
{
|
|
string keyAsString = VerifyKey(key);
|
|
this.VerifyValue(value);
|
|
base[keyAsString] = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the value associated with the specified key.
|
|
/// </summary>
|
|
/// <param name="key">The key whose value to get or set.</param>
|
|
/// <returns>The value associated with the specified key.</returns>
|
|
/// <remarks>
|
|
/// If the specified key is not found, attempting to get it returns <see langword="null"/>
|
|
/// and attempting to set it creates a new element using the specified key.
|
|
/// </remarks>
|
|
/// <exception cref="ArgumentException">
|
|
/// The setter will throw if the value
|
|
/// is not one of primitive types that will work during PowerShell remoting handshake.
|
|
/// Use of strongly-typed overloads of Add method is suggested if throwing an exception is not acceptable.
|
|
/// </exception>
|
|
public object this[string key]
|
|
{
|
|
get
|
|
{
|
|
return base[key];
|
|
}
|
|
|
|
set
|
|
{
|
|
this.VerifyValue(value);
|
|
base[key] = value;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Helper methods
|
|
|
|
/// <summary>
|
|
/// Creates a new instance by doing a shallow copy of the current instance.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public override object Clone()
|
|
{
|
|
return new PSPrimitiveDictionary(this);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, bool value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, bool[] value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, byte value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, byte[] value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, char value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, char[] value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, DateTime value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, DateTime[] value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, Decimal value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, Decimal[] value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, double value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, double[] value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, Guid value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, Guid[] value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, Int32 value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, Int32[] value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, Int64 value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, Int64[] value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, sbyte value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, sbyte[] value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, Single value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, Single[] value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, string value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, string[] value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, TimeSpan value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, TimeSpan[] value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, UInt16 value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, UInt16[] value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, UInt32 value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, UInt32[] value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, UInt64 value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, UInt64[] value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, Uri value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, Uri[] value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, Version value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, Version[] value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, PSPrimitiveDictionary value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an element with the specified key and value into the Hashtable.
|
|
/// </summary>
|
|
/// <param name="key">The key of the element to add.</param>
|
|
/// <param name="value">The value of the element to add.</param>
|
|
public void Add(string key, PSPrimitiveDictionary[] value)
|
|
{
|
|
this.Add((object)key, (object)value);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Internal Methods
|
|
|
|
/// <summary>
|
|
/// If originalHash contains PSVersionTable, then just returns the Cloned copy of
|
|
/// the original hash. Othewise, creates a clone copy and add PSVersionInfo.GetPSVersionTable
|
|
/// to the clone and returns.
|
|
/// </summary>
|
|
/// <param name="originalHash"></param>
|
|
/// <returns></returns>
|
|
internal static PSPrimitiveDictionary CloneAndAddPSVersionTable(PSPrimitiveDictionary originalHash)
|
|
{
|
|
if ((originalHash != null) &&
|
|
(originalHash.ContainsKey(PSVersionInfo.PSVersionTableName)))
|
|
{
|
|
return (PSPrimitiveDictionary)originalHash.Clone();
|
|
}
|
|
|
|
PSPrimitiveDictionary result = originalHash;
|
|
if (originalHash != null)
|
|
{
|
|
result = (PSPrimitiveDictionary)originalHash.Clone();
|
|
}
|
|
else
|
|
{
|
|
result = new PSPrimitiveDictionary();
|
|
}
|
|
|
|
PSPrimitiveDictionary versionTable = new PSPrimitiveDictionary(PSVersionInfo.GetPSVersionTableForDownLevel())
|
|
{
|
|
{"PSSemanticVersion", PSVersionInfo.PSVersion.ToString()}
|
|
};
|
|
result.Add(PSVersionInfo.PSVersionTableName, versionTable);
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tries to get a value that might be present in a chain of nested PSPrimitiveDictionaries.
|
|
/// For example to get $sessionInfo.ApplicationPrivateData.ImplicitRemoting.Hash you could call
|
|
/// TryPathGet<string>($sessionInfo.ApplicationPrivateData, out myHash, "ImplicitRemoting", "Hash").
|
|
/// </summary>
|
|
/// <typeparam name="T">Expected type of the value</typeparam>
|
|
/// <param name="data">The root dictionary.</param>
|
|
/// <param name="result"></param>
|
|
/// <param name="keys">A chain of keys leading from the root dictionary (<paramref name="data"/>) to the value.</param>
|
|
/// <returns><see langword="true"/> if the value was found and was of the correct type; <see langword="false"/> otherwise.</returns>
|
|
internal static bool TryPathGet<T>(IDictionary data, out T result, params string[] keys)
|
|
{
|
|
Dbg.Assert(keys != null, "Caller should verify that keys != null");
|
|
Dbg.Assert(keys.Length >= 1, "Caller should verify that keys.Length >= 1");
|
|
Dbg.Assert(keys[0] != null, "Caller should verify that keys[i] != null");
|
|
|
|
if (data == null || !data.Contains(keys[0]))
|
|
{
|
|
result = default(T);
|
|
return false;
|
|
}
|
|
|
|
if (keys.Length == 1)
|
|
{
|
|
return LanguagePrimitives.TryConvertTo<T>(data[keys[0]], out result);
|
|
}
|
|
else
|
|
{
|
|
IDictionary subData;
|
|
if (LanguagePrimitives.TryConvertTo<IDictionary>(data[keys[0]], out subData)
|
|
&& subData != null)
|
|
{
|
|
string[] subKeys = new string[keys.Length - 1];
|
|
Array.Copy(keys, 1, subKeys, 0, subKeys.Length);
|
|
return TryPathGet<T>(subData, out result, subKeys);
|
|
}
|
|
else
|
|
{
|
|
result = default(T);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|
|
|
|
namespace Microsoft.PowerShell
|
|
{
|
|
using System.Management.Automation;
|
|
using System.Security.Principal;
|
|
|
|
/// <summary>
|
|
/// Rehydrating type converter used during deserialization.
|
|
/// It takes results of serializing some common types
|
|
/// and rehydrates them back from property bags into live objects.
|
|
/// </summary>
|
|
/// <!--
|
|
/// To add a new type for rehydration:
|
|
/// - Add a new T RehydrateT(PSObject pso) method below
|
|
/// - Add this method to converters dictionary in the static constructor below
|
|
/// - If implicit rehydration is required then
|
|
/// - Add appropriate types.ps1 xml entries for
|
|
/// - SerializationDepth=X
|
|
/// - For types depending only on ToString for rehydration set
|
|
/// - SerializationMethod=SpecificProperties
|
|
/// - PropertySerializationSet=<empty>
|
|
/// - TargetTypeForDeserialization=DeserializingTypeConverter
|
|
/// - Add a field of that type in unit tests / S.M.A.Test.SerializationTest+RehydratedType
|
|
/// (testsrc\admintest\monad\DRT\engine\UnitTests\SerializationTest.cs)
|
|
/// -->
|
|
public sealed class DeserializingTypeConverter : PSTypeConverter
|
|
{
|
|
#region Infrastructure
|
|
|
|
private static readonly Dictionary<Type, Func<PSObject, object>> s_converter;
|
|
|
|
static DeserializingTypeConverter()
|
|
{
|
|
s_converter = new Dictionary<Type, Func<PSObject, object>>();
|
|
|
|
s_converter.Add(typeof(PSPrimitiveDictionary), RehydratePrimitiveHashtable);
|
|
|
|
s_converter.Add(typeof(SwitchParameter), RehydrateSwitchParameter);
|
|
s_converter.Add(typeof(PSListModifier), RehydratePSListModifier);
|
|
s_converter.Add(typeof(PSCredential), RehydratePSCredential);
|
|
s_converter.Add(typeof(PSSenderInfo), RehydratePSSenderInfo);
|
|
s_converter.Add(typeof(CultureInfo), RehydrateCultureInfo);
|
|
s_converter.Add(typeof(ParameterSetMetadata), RehydrateParameterSetMetadata);
|
|
|
|
s_converter.Add(typeof(System.Security.Cryptography.X509Certificates.X509Certificate2), RehydrateX509Certificate2);
|
|
s_converter.Add(typeof(System.Security.Cryptography.X509Certificates.X500DistinguishedName), RehydrateX500DistinguishedName);
|
|
s_converter.Add(typeof(System.Net.IPAddress), RehydrateIPAddress);
|
|
|
|
s_converter.Add(typeof(MailAddress), RehydrateMailAddress);
|
|
|
|
s_converter.Add(typeof(System.Security.AccessControl.DirectorySecurity), RehydrateObjectSecurity<System.Security.AccessControl.DirectorySecurity>);
|
|
s_converter.Add(typeof(System.Security.AccessControl.FileSecurity), RehydrateObjectSecurity<System.Security.AccessControl.FileSecurity>);
|
|
s_converter.Add(typeof(System.Security.AccessControl.RegistrySecurity), RehydrateObjectSecurity<System.Security.AccessControl.RegistrySecurity>);
|
|
|
|
s_converter.Add(typeof(ExtendedTypeDefinition), RehydrateExtendedTypeDefinition);
|
|
s_converter.Add(typeof(FormatViewDefinition), RehydrateFormatViewDefinition);
|
|
s_converter.Add(typeof(PSControl), RehydratePSControl);
|
|
s_converter.Add(typeof(PSControlGroupBy), RehydrateGroupBy);
|
|
s_converter.Add(typeof(DisplayEntry), RehydrateDisplayEntry);
|
|
s_converter.Add(typeof(EntrySelectedBy), RehydrateEntrySelectedBy);
|
|
s_converter.Add(typeof(TableControlColumnHeader), RehydrateTableControlColumnHeader);
|
|
s_converter.Add(typeof(TableControlRow), RehydrateTableControlRow);
|
|
s_converter.Add(typeof(TableControlColumn), RehydrateTableControlColumn);
|
|
s_converter.Add(typeof(ListControlEntry), RehydrateListControlEntry);
|
|
s_converter.Add(typeof(ListControlEntryItem), RehydrateListControlEntryItem);
|
|
s_converter.Add(typeof(WideControlEntryItem), RehydrateWideControlEntryItem);
|
|
s_converter.Add(typeof(CustomControlEntry), RehydrateCustomControlEntry);
|
|
s_converter.Add(typeof(CustomItemBase), RehydrateCustomItemBase);
|
|
|
|
s_converter.Add(typeof(CompletionResult), RehydrateCompletionResult);
|
|
s_converter.Add(typeof(ModuleSpecification), RehydrateModuleSpecification);
|
|
s_converter.Add(typeof(CommandCompletion), RehydrateCommandCompletion);
|
|
s_converter.Add(typeof(JobStateInfo), RehydrateJobStateInfo);
|
|
s_converter.Add(typeof(JobStateEventArgs), RehydrateJobStateEventArgs);
|
|
s_converter.Add(typeof(PSSessionOption), RehydratePSSessionOption);
|
|
s_converter.Add(typeof(LineBreakpoint), RehydrateLineBreakpoint);
|
|
s_converter.Add(typeof(CommandBreakpoint), RehydrateCommandBreakpoint);
|
|
s_converter.Add(typeof(VariableBreakpoint), RehydrateVariableBreakpoint);
|
|
s_converter.Add(typeof(BreakpointUpdatedEventArgs), RehydrateBreakpointUpdatedEventArgs);
|
|
s_converter.Add(typeof(DebuggerCommand), RehydrateDebuggerCommand);
|
|
s_converter.Add(typeof(DebuggerCommandResults), RehydrateDebuggerCommandResults);
|
|
s_converter.Add(typeof(DebuggerStopEventArgs), RehydrateDebuggerStopEventArgs);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines if the converter can convert the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter.
|
|
/// </summary>
|
|
/// <param name="sourceValue">The value to convert from.</param>
|
|
/// <param name="destinationType">The type to convert to.</param>
|
|
/// <returns>True if the converter can convert the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter, otherwise false.</returns>
|
|
public override bool CanConvertFrom(PSObject sourceValue, Type destinationType)
|
|
{
|
|
foreach (Type type in s_converter.Keys)
|
|
{
|
|
if (Deserializer.IsDeserializedInstanceOfType(sourceValue, type))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter using formatProvider and ignoreCase.
|
|
/// </summary>
|
|
/// <param name="sourceValue">The value to convert from.</param>
|
|
/// <param name="destinationType">The type to convert to.</param>
|
|
/// <param name="formatProvider">The format provider to use like in IFormattable's ToString.</param>
|
|
/// <param name="ignoreCase">True if case should be ignored.</param>
|
|
/// <returns>The <paramref name="sourceValue"/> parameter converted to the <paramref name="destinationType"/> parameter using formatProvider and ignoreCase.</returns>
|
|
/// <exception cref="InvalidCastException">If no conversion was possible.</exception>
|
|
public override object ConvertFrom(PSObject sourceValue, Type destinationType, IFormatProvider formatProvider, bool ignoreCase)
|
|
{
|
|
if (destinationType == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(destinationType));
|
|
}
|
|
|
|
if (sourceValue == null)
|
|
{
|
|
throw new PSInvalidCastException(
|
|
"InvalidCastWhenRehydratingFromNull",
|
|
PSTraceSource.NewArgumentNullException(nameof(sourceValue)),
|
|
ExtendedTypeSystem.InvalidCastFromNull,
|
|
destinationType.ToString());
|
|
}
|
|
|
|
foreach (KeyValuePair<Type, Func<PSObject, object>> item in s_converter)
|
|
{
|
|
Type type = item.Key;
|
|
Func<PSObject, object> typeConverter = item.Value;
|
|
if (Deserializer.IsDeserializedInstanceOfType(sourceValue, type))
|
|
{
|
|
return ConvertFrom(sourceValue, typeConverter);
|
|
}
|
|
}
|
|
|
|
throw new PSInvalidCastException(
|
|
"InvalidCastEnumFromTypeNotAString",
|
|
null,
|
|
ExtendedTypeSystem.InvalidCastException,
|
|
sourceValue,
|
|
typeof(PSObject),
|
|
destinationType);
|
|
}
|
|
|
|
private static object ConvertFrom(PSObject o, Func<PSObject, object> converter)
|
|
{
|
|
// rehydrate
|
|
PSObject dso = o;
|
|
object rehydratedObject = converter(dso);
|
|
|
|
// re-add instance properties
|
|
// (dso.InstanceMembers includes both instance and *type table* properties;
|
|
// therefore this will also re-add type table properties if they are not present when the deserializer runs;
|
|
// this is ok)
|
|
bool returnPSObject = false;
|
|
PSObject rehydratedPSObject = PSObject.AsPSObject(rehydratedObject);
|
|
foreach (PSMemberInfo member in dso.InstanceMembers)
|
|
{
|
|
if (member.MemberType == (member.MemberType & (PSMemberTypes.Properties | PSMemberTypes.MemberSet | PSMemberTypes.PropertySet)))
|
|
{
|
|
if (rehydratedPSObject.Members[member.Name] == null)
|
|
{
|
|
rehydratedPSObject.InstanceMembers.Add(member);
|
|
returnPSObject = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (returnPSObject)
|
|
{
|
|
return rehydratedPSObject;
|
|
}
|
|
else
|
|
{
|
|
return rehydratedObject;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns true if the converter can convert the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter.
|
|
/// </summary>
|
|
/// <param name="sourceValue">The value to convert from.</param>
|
|
/// <param name="destinationType">The type to convert to.</param>
|
|
/// <returns>True if the converter can convert the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter, otherwise false.</returns>
|
|
public override bool CanConvertTo(object sourceValue, Type destinationType)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter using formatProvider and ignoreCase.
|
|
/// </summary>
|
|
/// <param name="sourceValue">The value to convert from.</param>
|
|
/// <param name="destinationType">The type to convert to.</param>
|
|
/// <param name="formatProvider">The format provider to use like in IFormattable's ToString.</param>
|
|
/// <param name="ignoreCase">True if case should be ignored.</param>
|
|
/// <returns>SourceValue converted to the <paramref name="destinationType"/> parameter using formatProvider and ignoreCase.</returns>
|
|
/// <exception cref="InvalidCastException">If no conversion was possible.</exception>
|
|
public override object ConvertTo(object sourceValue, Type destinationType, IFormatProvider formatProvider, bool ignoreCase)
|
|
{
|
|
throw PSTraceSource.NewNotSupportedException();
|
|
}
|
|
|
|
/// <summary>
|
|
/// This method is not implemented - an overload taking a PSObject is implemented instead.
|
|
/// </summary>
|
|
public override bool CanConvertFrom(object sourceValue, Type destinationType)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
/// <summary>
|
|
/// This method is not implemented - an overload taking a PSObject is implemented instead.
|
|
/// </summary>
|
|
public override bool CanConvertTo(PSObject sourceValue, Type destinationType)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
/// <summary>
|
|
/// This method is not implemented - an overload taking a PSObject is implemented instead.
|
|
/// </summary>
|
|
public override object ConvertFrom(object sourceValue, Type destinationType, IFormatProvider formatProvider, bool ignoreCase)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
/// <summary>
|
|
/// This method is not implemented - an overload taking a PSObject is implemented instead.
|
|
/// </summary>
|
|
public override object ConvertTo(PSObject sourceValue, Type destinationType, IFormatProvider formatProvider, bool ignoreCase)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Rehydration helpers
|
|
|
|
[Flags]
|
|
internal enum RehydrationFlags
|
|
{
|
|
NullValueBad = 0,
|
|
NullValueOk = 0x1,
|
|
NullValueMeansEmptyList = 0x3,
|
|
|
|
MissingPropertyBad = 0,
|
|
MissingPropertyOk = 0x4,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets value of a property (has to be present, value has to be non-null).
|
|
/// Can throw any exception (which is ok - LanguagePrimitives.ConvertTo will catch that).
|
|
/// </summary>
|
|
/// <typeparam name="T">Expected type of the property</typeparam>
|
|
/// <param name="pso">Deserialized object.</param>
|
|
/// <param name="propertyName">Property name.</param>
|
|
/// <returns></returns>
|
|
private static T GetPropertyValue<T>(PSObject pso, string propertyName)
|
|
{
|
|
return GetPropertyValue<T>(pso, propertyName, RehydrationFlags.NullValueBad | RehydrationFlags.MissingPropertyBad);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets value of a property. Can throw any exception (which is ok - LanguagePrimitives.ConvertTo will catch that).
|
|
/// </summary>
|
|
/// <typeparam name="T">Expected type of the property</typeparam>
|
|
/// <param name="pso">Deserialized object.</param>
|
|
/// <param name="propertyName">Property name.</param>
|
|
/// <param name="flags"></param>
|
|
/// <returns></returns>
|
|
internal static T GetPropertyValue<T>(PSObject pso, string propertyName, RehydrationFlags flags)
|
|
{
|
|
Dbg.Assert(pso != null, "Caller should verify pso != null");
|
|
Dbg.Assert(!string.IsNullOrEmpty(propertyName), "Caller should verify propertyName != null");
|
|
|
|
PSPropertyInfo property = pso.Properties[propertyName];
|
|
if ((property == null) && ((flags & RehydrationFlags.MissingPropertyOk) == RehydrationFlags.MissingPropertyOk))
|
|
{
|
|
return default(T);
|
|
}
|
|
else
|
|
{
|
|
object propertyValue = property.Value;
|
|
if ((propertyValue == null) && ((flags & RehydrationFlags.NullValueOk) == RehydrationFlags.NullValueOk))
|
|
{
|
|
return default(T);
|
|
}
|
|
else
|
|
{
|
|
T t = (T)LanguagePrimitives.ConvertTo(propertyValue, typeof(T), CultureInfo.InvariantCulture);
|
|
return t;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static TList RehydrateList<TList, TItem>(PSObject pso, string propertyName, RehydrationFlags flags)
|
|
where TList : IList, new()
|
|
{
|
|
ArrayList deserializedList = GetPropertyValue<ArrayList>(pso, propertyName, flags);
|
|
if (deserializedList == null)
|
|
{
|
|
if ((flags & RehydrationFlags.NullValueMeansEmptyList) == RehydrationFlags.NullValueMeansEmptyList)
|
|
{
|
|
return new TList();
|
|
}
|
|
else
|
|
{
|
|
return default(TList);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TList newList = new TList();
|
|
foreach (object deserializedItem in deserializedList)
|
|
{
|
|
TItem item = (TItem)LanguagePrimitives.ConvertTo(deserializedItem, typeof(TItem), CultureInfo.InvariantCulture);
|
|
newList.Add(item);
|
|
}
|
|
|
|
return newList;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Rehydration of miscellaneous types
|
|
|
|
private static object RehydratePrimitiveHashtable(PSObject pso)
|
|
{
|
|
Hashtable hashtable = (Hashtable)LanguagePrimitives.ConvertTo(pso, typeof(Hashtable), CultureInfo.InvariantCulture);
|
|
return new PSPrimitiveDictionary(hashtable);
|
|
}
|
|
|
|
private static object RehydrateSwitchParameter(PSObject pso)
|
|
{
|
|
return GetPropertyValue<SwitchParameter>(pso, "IsPresent");
|
|
}
|
|
|
|
private static CultureInfo RehydrateCultureInfo(PSObject pso)
|
|
{
|
|
string s = pso.ToString();
|
|
return new CultureInfo(s);
|
|
}
|
|
|
|
private static PSListModifier RehydratePSListModifier(PSObject pso)
|
|
{
|
|
Hashtable h = new Hashtable();
|
|
|
|
PSPropertyInfo addProperty = pso.Properties[PSListModifier.AddKey];
|
|
if ((addProperty != null) && (addProperty.Value != null))
|
|
{
|
|
h.Add(PSListModifier.AddKey, addProperty.Value);
|
|
}
|
|
|
|
PSPropertyInfo removeProperty = pso.Properties[PSListModifier.RemoveKey];
|
|
if ((removeProperty != null) && (removeProperty.Value != null))
|
|
{
|
|
h.Add(PSListModifier.RemoveKey, removeProperty.Value);
|
|
}
|
|
|
|
PSPropertyInfo replaceProperty = pso.Properties[PSListModifier.ReplaceKey];
|
|
if ((replaceProperty != null) && (replaceProperty.Value != null))
|
|
{
|
|
h.Add(PSListModifier.ReplaceKey, replaceProperty.Value);
|
|
}
|
|
|
|
return new PSListModifier(h);
|
|
}
|
|
|
|
private static CompletionResult RehydrateCompletionResult(PSObject pso)
|
|
{
|
|
string completionText = GetPropertyValue<string>(pso, "CompletionText");
|
|
string listItemText = GetPropertyValue<string>(pso, "ListItemText");
|
|
string toolTip = GetPropertyValue<string>(pso, "ToolTip");
|
|
CompletionResultType resultType = GetPropertyValue<CompletionResultType>(pso, "ResultType");
|
|
|
|
return new CompletionResult(completionText, listItemText, resultType, toolTip);
|
|
}
|
|
|
|
private static ModuleSpecification RehydrateModuleSpecification(PSObject pso)
|
|
{
|
|
return new ModuleSpecification
|
|
{
|
|
Name = GetPropertyValue<string>(pso, "Name"),
|
|
Guid = GetPropertyValue<Guid?>(pso, "Guid"),
|
|
Version = GetPropertyValue<Version>(pso, "Version"),
|
|
MaximumVersion = GetPropertyValue<string>(pso, "MaximumVersion"),
|
|
RequiredVersion =
|
|
GetPropertyValue<Version>(pso, "RequiredVersion")
|
|
};
|
|
}
|
|
|
|
private static CommandCompletion RehydrateCommandCompletion(PSObject pso)
|
|
{
|
|
var completions = new Collection<CompletionResult>();
|
|
foreach (var match in GetPropertyValue<ArrayList>(pso, "CompletionMatches"))
|
|
{
|
|
completions.Add((CompletionResult)match);
|
|
}
|
|
|
|
var currentMatchIndex = GetPropertyValue<int>(pso, "CurrentMatchIndex");
|
|
var replacementIndex = GetPropertyValue<int>(pso, "ReplacementIndex");
|
|
var replacementLength = GetPropertyValue<int>(pso, "ReplacementLength");
|
|
return new CommandCompletion(completions, currentMatchIndex, replacementIndex, replacementLength);
|
|
}
|
|
|
|
private static JobStateInfo RehydrateJobStateInfo(PSObject pso)
|
|
{
|
|
var jobState = GetPropertyValue<JobState>(pso, "State");
|
|
|
|
Exception reason = null;
|
|
object propertyValue = null;
|
|
PSPropertyInfo property = pso.Properties["Reason"];
|
|
string message = string.Empty;
|
|
|
|
if (property != null)
|
|
{
|
|
propertyValue = property.Value;
|
|
}
|
|
|
|
if (propertyValue != null)
|
|
{
|
|
if (Deserializer.IsDeserializedInstanceOfType(propertyValue, typeof(Exception)))
|
|
{
|
|
// if we have a deserialized remote or any other exception, use its message to construct
|
|
// an exception
|
|
message = PSObject.AsPSObject(propertyValue).Properties["Message"].Value as string;
|
|
}
|
|
else if (propertyValue is Exception)
|
|
{
|
|
reason = (Exception)propertyValue;
|
|
}
|
|
else
|
|
{
|
|
message = propertyValue.ToString();
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(message))
|
|
{
|
|
try
|
|
{
|
|
reason = (Exception)LanguagePrimitives.ConvertTo(message, typeof(Exception), CultureInfo.InvariantCulture);
|
|
}
|
|
catch (Exception)
|
|
{
|
|
// it is ok to eat this exception since we do not want
|
|
// rehydration to fail
|
|
reason = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
return new JobStateInfo(jobState, reason);
|
|
}
|
|
|
|
internal static JobStateEventArgs RehydrateJobStateEventArgs(PSObject pso)
|
|
{
|
|
var jobStateInfo = RehydrateJobStateInfo(PSObject.AsPSObject(pso.Properties["JobStateInfo"].Value));
|
|
JobStateInfo previousJobStateInfo = null;
|
|
var previousJobStateInfoProperty = pso.Properties["PreviousJobStateInfo"];
|
|
|
|
if (previousJobStateInfoProperty != null && previousJobStateInfoProperty.Value != null)
|
|
{
|
|
previousJobStateInfo = RehydrateJobStateInfo(PSObject.AsPSObject(previousJobStateInfoProperty.Value));
|
|
}
|
|
|
|
return new JobStateEventArgs(jobStateInfo, previousJobStateInfo);
|
|
}
|
|
|
|
internal static PSSessionOption RehydratePSSessionOption(PSObject pso)
|
|
{
|
|
PSSessionOption option = new PSSessionOption();
|
|
|
|
option.ApplicationArguments = GetPropertyValue<PSPrimitiveDictionary>(pso, "ApplicationArguments");
|
|
option.CancelTimeout = GetPropertyValue<TimeSpan>(pso, "CancelTimeout");
|
|
option.Culture = GetPropertyValue<CultureInfo>(pso, "Culture");
|
|
option.IdleTimeout = GetPropertyValue<TimeSpan>(pso, "IdleTimeout");
|
|
option.MaximumConnectionRedirectionCount = GetPropertyValue<int>(pso, "MaximumConnectionRedirectionCount");
|
|
option.MaximumReceivedDataSizePerCommand = GetPropertyValue<int?>(pso, "MaximumReceivedDataSizePerCommand");
|
|
option.MaximumReceivedObjectSize = GetPropertyValue<int?>(pso, "MaximumReceivedObjectSize");
|
|
option.NoCompression = GetPropertyValue<bool>(pso, "NoCompression");
|
|
option.NoEncryption = GetPropertyValue<bool>(pso, "NoEncryption");
|
|
option.NoMachineProfile = GetPropertyValue<bool>(pso, "NoMachineProfile");
|
|
option.OpenTimeout = GetPropertyValue<TimeSpan>(pso, "OpenTimeout");
|
|
option.OperationTimeout = GetPropertyValue<TimeSpan>(pso, "OperationTimeout");
|
|
option.OutputBufferingMode = GetPropertyValue<OutputBufferingMode>(pso, "OutputBufferingMode");
|
|
option.MaxConnectionRetryCount = GetPropertyValue<int>(pso, "MaxConnectionRetryCount");
|
|
option.ProxyAccessType = GetPropertyValue<ProxyAccessType>(pso, "ProxyAccessType");
|
|
option.ProxyAuthentication = GetPropertyValue<AuthenticationMechanism>(pso, "ProxyAuthentication");
|
|
option.ProxyCredential = GetPropertyValue<PSCredential>(pso, "ProxyCredential");
|
|
option.SkipCACheck = GetPropertyValue<bool>(pso, "SkipCACheck");
|
|
option.SkipCNCheck = GetPropertyValue<bool>(pso, "SkipCNCheck");
|
|
option.SkipRevocationCheck = GetPropertyValue<bool>(pso, "SkipRevocationCheck");
|
|
option.UICulture = GetPropertyValue<CultureInfo>(pso, "UICulture");
|
|
option.UseUTF16 = GetPropertyValue<bool>(pso, "UseUTF16");
|
|
option.IncludePortInSPN = GetPropertyValue<bool>(pso, "IncludePortInSPN");
|
|
|
|
return option;
|
|
}
|
|
|
|
internal static LineBreakpoint RehydrateLineBreakpoint(PSObject pso)
|
|
{
|
|
string script = GetPropertyValue<string>(pso, "Script");
|
|
int line = GetPropertyValue<int>(pso, "Line");
|
|
int column = GetPropertyValue<int>(pso, "Column");
|
|
int id = GetPropertyValue<int>(pso, "Id");
|
|
bool enabled = GetPropertyValue<bool>(pso, "Enabled");
|
|
ScriptBlock action = RehydrateScriptBlock(
|
|
GetPropertyValue<string>(pso, "Action", RehydrationFlags.MissingPropertyOk));
|
|
|
|
var bp = new LineBreakpoint(script, line, column, action, id);
|
|
bp.SetEnabled(enabled);
|
|
return bp;
|
|
}
|
|
|
|
internal static CommandBreakpoint RehydrateCommandBreakpoint(PSObject pso)
|
|
{
|
|
string script = GetPropertyValue<string>(pso, "Script", RehydrationFlags.MissingPropertyOk | RehydrationFlags.NullValueOk);
|
|
string command = GetPropertyValue<string>(pso, "Command");
|
|
int id = GetPropertyValue<int>(pso, "Id");
|
|
bool enabled = GetPropertyValue<bool>(pso, "Enabled");
|
|
WildcardPattern pattern = WildcardPattern.Get(command, WildcardOptions.Compiled | WildcardOptions.IgnoreCase);
|
|
ScriptBlock action = RehydrateScriptBlock(
|
|
GetPropertyValue<string>(pso, "Action", RehydrationFlags.MissingPropertyOk));
|
|
|
|
var bp = new CommandBreakpoint(script, pattern, command, action, id);
|
|
bp.SetEnabled(enabled);
|
|
return bp;
|
|
}
|
|
|
|
internal static VariableBreakpoint RehydrateVariableBreakpoint(PSObject pso)
|
|
{
|
|
string script = GetPropertyValue<string>(pso, "Script", RehydrationFlags.MissingPropertyOk | RehydrationFlags.NullValueOk);
|
|
string variableName = GetPropertyValue<string>(pso, "Variable");
|
|
int id = GetPropertyValue<int>(pso, "Id");
|
|
bool enabled = GetPropertyValue<bool>(pso, "Enabled");
|
|
VariableAccessMode access = GetPropertyValue<VariableAccessMode>(pso, "AccessMode");
|
|
ScriptBlock action = RehydrateScriptBlock(
|
|
GetPropertyValue<string>(pso, "Action", RehydrationFlags.MissingPropertyOk));
|
|
|
|
var bp = new VariableBreakpoint(script, variableName, access, action, id);
|
|
bp.SetEnabled(enabled);
|
|
return bp;
|
|
}
|
|
|
|
internal static BreakpointUpdatedEventArgs RehydrateBreakpointUpdatedEventArgs(PSObject pso)
|
|
{
|
|
Breakpoint bp = GetPropertyValue<Breakpoint>(pso, "Breakpoint");
|
|
BreakpointUpdateType bpUpdateType = GetPropertyValue<BreakpointUpdateType>(pso, "UpdateType");
|
|
int bpCount = GetPropertyValue<int>(pso, "BreakpointCount");
|
|
|
|
return new BreakpointUpdatedEventArgs(bp, bpUpdateType, bpCount);
|
|
}
|
|
|
|
internal static DebuggerCommand RehydrateDebuggerCommand(PSObject pso)
|
|
{
|
|
string command = GetPropertyValue<string>(pso, "Command");
|
|
bool repeatOnEnter = GetPropertyValue<bool>(pso, "RepeatOnEnter");
|
|
bool executedByDebugger = GetPropertyValue<bool>(pso, "ExecutedByDebugger");
|
|
DebuggerResumeAction? resumeAction = GetPropertyValue<DebuggerResumeAction?>(pso, "ResumeAction", RehydrationFlags.NullValueOk);
|
|
|
|
return new DebuggerCommand(command, resumeAction, repeatOnEnter, executedByDebugger);
|
|
}
|
|
|
|
internal static DebuggerCommandResults RehydrateDebuggerCommandResults(PSObject pso)
|
|
{
|
|
DebuggerResumeAction? resumeAction = GetPropertyValue<DebuggerResumeAction?>(pso, "ResumeAction", RehydrationFlags.NullValueOk);
|
|
bool evaluatedByDebugger = GetPropertyValue<bool>(pso, "EvaluatedByDebugger");
|
|
|
|
return new DebuggerCommandResults(resumeAction, evaluatedByDebugger);
|
|
}
|
|
|
|
internal static DebuggerStopEventArgs RehydrateDebuggerStopEventArgs(PSObject pso)
|
|
{
|
|
PSObject psoInvocationInfo = GetPropertyValue<PSObject>(pso, "SerializedInvocationInfo", RehydrationFlags.NullValueOk | RehydrationFlags.MissingPropertyOk);
|
|
InvocationInfo invocationInfo = (psoInvocationInfo != null) ? new InvocationInfo(psoInvocationInfo) : null;
|
|
DebuggerResumeAction resumeAction = GetPropertyValue<DebuggerResumeAction>(pso, "ResumeAction");
|
|
Collection<Breakpoint> breakpoints = new Collection<Breakpoint>();
|
|
foreach (var item in GetPropertyValue<ArrayList>(pso, "Breakpoints"))
|
|
{
|
|
Breakpoint bp = item as Breakpoint;
|
|
if (bp != null)
|
|
{
|
|
breakpoints.Add(bp);
|
|
}
|
|
}
|
|
|
|
return new DebuggerStopEventArgs(invocationInfo, breakpoints, resumeAction);
|
|
}
|
|
|
|
private static ScriptBlock RehydrateScriptBlock(string script)
|
|
{
|
|
if (!string.IsNullOrEmpty(script))
|
|
{
|
|
return ScriptBlock.Create(script);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Rehydration of security-related types
|
|
|
|
private static PSCredential RehydratePSCredential(PSObject pso)
|
|
{
|
|
string userName = GetPropertyValue<string>(pso, "UserName");
|
|
System.Security.SecureString password = GetPropertyValue<System.Security.SecureString>(pso, "Password");
|
|
|
|
if (string.IsNullOrEmpty(userName))
|
|
{
|
|
return PSCredential.Empty;
|
|
}
|
|
else
|
|
{
|
|
return new PSCredential(userName, password);
|
|
}
|
|
}
|
|
|
|
internal static PSSenderInfo RehydratePSSenderInfo(PSObject pso)
|
|
{
|
|
PSObject userInfo = GetPropertyValue<PSObject>(pso, "UserInfo");
|
|
PSObject userIdentity = GetPropertyValue<PSObject>(userInfo, "Identity");
|
|
PSObject certDetails = GetPropertyValue<PSObject>(userIdentity, "CertificateDetails");
|
|
|
|
PSCertificateDetails psCertDetails = certDetails == null ? null : new PSCertificateDetails(
|
|
GetPropertyValue<string>(certDetails, "Subject"),
|
|
GetPropertyValue<string>(certDetails, "IssuerName"),
|
|
GetPropertyValue<string>(certDetails, "IssuerThumbprint"));
|
|
PSIdentity psIdentity = new PSIdentity(
|
|
GetPropertyValue<string>(userIdentity, "AuthenticationType"),
|
|
GetPropertyValue<bool>(userIdentity, "IsAuthenticated"),
|
|
GetPropertyValue<string>(userIdentity, "Name"),
|
|
psCertDetails);
|
|
PSPrincipal psPrincipal = new PSPrincipal(psIdentity, WindowsIdentity.GetCurrent());
|
|
|
|
PSSenderInfo senderInfo = new PSSenderInfo(psPrincipal, GetPropertyValue<string>(pso, "ConnectionString"));
|
|
|
|
senderInfo.ClientTimeZone = TimeZoneInfo.Local;
|
|
senderInfo.ApplicationArguments = GetPropertyValue<PSPrimitiveDictionary>(pso, "ApplicationArguments");
|
|
|
|
return senderInfo;
|
|
}
|
|
|
|
private static System.Security.Cryptography.X509Certificates.X509Certificate2 RehydrateX509Certificate2(PSObject pso)
|
|
{
|
|
byte[] rawData = GetPropertyValue<byte[]>(pso, "RawData");
|
|
return new System.Security.Cryptography.X509Certificates.X509Certificate2(rawData);
|
|
}
|
|
|
|
private static System.Security.Cryptography.X509Certificates.X500DistinguishedName RehydrateX500DistinguishedName(PSObject pso)
|
|
{
|
|
byte[] rawData = GetPropertyValue<byte[]>(pso, "RawData");
|
|
return new System.Security.Cryptography.X509Certificates.X500DistinguishedName(rawData);
|
|
}
|
|
|
|
private static System.Net.IPAddress RehydrateIPAddress(PSObject pso)
|
|
{
|
|
string s = pso.ToString();
|
|
return System.Net.IPAddress.Parse(s);
|
|
}
|
|
|
|
private static MailAddress RehydrateMailAddress(PSObject pso)
|
|
{
|
|
string s = pso.ToString();
|
|
return new MailAddress(s);
|
|
}
|
|
|
|
private static T RehydrateObjectSecurity<T>(PSObject pso)
|
|
where T : System.Security.AccessControl.ObjectSecurity, new()
|
|
{
|
|
string sddl = GetPropertyValue<string>(pso, "SDDL");
|
|
|
|
T t = new T();
|
|
t.SetSecurityDescriptorSddlForm(sddl);
|
|
return t;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Rehydration of types needed by implicit remoting
|
|
|
|
/// <summary>
|
|
/// Gets the boolean properties of ParameterSetMetadata object encoded as an integer.
|
|
/// </summary>
|
|
/// <param name="instance">
|
|
/// The PSObject for which to obtain the flags
|
|
/// </param>
|
|
/// <returns>
|
|
/// Boolean properties of ParameterSetMetadata object encoded as an integer
|
|
/// </returns>
|
|
public static UInt32 GetParameterSetMetadataFlags(PSObject instance)
|
|
{
|
|
if (instance == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(instance));
|
|
}
|
|
|
|
if (!(instance.BaseObject is ParameterSetMetadata parameterSetMetadata))
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(instance));
|
|
}
|
|
|
|
return (UInt32)(parameterSetMetadata.Flags);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the full remoting serialized PSObject for the InvocationInfo property
|
|
/// of the DebuggerStopEventArgs type.
|
|
/// </summary>
|
|
/// <param name="instance">InvocationInfo instance.</param>
|
|
/// <returns>PSObject containing serialized InvocationInfo.</returns>
|
|
public static PSObject GetInvocationInfo(PSObject instance)
|
|
{
|
|
if (instance == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(instance));
|
|
}
|
|
|
|
if (!(instance.BaseObject is DebuggerStopEventArgs dbgStopEventArgs))
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(instance));
|
|
}
|
|
|
|
if (dbgStopEventArgs.InvocationInfo == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
PSObject psoInvocationInfo = new PSObject();
|
|
dbgStopEventArgs.InvocationInfo.ToPSObjectForRemoting(psoInvocationInfo);
|
|
return psoInvocationInfo;
|
|
}
|
|
|
|
private static ParameterSetMetadata RehydrateParameterSetMetadata(PSObject pso)
|
|
{
|
|
int position = GetPropertyValue<int>(pso, "Position");
|
|
UInt32 flags = GetPropertyValue<UInt32>(pso, "Flags");
|
|
string helpMessage = GetPropertyValue<string>(pso, "HelpMessage");
|
|
return new ParameterSetMetadata(position, (ParameterSetMetadata.ParameterFlags)flags, helpMessage);
|
|
}
|
|
|
|
private static DisplayEntry RehydrateDisplayEntry(PSObject deserializedDisplayEntry)
|
|
{
|
|
var result = new DisplayEntry
|
|
{
|
|
Value = GetPropertyValue<string>(deserializedDisplayEntry, "Value"),
|
|
ValueType = GetPropertyValue<DisplayEntryValueType>(deserializedDisplayEntry, "ValueType")
|
|
};
|
|
return result;
|
|
}
|
|
|
|
private static EntrySelectedBy RehydrateEntrySelectedBy(PSObject deserializedEsb)
|
|
{
|
|
var result = new EntrySelectedBy
|
|
{
|
|
TypeNames = RehydrateList<List<string>, string>(deserializedEsb, "TypeNames", RehydrationFlags.MissingPropertyOk),
|
|
SelectionCondition = RehydrateList<List<DisplayEntry>, DisplayEntry>(deserializedEsb, "SelectionCondition", RehydrationFlags.MissingPropertyOk)
|
|
};
|
|
return result;
|
|
}
|
|
|
|
private static WideControlEntryItem RehydrateWideControlEntryItem(PSObject deserializedEntryItem)
|
|
{
|
|
var entrySelectedBy = GetPropertyValue<EntrySelectedBy>(deserializedEntryItem, "EntrySelectedBy", RehydrationFlags.MissingPropertyOk);
|
|
if (entrySelectedBy == null)
|
|
{
|
|
var selectedBy = RehydrateList<List<string>, string>(deserializedEntryItem, "SelectedBy", RehydrationFlags.MissingPropertyOk);
|
|
entrySelectedBy = EntrySelectedBy.Get(selectedBy, null);
|
|
}
|
|
|
|
var result = new WideControlEntryItem
|
|
{
|
|
DisplayEntry = GetPropertyValue<DisplayEntry>(deserializedEntryItem, "DisplayEntry"),
|
|
EntrySelectedBy = entrySelectedBy,
|
|
FormatString = GetPropertyValue<string>(deserializedEntryItem, "FormatString", RehydrationFlags.MissingPropertyOk),
|
|
};
|
|
return result;
|
|
}
|
|
|
|
private static ListControlEntryItem RehydrateListControlEntryItem(PSObject deserializedEntryItem)
|
|
{
|
|
var result = new ListControlEntryItem
|
|
{
|
|
DisplayEntry = GetPropertyValue<DisplayEntry>(deserializedEntryItem, "DisplayEntry"),
|
|
ItemSelectionCondition = GetPropertyValue<DisplayEntry>(deserializedEntryItem, "ItemSelectionCondition", RehydrationFlags.MissingPropertyOk),
|
|
FormatString = GetPropertyValue<string>(deserializedEntryItem, "FormatString", RehydrationFlags.MissingPropertyOk),
|
|
Label = GetPropertyValue<string>(deserializedEntryItem, "Label", RehydrationFlags.NullValueOk)
|
|
};
|
|
return result;
|
|
}
|
|
|
|
private static ListControlEntry RehydrateListControlEntry(PSObject deserializedEntry)
|
|
{
|
|
var entrySelectedBy = GetPropertyValue<EntrySelectedBy>(deserializedEntry, "EntrySelectedBy", RehydrationFlags.MissingPropertyOk);
|
|
if (entrySelectedBy == null)
|
|
{
|
|
var selectedBy = RehydrateList<List<string>, string>(deserializedEntry, "SelectedBy", RehydrationFlags.MissingPropertyOk);
|
|
entrySelectedBy = EntrySelectedBy.Get(selectedBy, null);
|
|
}
|
|
|
|
var result = new ListControlEntry
|
|
{
|
|
Items = RehydrateList<List<ListControlEntryItem>, ListControlEntryItem>(deserializedEntry, "Items", RehydrationFlags.NullValueBad),
|
|
EntrySelectedBy = entrySelectedBy
|
|
};
|
|
return result;
|
|
}
|
|
|
|
private static TableControlColumnHeader RehydrateTableControlColumnHeader(PSObject deserializedHeader)
|
|
{
|
|
var result = new TableControlColumnHeader
|
|
{
|
|
Alignment = GetPropertyValue<Alignment>(deserializedHeader, "Alignment"),
|
|
Label = GetPropertyValue<string>(deserializedHeader, "Label", RehydrationFlags.NullValueOk),
|
|
Width = GetPropertyValue<int>(deserializedHeader, "Width")
|
|
};
|
|
return result;
|
|
}
|
|
|
|
private static TableControlColumn RehydrateTableControlColumn(PSObject deserializedColumn)
|
|
{
|
|
var result = new TableControlColumn
|
|
{
|
|
Alignment = GetPropertyValue<Alignment>(deserializedColumn, "Alignment"),
|
|
DisplayEntry = GetPropertyValue<DisplayEntry>(deserializedColumn, "DisplayEntry"),
|
|
FormatString = GetPropertyValue<string>(deserializedColumn, "FormatString", RehydrationFlags.MissingPropertyOk)
|
|
};
|
|
return result;
|
|
}
|
|
|
|
private static TableControlRow RehydrateTableControlRow(PSObject deserializedRow)
|
|
{
|
|
var result = new TableControlRow
|
|
{
|
|
Wrap = GetPropertyValue<bool>(deserializedRow, "Wrap", RehydrationFlags.MissingPropertyOk),
|
|
SelectedBy = GetPropertyValue<EntrySelectedBy>(deserializedRow, "EntrySelectedBy", RehydrationFlags.MissingPropertyOk),
|
|
Columns = RehydrateList<List<TableControlColumn>, TableControlColumn>(deserializedRow, "Columns", RehydrationFlags.NullValueBad)
|
|
};
|
|
return result;
|
|
}
|
|
|
|
private static CustomControlEntry RehydrateCustomControlEntry(PSObject deserializedEntry)
|
|
{
|
|
var result = new CustomControlEntry
|
|
{
|
|
CustomItems = RehydrateList<List<CustomItemBase>, CustomItemBase>(deserializedEntry, "CustomItems", RehydrationFlags.MissingPropertyBad),
|
|
SelectedBy = GetPropertyValue<EntrySelectedBy>(deserializedEntry, "SelectedBy", RehydrationFlags.MissingPropertyOk)
|
|
};
|
|
return result;
|
|
}
|
|
|
|
private static CustomItemBase RehydrateCustomItemBase(PSObject deserializedItem)
|
|
{
|
|
CustomItemBase result;
|
|
|
|
if (Deserializer.IsDeserializedInstanceOfType(deserializedItem, typeof(CustomItemNewline)))
|
|
{
|
|
result = new CustomItemNewline
|
|
{
|
|
Count = GetPropertyValue<int>(deserializedItem, "Count", RehydrationFlags.MissingPropertyBad)
|
|
};
|
|
}
|
|
else if (Deserializer.IsDeserializedInstanceOfType(deserializedItem, typeof(CustomItemText)))
|
|
{
|
|
result = new CustomItemText
|
|
{
|
|
Text = GetPropertyValue<string>(deserializedItem, "Text", RehydrationFlags.MissingPropertyBad)
|
|
};
|
|
}
|
|
else if (Deserializer.IsDeserializedInstanceOfType(deserializedItem, typeof(CustomItemFrame)))
|
|
{
|
|
result = new CustomItemFrame
|
|
{
|
|
FirstLineHanging = GetPropertyValue<uint>(deserializedItem, "FirstLineHanging"),
|
|
FirstLineIndent = GetPropertyValue<uint>(deserializedItem, "FirstLineIndent"),
|
|
RightIndent = GetPropertyValue<uint>(deserializedItem, "RightIndent"),
|
|
LeftIndent = GetPropertyValue<uint>(deserializedItem, "LeftIndent"),
|
|
CustomItems = RehydrateList<List<CustomItemBase>, CustomItemBase>(deserializedItem, "CustomItems", RehydrationFlags.MissingPropertyBad)
|
|
};
|
|
}
|
|
else if (Deserializer.IsDeserializedInstanceOfType(deserializedItem, typeof(CustomItemExpression)))
|
|
{
|
|
result = new CustomItemExpression
|
|
{
|
|
EnumerateCollection = GetPropertyValue<bool>(deserializedItem, "EnumerateCollection"),
|
|
CustomControl = GetPropertyValue<CustomControl>(deserializedItem, "CustomControl", RehydrationFlags.MissingPropertyOk),
|
|
Expression = GetPropertyValue<DisplayEntry>(deserializedItem, "Expression", RehydrationFlags.MissingPropertyOk),
|
|
ItemSelectionCondition = GetPropertyValue<DisplayEntry>(deserializedItem, "ItemSelectionCondition", RehydrationFlags.MissingPropertyOk)
|
|
};
|
|
}
|
|
else
|
|
{
|
|
throw PSTraceSource.NewArgumentException(nameof(deserializedItem));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private static PSControl RehydratePSControl(PSObject deserializedControl)
|
|
{
|
|
// Earlier versions of PowerShell did not have all of the possible properties in a control, so we must
|
|
// use MissingPropertyOk to allow for connections to those older endpoints.
|
|
PSControl result;
|
|
if (Deserializer.IsDeserializedInstanceOfType(deserializedControl, typeof(TableControl)))
|
|
{
|
|
var tableControl = new TableControl
|
|
{
|
|
AutoSize = GetPropertyValue<bool>(deserializedControl, "AutoSize", RehydrationFlags.MissingPropertyOk),
|
|
HideTableHeaders = GetPropertyValue<bool>(deserializedControl, "HideTableHeaders", RehydrationFlags.MissingPropertyOk),
|
|
Headers = RehydrateList<List<TableControlColumnHeader>, TableControlColumnHeader>(deserializedControl, "Headers", RehydrationFlags.NullValueBad),
|
|
Rows = RehydrateList<List<TableControlRow>, TableControlRow>(deserializedControl, "Rows", RehydrationFlags.NullValueBad)
|
|
};
|
|
result = tableControl;
|
|
}
|
|
else if (Deserializer.IsDeserializedInstanceOfType(deserializedControl, typeof(ListControl)))
|
|
{
|
|
var listControl = new ListControl
|
|
{
|
|
Entries = RehydrateList<List<ListControlEntry>, ListControlEntry>(deserializedControl, "Entries", RehydrationFlags.NullValueBad)
|
|
};
|
|
result = listControl;
|
|
}
|
|
else if (Deserializer.IsDeserializedInstanceOfType(deserializedControl, typeof(WideControl)))
|
|
{
|
|
var wideControl = new WideControl
|
|
{
|
|
AutoSize = GetPropertyValue<bool>(deserializedControl, "Alignment", RehydrationFlags.MissingPropertyOk),
|
|
Columns = GetPropertyValue<uint>(deserializedControl, "Columns"),
|
|
Entries = RehydrateList<List<WideControlEntryItem>, WideControlEntryItem>(deserializedControl, "Entries", RehydrationFlags.NullValueBad)
|
|
};
|
|
result = wideControl;
|
|
}
|
|
else if (Deserializer.IsDeserializedInstanceOfType(deserializedControl, typeof(CustomControl)))
|
|
{
|
|
var customControl = new CustomControl
|
|
{
|
|
Entries = RehydrateList<List<CustomControlEntry>, CustomControlEntry>(deserializedControl, "Entries", RehydrationFlags.NullValueBad)
|
|
};
|
|
result = customControl;
|
|
}
|
|
else
|
|
{
|
|
throw PSTraceSource.NewArgumentException("pso");
|
|
}
|
|
|
|
result.GroupBy = GetPropertyValue<PSControlGroupBy>(deserializedControl, "GroupBy", RehydrationFlags.MissingPropertyOk);
|
|
result.OutOfBand = GetPropertyValue<bool>(deserializedControl, "OutOfBand", RehydrationFlags.MissingPropertyOk);
|
|
return result;
|
|
}
|
|
|
|
private static PSControlGroupBy RehydrateGroupBy(PSObject deserializedGroupBy)
|
|
{
|
|
var result = new PSControlGroupBy
|
|
{
|
|
CustomControl = GetPropertyValue<CustomControl>(deserializedGroupBy, "CustomControl", RehydrationFlags.MissingPropertyOk),
|
|
Expression = GetPropertyValue<DisplayEntry>(deserializedGroupBy, "Expression", RehydrationFlags.MissingPropertyOk),
|
|
Label = GetPropertyValue<string>(deserializedGroupBy, "Label", RehydrationFlags.NullValueOk)
|
|
};
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the boolean properties of ParameterSetMetadata object encoded as an integer.
|
|
/// </summary>
|
|
/// <param name="instance">
|
|
/// The PSObject for which to obtain the flags
|
|
/// </param>
|
|
/// <returns>
|
|
/// Boolean properties of ParameterSetMetadata object encoded as an integer
|
|
/// </returns>
|
|
public static Guid GetFormatViewDefinitionInstanceId(PSObject instance)
|
|
{
|
|
if (instance == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(instance));
|
|
}
|
|
|
|
if (!(instance.BaseObject is FormatViewDefinition formatViewDefinition))
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(instance));
|
|
}
|
|
|
|
return formatViewDefinition.InstanceId;
|
|
}
|
|
|
|
private static FormatViewDefinition RehydrateFormatViewDefinition(PSObject deserializedViewDefinition)
|
|
{
|
|
string name = GetPropertyValue<string>(deserializedViewDefinition, "Name");
|
|
Guid instanceId = GetPropertyValue<Guid>(deserializedViewDefinition, "InstanceId");
|
|
PSControl control = GetPropertyValue<PSControl>(deserializedViewDefinition, "Control");
|
|
|
|
return new FormatViewDefinition(name, control, instanceId);
|
|
}
|
|
|
|
private static ExtendedTypeDefinition RehydrateExtendedTypeDefinition(PSObject deserializedTypeDefinition)
|
|
{
|
|
// Prefer TypeNames to TypeName - as it was incorrect to create multiple ExtendedTypeDefinitions for a group of types.
|
|
// But if a new PowerShell connects to an old endpoint, TypeNames will be missing, so fall back to TypeName in that case.
|
|
string typeName;
|
|
var typeNames = RehydrateList<List<string>, string>(deserializedTypeDefinition, "TypeNames", RehydrationFlags.MissingPropertyOk);
|
|
if (typeNames == null || typeNames.Count == 0)
|
|
{
|
|
typeName = GetPropertyValue<string>(deserializedTypeDefinition, "TypeName");
|
|
}
|
|
else
|
|
{
|
|
typeName = typeNames[0];
|
|
}
|
|
|
|
List<FormatViewDefinition> viewDefinitions = RehydrateList<List<FormatViewDefinition>, FormatViewDefinition>(
|
|
deserializedTypeDefinition,
|
|
"FormatViewDefinition",
|
|
RehydrationFlags.NullValueBad);
|
|
|
|
var result = new ExtendedTypeDefinition(typeName, viewDefinitions);
|
|
if (typeNames != null && typeNames.Count > 1)
|
|
{
|
|
for (var i = 1; i < typeNames.Count; i++)
|
|
{
|
|
result.TypeNames.Add(typeNames[i]);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|