/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Management.Automation.Internal;
using System.Management.Automation.Runspaces;
using System.Management.Automation.Tracing;
using Microsoft.Management.Infrastructure;
using Dbg = System.Management.Automation.Diagnostics;
namespace System.Management.Automation
{
///
/// This internal helper class provides methods for serializing mshObject.
///
internal class InternalMISerializer
{
//TODO, insivara : Is this the correct namespace
private const string PowerShellRemotingProviderNamespace = "root/Microsoft/Windows/Powershellv4";
#region Constructor
//TODO, insivara : Depth implementation will come in a later checkin
private int _depth;
internal InternalMISerializer(int depth)
{
_depth = depth;
// TODO, insivara : Revisit this
_typeTable = new TypeTable();
}
#endregion Constructor
#region Properties
internal CimInstance CimInstance { get; set; }
/// Used by Remoting infrastructure. This TypeTable instance
/// will be used by Serializer if ExecutionContext is not
/// avaliable (to get the ExecutionContext's TypeTable)
private TypeTable _typeTable;
private Collection> _allPropertiesCollection;
private Collection> AllPropertiesCollection
{
get {
return _allPropertiesCollection ??
(_allPropertiesCollection = PSObject.GetPropertyCollection(PSMemberViewTypes.All, _typeTable));
}
}
// Similar to the property by the same name in serialization.cs
private bool? _canUseDefaultRunspaceInThreadSafeManner;
private bool CanUseDefaultRunspaceInThreadSafeManner
{
get
{
// can use default runspace in a thread safe manner only if
// 1. we have a default runspace
// 2. we recognize the type of current runspace and current pipeline
// 3. the pipeline executes on the same thread as this method
// we don't return "true" for
// 1. we have a default runspace
// 2. no currently executing pipeline
// to avoid a race condition where a pipeline is started
// after this property getter did all the checks
if (!_canUseDefaultRunspaceInThreadSafeManner.HasValue)
{
_canUseDefaultRunspaceInThreadSafeManner = false;
RunspaceBase runspace = Runspace.DefaultRunspace as RunspaceBase;
if (runspace != null)
{
Pipeline currentPipeline = runspace.GetCurrentlyRunningPipeline();
LocalPipeline localPipeline = currentPipeline as LocalPipeline;
if ((localPipeline != null) && (localPipeline.NestedPipelineExecutionThread != null))
{
_canUseDefaultRunspaceInThreadSafeManner =
(localPipeline.NestedPipelineExecutionThread.ManagedThreadId
== Threading.Thread.CurrentThread.ManagedThreadId);
}
}
}
return _canUseDefaultRunspaceInThreadSafeManner.Value;
}
}
#endregion Properties
internal CimInstance Serialize(object o)
{
return CreateCimInstanceForOneTopLevelObject(o);
}
///
/// This writes one object.
///
///
/// source to be serialized.
///
internal CimInstance CreateCimInstanceForOneTopLevelObject
(
object source
)
{
CimInstance result;
CreateCimInstanceForOneObject(source, null, _depth, out result);
return result;
}
private void CreateCimInstanceForOneObject
(
object source,
string property,
int depth,
out CimInstance result
)
{
// To avoid compiler error
result = CreateNullCimInstance();
Dbg.Assert(depth >= 0, "depth should always be greater or equal to zero");
if (source == null)
{
//TODO, insivara : TO BE IMPLEMENTED
//TODO, insivara : Return Null CimInstance
return;
}
if (HandlePrimitiveKnownTypeByConvertingToPSObject(source, property, depth, out result))
{
return;
}
if (HandleKnownContainerTypes(source, property, depth, out result))
{
return;
}
HandleComplexTypePSObject(source, property, depth, out result);
//TODO, insivara : Depth implementation
return;
}
///
/// Handles primitive known type by first converting it to a PSObject.
///
///
///
///
///
///
private bool HandlePrimitiveKnownTypeByConvertingToPSObject(
object source,
string property,
int depth,
out CimInstance result
)
{
// To avoid compiler error
result = CreateNullCimInstance();
Dbg.Assert(source != null, "caller should validate the parameter");
//Check if source is of primitive known type
MITypeSerializationInfo pktInfo = KnownMITypes.GetTypeSerializationInfo(source.GetType());
if (pktInfo != null)
{
PSObject pktInfoPSObject = PSObject.AsPSObject(source);
return HandlePrimitiveKnownTypePSObject(pktInfoPSObject, property, depth, out result);
}
return false;
}
///
/// Serializes PSObject whose base objects are of primitive known type
///
///
///
///
///
///
/// true if source is handled, else false.
///
private bool HandlePrimitiveKnownTypePSObject
(
object source,
string property,
int depth,
out CimInstance result
)
{
// To avoid compiler error
result = CreateNullCimInstance();
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;
MITypeSerializationInfo pktInfo = KnownMITypes.GetTypeSerializationInfo(baseObject.GetType());
if (pktInfo != null)
{
CreateCimInstanceForPrimitiveTypePSObject(moSource, baseObject, pktInfo, property, depth, out result);
sourceHandled = true;
}
}
return sourceHandled;
}
private bool HandleKnownContainerTypes
(
object source,
string property,
int depth,
out CimInstance result
)
{
Dbg.Assert(source != null, "caller should validate the parameter");
result = CreateNullCimInstance();
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;
result = CreateCimInstanceForPSObject(cimClassName: "PS_Object",
psObj: mshSource ?? PSObject.AsPSObject(source),
writeToString: false);
List listOfCimInstances = null;
switch (ct)
{
case ContainerType.Dictionary:
WriteDictionary(dictionary, depth, out listOfCimInstances);
break;
case ContainerType.Stack:
WriteEnumerable(enumerable, property, depth, out listOfCimInstances);
break;
case ContainerType.Queue:
WriteEnumerable(enumerable, property, depth, out listOfCimInstances);
break;
case ContainerType.List:
WriteEnumerable(enumerable, property, depth, out listOfCimInstances);
break;
case ContainerType.Enumerable:
WriteEnumerable(enumerable, property, depth, out listOfCimInstances);
break;
default:
Dbg.Assert(false, "All containers should be handled in the switch");
break;
}
CimInstance[] instanceArray = listOfCimInstances.ToArray();
CimProperty valueProperty = CimProperty.Create("Value", instanceArray, Microsoft.Management.Infrastructure.CimType.InstanceArray, CimFlags.Property);
result.CimInstanceProperties.Add(valueProperty);
return true;
}
private void HandleComplexTypePSObject
(
object source,
string property,
int depth,
out CimInstance result
)
{
List listOfCimInstancesProperties = null;
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;
//TODO, insivara : To be implemented
//bool isCimInstance = false;
if (!mshSource.immediateBaseObjectIsEmpty)
{
ErrorRecord errorRecord = mshSource.ImmediateBaseObject as ErrorRecord;
if (errorRecord == null)
{
InformationalRecord informationalRecord = mshSource.ImmediateBaseObject as InformationalRecord;
if (informationalRecord == null)
{
isEnum = mshSource.ImmediateBaseObject is Enum;
isPSObject = mshSource.ImmediateBaseObject is PSObject;
}
else
{
informationalRecord.ToPSObjectForRemoting(mshSource);
isInformationalRecord = true;
}
}
else
{
errorRecord.ToPSObjectForRemoting(mshSource);
isErrorRecord = true;
}
}
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;
}
}
// This will create a CimInstance for PS_Object and populate the typenames.
result = CreateCimInstanceForPSObject(cimClassName: "PS_Object",
psObj: mshSource,
writeToString: writeToString);
PSMemberInfoInternalCollection specificPropertiesToSerialize =
SerializationUtilities.GetSpecificPropertiesToSerialize(mshSource, AllPropertiesCollection, _typeTable);
if (isEnum)
{
CimInstance enumCimInstance = CreateCimInstanceForEnum(mshSource, depth, property != null);
CimProperty p = CimProperty.Create("Value", enumCimInstance,
Microsoft.Management.Infrastructure.CimType.Reference,
Microsoft.Management.Infrastructure.CimFlags.Property);
result.CimInstanceProperties.Add(p);
}
else if (isPSObject)
{
CimInstance psObjectCimInstance;
CreateCimInstanceForOneObject(mshSource.ImmediateBaseObject, property, depth, out psObjectCimInstance);
CimProperty valueProperty = CimProperty.Create("Value", psObjectCimInstance,
Microsoft.Management.Infrastructure.CimType.Reference,
Microsoft.Management.Infrastructure.CimFlags.Property);
result.CimInstanceProperties.Add(valueProperty);
}
else if (isErrorRecord || isInformationalRecord)
{
// nothing to do
}
else
{
CreateCimInstanceForPSObjectProperties(mshSource, depth, specificPropertiesToSerialize, out listOfCimInstancesProperties);
}
//TODO, insivara : Implement serialization of CimInstance
//if (isCimInstance)
//{
// CimInstance cimInstance = mshSource.ImmediateBaseObject as CimInstance;
// PrepareCimInstanceForSerialization(mshSource, cimInstance);
//}
//TODO, insivara : ExtendedProperties implementation will be done in a subsequent checkin
//SerializeExtendedProperties(mshSource, depth, specificPropertiesToSerialize, out listOfCimInstancesExtendedProperties);
if (listOfCimInstancesProperties != null && listOfCimInstancesProperties.Count > 0)
{
CimInstance[] referenceArray = listOfCimInstancesProperties.ToArray();
CimProperty properties = CimProperty.Create("Properties", referenceArray,
Microsoft.Management.Infrastructure.CimType.ReferenceArray,
Microsoft.Management.Infrastructure.CimFlags.Property);
result.CimInstanceProperties.Add(properties);
}
}
///
/// Serializes an PSObject whose baseobject is of primitive type.
///
///
/// source from which notes are written
///
///
/// primitive object which is written as base object. In most cases it
/// is same source.ImmediateBaseObject. When PSObject is serialized as string,
///
///
/// TypeSerializationInfo for the primitive.
///
///
///
///
private void CreateCimInstanceForPrimitiveTypePSObject
(
PSObject source,
object primitive,
MITypeSerializationInfo pktInfo,
string property,
int depth,
out CimInstance result
)
{
// To avoid compiler error
result = CreateNullCimInstance();
Dbg.Assert(source != null, "Caller should validate source != null");
string toStringValue = SerializationUtilities.GetToStringForPrimitiveObject(source);
bool hasModifiedTypesCollection = false;
//hasModifiedTypesCollection = PSObjectHasModifiedTypesCollection(source);
bool hasNotes = false;
//hasNotes = PSObjectHasNotes(source);
bool hasModifiedToString = (toStringValue != null);
if (hasNotes || hasModifiedTypesCollection || hasModifiedToString)
{
//TODO, insivara : TO BE IMPLEMENTED
//WritePrimitiveTypePSObjectWithNotes(
// source,
// primitive,
// hasModifiedTypesCollection,
// toStringValue,
// pktInfo,
// streamName,
// property,
// depth);
//return;
}
else
{
if (primitive != null)
{
CreateCimInstanceForOnePrimitiveKnownType(this, property, primitive, pktInfo, out result);
return;
}
else
{
//TODO, insivara : Return Null CimInstance
return;
}
}
}
///
/// Writes an item or property in Monad namespace
///
/// The serializer to which the object is serialized.
/// name of property. Pass null for item
/// object to be written
/// serialization information about source
///
private static void CreateCimInstanceForOnePrimitiveKnownType
(
InternalMISerializer serializer,
string property,
object source,
MITypeSerializationInfo entry,
out CimInstance result
)
{
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 != null && 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");
result = CreateRawStringCimInstance(property, value, entry);
}
else
{
result = entry.Serializer(property, source, entry);
}
}
#region membersets
#endregion membersets
#region properties
#endregion properties
# region enumerable and dictionary
///
/// Serializes IEnumerable
///
///
/// enumerable which is serialized
///
///
///
///
///
private void WriteEnumerable
(
IEnumerable enumerable,
string property,
int depth,
out List enumerableInstances
)
{
enumerableInstances = new List();
Dbg.Assert(enumerable != null, "caller should validate the parameter");
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.
CommandProcessorBase.CheckForSevereException(exception);
PSEtwLog.LogAnalyticWarning(
PSEventId.Serializer_EnumerationFailed, PSOpcode.Exception, PSTask.Serialization,
PSKeyword.Serializer | PSKeyword.UseAlwaysAnalytic,
enumerable.GetType().AssemblyQualifiedName,
exception.ToString());
enumerator = null;
}
if (enumerator != null)
{
while (true)
{
object item = null;
try
{
if (!enumerator.MoveNext())
{
break;
}
item = enumerator.Current;
}
catch (Exception exception)
{
// Catch-all OK. This is a third-party call-out.
CommandProcessorBase.CheckForSevereException(exception);
PSEtwLog.LogAnalyticWarning(
PSEventId.Serializer_EnumerationFailed, PSOpcode.Exception, PSTask.Serialization,
PSKeyword.Serializer | PSKeyword.UseAlwaysAnalytic,
enumerable.GetType().AssemblyQualifiedName,
exception.ToString());
break;
}
CimInstance enumerableInstance;
CreateCimInstanceForOneObject(item, property, depth, out enumerableInstance);
enumerableInstances.Add(enumerableInstance);
}
}
}
private void WriteDictionary(IDictionary dictionary, int depth, out List listOfCimInstances)
{
listOfCimInstances = new List();
Dbg.Assert(dictionary != null, "caller should validate the parameter");
IDictionaryEnumerator dictionaryEnum = null;
try
{
dictionaryEnum = dictionary.GetEnumerator();
}
catch (Exception exception) // ignore non-severe exceptions
{
// Catch-all OK. This is a third-party call-out.
CommandProcessorBase.CheckForSevereException(exception);
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.
CommandProcessorBase.CheckForSevereException(exception);
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;
CimInstance dictionaryEntryInstance = CreateCimInstanceForDictionaryEntry(key, value, depth);
listOfCimInstances.Add(dictionaryEntryInstance);
}
}
}
private CimInstance CreateCimInstanceForDictionaryEntry(object key, object value, int depth)
{
//TODO, insivara: Update class name here
CimInstance dictionaryEntryCimInstance = CreateCimInstance("PSObject_DictionaryEntry");
CimInstance keyCimInstance, valueCimInstance;
CreateCimInstanceForOneObject(key, null, depth, out keyCimInstance);
CreateCimInstanceForOneObject(value, null, depth, out valueCimInstance);
CimProperty keyProperty = CimProperty.Create("Key", keyCimInstance,
Microsoft.Management.Infrastructure.CimType.Instance,
Microsoft.Management.Infrastructure.CimFlags.Property);
CimProperty valueProperty = CimProperty.Create("Value", valueCimInstance,
Microsoft.Management.Infrastructure.CimType.Instance,
Microsoft.Management.Infrastructure.CimFlags.Property);
dictionaryEntryCimInstance.CimInstanceProperties.Add(keyProperty);
dictionaryEntryCimInstance.CimInstanceProperties.Add(valueProperty);
return dictionaryEntryCimInstance;
}
#endregion enumerable and dictionary
# region Serialization Delegates
///
/// Creates CimInstance for a primitive type
///
/// name of property. pass null for item
/// value
/// serialization information about source
internal static CimInstance CreateCimInstanceForPrimitiveType(string property, object source, MITypeSerializationInfo entry)
{
CimInstance c;
if (property != null)
{
c = CreateCimInstanceWhenPropertyNameExists(property, source, entry);
}
else
{
c = CreateCimInstance(entry.CimClassName);
CimProperty valueProperty = CimProperty.Create("Value", source, entry.CimType, CimFlags.Property);
c.CimInstanceProperties.Add(valueProperty);
}
return c;
}
///
/// Creates CimInstance for a string
///
/// name of property. pass null for item
/// string value to write
/// serialization information about source
internal static CimInstance CreateCimInstanceForString(string property, object source, MITypeSerializationInfo entry)
{
CimInstance c;
String value = InternalSerializer.EncodeString((String)source);
if (property != null)
{
c = CreateCimInstanceWhenPropertyNameExists(property, value, entry);
}
else
{
c = CreateCimInstance(entry.CimClassName);
CimProperty valueProperty = CimProperty.Create("Value", value, entry.CimType, CimFlags.Property);
c.CimInstanceProperties.Add(valueProperty);
}
return c;
}
#endregion
#region Serialization Helper Methods
private static CimProperty CreateTypeNamesProperty(IEnumerable types)
{
return CimProperty.Create("TypeNames", types, Microsoft.Management.Infrastructure.CimType.StringArray, CimFlags.Property);
}
internal static CimProperty CreateCimProperty(string propertyName, object propertyValue, Microsoft.Management.Infrastructure.CimType cimType)
{
return CimProperty.Create(propertyName, propertyValue, cimType, CimFlags.Property);
}
internal static CimInstance CreateCimInstance(string cimClassName)
{
CimInstance cimInstance = new CimInstance(cimClassName, PowerShellRemotingProviderNamespace);
return cimInstance;
}
internal static CimInstance CreateCimInstanceForPSObject(string cimClassName, PSObject psObj, bool writeToString)
{
CimInstance cimInstance = new CimInstance(cimClassName, PowerShellRemotingProviderNamespace);
CimProperty typeProperty = CreateTypeNamesProperty(psObj.TypeNames);
cimInstance.CimInstanceProperties.Add(typeProperty);
if (writeToString)
{
CimProperty toStringProperty = CimProperty.Create("ToString",
SerializationUtilities.GetToString(psObj),
Microsoft.Management.Infrastructure.CimType.String,
Microsoft.Management.Infrastructure.CimFlags.Property);
cimInstance.CimInstanceProperties.Add(toStringProperty);
}
return cimInstance;
}
private static CimInstance CreateRawStringCimInstance(string property, string value, MITypeSerializationInfo entry)
{
CimInstance c;
if (property != null)
{
c = CreateCimInstanceWhenPropertyNameExists(property, value, entry);
}
else
{
c = CreateCimInstance(entry.CimClassName);
CimProperty p1 = CimProperty.Create("Value", value, entry.CimType, CimFlags.Property);
c.CimInstanceProperties.Add(p1);
}
return c;
}
private static CimInstance CreateCimInstanceWhenPropertyNameExists(string property, object source, MITypeSerializationInfo entry)
{
CimInstance innerInstance = CreateCimInstance(entry.CimClassName);
CimProperty valueProperty = CimProperty.Create("Value", source, entry.CimType, CimFlags.Property);
innerInstance.CimInstanceProperties.Add(valueProperty);
CimInstance c = CreateCimInstance("PS_ObjectProperty");
CimProperty name = CimProperty.Create("Name", property,
Microsoft.Management.Infrastructure.CimType.String,
CimFlags.Property);
c.CimInstanceProperties.Add(name);
CimProperty outerInstanceValueProperty = CimProperty.Create("Value", innerInstance,
Microsoft.Management.Infrastructure.CimType.Reference,
CimFlags.Property);
c.CimInstanceProperties.Add(outerInstanceValueProperty);
return c;
}
private CimInstance CreateCimInstanceForEnum(PSObject mshSource, int depth, bool serializeAsString)
{
CimInstance enumCimInstance = null;
object baseObject = mshSource.ImmediateBaseObject;
CreateCimInstanceForOneObject(source: System.Convert.ChangeType(baseObject, Enum.GetUnderlyingType(baseObject.GetType()), System.Globalization.CultureInfo.InvariantCulture),
property: null,
depth: depth,
result: out enumCimInstance);
return enumCimInstance;
}
///
/// Serializes properties of PSObject
///
private void CreateCimInstanceForPSObjectProperties
(
PSObject source,
int depth,
IEnumerable specificPropertiesToSerialize,
out List listOfCimInstances
)
{
listOfCimInstances = new List();
Dbg.Assert(source != null, "caller should validate the information");
//TODO, insivara : Depth implementation will come later
if (specificPropertiesToSerialize != null)
{
SerializeProperties(specificPropertiesToSerialize, depth, out listOfCimInstances);
}
else
{
if (source.ShouldSerializeAdapter())
{
IEnumerable adapterCollection = null;
adapterCollection = source.GetAdaptedProperties();
if (adapterCollection != null)
{
SerializeProperties(adapterCollection, depth, out listOfCimInstances);
}
}
}
}
///
/// Serializes properties from collection
///
///
/// Collection of properties to serialize
///
///
/// depth to which each property should be
/// serialized
///
///
/// list of CimInstances for the properties
/// serialized
///
private void SerializeProperties
(
IEnumerable propertyCollection,
int depth,
out List listOfCimInstances
)
{
listOfCimInstances = new List();
Dbg.Assert(propertyCollection != null, "caller should validate the parameter");
foreach (PSMemberInfo info in propertyCollection)
{
PSProperty prop = info as PSProperty;
if (prop == null)
{
continue;
}
bool success;
object value = SerializationUtilities.GetPropertyValueInThreadSafeManner(prop, this.CanUseDefaultRunspaceInThreadSafeManner, out success);
if (success)
{
CimInstance propertyInstance = null;
CreateCimInstanceForOneObject(value, prop.Name, depth, out propertyInstance);
listOfCimInstances.Add(propertyInstance);
}
}
}
private static CimInstance CreateNullCimInstance()
{
return new CimInstance("Null");
}
#endregion
}
}