Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes308downloads
MshObjectTypeDescriptor.cs770 linesDownload Raw Back to engine
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.ComponentModel;5using System.Management.Automation.Runspaces;6 7namespace System.Management.Automation8{9    /// <summary>10    /// Serves as the arguments for events triggered by exceptions in the SetValue method of <see cref="PSObjectPropertyDescriptor"/>11    /// </summary>12    /// <remarks>13    /// The sender of this event is an object of type <see cref="PSObjectPropertyDescriptor"/>.14    /// It is permitted to subclass <see cref="SettingValueExceptionEventArgs"/>15    /// but there is no established scenario for doing this, nor has it been tested.16    /// </remarks>17    public class SettingValueExceptionEventArgs : EventArgs18    {19        /// <summary>20        /// Gets and sets a <see cref="System.Boolean"/> indicating if the SetValue method of <see cref="PSObjectPropertyDescriptor"/>21        /// should throw the exception associated with this event.22        /// </summary>23        /// <remarks>24        /// The default value is true, indicating that the Exception associated with this event will be thrown.25        /// </remarks>26        public bool ShouldThrow { get; set; }27 28        /// <summary>29        /// Gets the exception that triggered the associated event.30        /// </summary>31        public Exception Exception { get; }32 33        /// <summary>34        /// Initializes a new instance of <see cref="SettingValueExceptionEventArgs"/> setting the value of the exception that triggered the associated event.35        /// </summary>36        /// <param name="exception">Exception that triggered the associated event.</param>37        internal SettingValueExceptionEventArgs(Exception exception)38        {39            Exception = exception;40            ShouldThrow = true;41        }42    }43 44    /// <summary>45    /// Serves as the arguments for events triggered by exceptions in the GetValue46    /// method of <see cref="PSObjectPropertyDescriptor"/>47    /// </summary>48    /// <remarks>49    /// The sender of this event is an object of type <see cref="PSObjectPropertyDescriptor"/>.50    /// It is permitted to subclass <see cref="GettingValueExceptionEventArgs"/>51    /// but there is no established scenario for doing this, nor has it been tested.52    /// </remarks>53    public class GettingValueExceptionEventArgs : EventArgs54    {55        /// <summary>56        /// Gets and sets a <see cref="System.Boolean"/> indicating if the GetValue method of <see cref="PSObjectPropertyDescriptor"/>57        /// should throw the exception associated with this event.58        /// </summary>59        public bool ShouldThrow { get; set; }60 61        /// <summary>62        /// Gets the Exception that triggered the associated event.63        /// </summary>64        public Exception Exception { get; }65 66        /// <summary>67        /// Initializes a new instance of <see cref="GettingValueExceptionEventArgs"/> setting the value of the exception that triggered the associated event.68        /// </summary>69        /// <param name="exception">Exception that triggered the associated event.</param>70        internal GettingValueExceptionEventArgs(Exception exception)71        {72            Exception = exception;73            ValueReplacement = null;74            ShouldThrow = true;75        }76 77        /// <summary>78        /// Gets and sets the value that will serve as a replacement to the return of the GetValue79        /// method of <see cref="PSObjectPropertyDescriptor"/>. If this property is not set80        /// to a value other than null then the exception associated with this event is thrown.81        /// </summary>82        public object ValueReplacement { get; set; }83    }84 85    /// <summary>86    /// Serves as the property information generated by the GetProperties method of <see cref="PSObjectTypeDescriptor"/>.87    /// </summary>88    /// <remarks>89    /// It is permitted to subclass <see cref="SettingValueExceptionEventArgs"/>90    /// but there is no established scenario for doing this, nor has it been tested.91    /// </remarks>92    public class PSObjectPropertyDescriptor : PropertyDescriptor93    {94        internal event EventHandler<SettingValueExceptionEventArgs> SettingValueException;95 96        internal event EventHandler<GettingValueExceptionEventArgs> GettingValueException;97 98        internal PSObjectPropertyDescriptor(string propertyName, Type propertyType, bool isReadOnly, AttributeCollection propertyAttributes)99            : base(propertyName, Array.Empty<Attribute>())100        {101            IsReadOnly = isReadOnly;102            Attributes = propertyAttributes;103            PropertyType = propertyType;104        }105 106        /// <summary>107        /// Gets the collection of attributes for this member.108        /// </summary>109        public override AttributeCollection Attributes { get; }110 111        /// <summary>112        /// Gets a value indicating whether this property is read-only.113        /// </summary>114        public override bool IsReadOnly { get; }115 116        /// <summary>117        /// This method has no effect for <see cref="PSObjectPropertyDescriptor"/>.118        /// CanResetValue returns false.119        /// </summary>120        /// <param name="component">This parameter is ignored for <see cref="PSObjectPropertyDescriptor"/></param>121        public override void ResetValue(object component) { }122 123        /// <summary>124        /// Returns false to indicate that ResetValue has no effect.125        /// </summary>126        /// <param name="component">The component to test for reset capability.</param>127        /// <returns>False.</returns>128        public override bool CanResetValue(object component) { return false; }129 130        /// <summary>131        /// Returns true to indicate that the value of this property needs to be persisted.132        /// </summary>133        /// <param name="component">The component with the property to be examined for persistence.</param>134        /// <returns>True.</returns>135        public override bool ShouldSerializeValue(object component)136        {137            return true;138        }139 140        /// <summary>141        /// Gets the type of the component this property is bound to.142        /// </summary>143        /// <remarks>This property returns the <see cref="PSObject"/> type.</remarks>144        public override Type ComponentType145        {146            get { return typeof(PSObject); }147        }148 149        /// <summary>150        /// Gets the type of the property value.151        /// </summary>152        public override Type PropertyType { get; }153 154        /// <summary>155        /// Gets the current value of the property on a component.156        /// </summary>157        /// <param name="component">The component with the property for which to retrieve the value.</param>158        /// <returns>The value of a property for a given component.</returns>159        /// <exception cref="ExtendedTypeSystemException">160        /// If the property has not been found in the component or an exception has161        /// been thrown when getting the value of the property.162        /// This Exception will only be thrown if there is no event handler for the GettingValueException163        /// event of the <see cref="PSObjectTypeDescriptor"/> that created this <see cref="PSObjectPropertyDescriptor"/>.164        /// If there is an event handler, it can prevent this exception from being thrown, by changing165        /// the ShouldThrow property of <see cref="GettingValueExceptionEventArgs"/> from its default166        /// value of true to false.167        /// </exception>168        /// <exception cref="PSArgumentNullException">If <paramref name="component"/> is null.</exception>169        /// <exception cref="PSArgumentException">If <paramref name="component"/> is not170        /// an <see cref="PSObject"/> or an <see cref="PSObjectTypeDescriptor"/>.</exception>171        public override object GetValue(object component)172        {173            if (component == null)174            {175                throw PSTraceSource.NewArgumentNullException(nameof(component));176            }177 178            PSObject mshObj = GetComponentPSObject(component);179            PSPropertyInfo property;180            try181            {182                property = mshObj.Properties[this.Name] as PSPropertyInfo;183                if (property == null)184                {185                    PSObjectTypeDescriptor.typeDescriptor.WriteLine("Could not find property \"{0}\" to get its value.", this.Name);186                    ExtendedTypeSystemException e = new ExtendedTypeSystemException("PropertyNotFoundInPropertyDescriptorGetValue",187                        null,188                        ExtendedTypeSystem.PropertyNotFoundInTypeDescriptor, this.Name);189                    bool shouldThrow;190                    object returnValue = DealWithGetValueException(e, out shouldThrow);191                    if (shouldThrow)192                    {193                        throw e;194                    }195 196                    return returnValue;197                }198 199                return property.Value;200            }201            catch (ExtendedTypeSystemException e)202            {203                PSObjectTypeDescriptor.typeDescriptor.WriteLine("Exception getting the value of the property \"{0}\": \"{1}\".", this.Name, e.Message);204                bool shouldThrow;205                object returnValue = DealWithGetValueException(e, out shouldThrow);206                if (shouldThrow)207                {208                    throw;209                }210 211                return returnValue;212            }213        }214 215        private static PSObject GetComponentPSObject(object component)216        {217            // If you use the PSObjectTypeDescriptor directly as your object, it will be the component218            // if you use a provider, the PSObject will be the component.219            PSObject mshObj = component as PSObject;220            if (mshObj == null)221            {222                if (component is not PSObjectTypeDescriptor descriptor)223                {224                    throw PSTraceSource.NewArgumentException(nameof(component), ExtendedTypeSystem.InvalidComponent,225                                                             "component",226                                                             nameof(PSObject),227                                                             nameof(PSObjectTypeDescriptor));228                }229 230                mshObj = descriptor.Instance;231            }232 233            return mshObj;234        }235 236        private object DealWithGetValueException(ExtendedTypeSystemException e, out bool shouldThrow)237        {238            GettingValueExceptionEventArgs eventArgs = new GettingValueExceptionEventArgs(e);239            if (GettingValueException != null)240            {241                GettingValueException.SafeInvoke(this, eventArgs);242                PSObjectTypeDescriptor.typeDescriptor.WriteLine(243                    "GettingValueException event has been triggered resulting in ValueReplacement:\"{0}\".",244                    eventArgs.ValueReplacement);245            }246 247            shouldThrow = eventArgs.ShouldThrow;248            return eventArgs.ValueReplacement;249        }250 251        /// <summary>252        /// Sets the value of the component to a different value.253        /// </summary>254        /// <param name="component">The component with the property value that is to be set.</param>255        /// <param name="value">The new value.</param>256        /// <exception cref="ExtendedTypeSystemException">257        /// If the property has not been found in the component or an exception has258        /// been thrown when setting the value of the property.259        /// This Exception will only be thrown if there is no event handler for the SettingValueException260        /// event of the <see cref="PSObjectTypeDescriptor"/> that created this <see cref="PSObjectPropertyDescriptor"/>.261        /// If there is an event handler, it can prevent this exception from being thrown, by changing262        /// the ShouldThrow property of <see cref="SettingValueExceptionEventArgs"/>263        /// from its default value of true to false.264        /// </exception>265        /// <exception cref="PSArgumentNullException">If <paramref name="component"/> is null.</exception>266        /// <exception cref="PSArgumentException">If <paramref name="component"/> is not an267        /// <see cref="PSObject"/> or an <see cref="PSObjectTypeDescriptor"/>.268        /// </exception>269        public override void SetValue(object component, object value)270        {271            if (component == null)272            {273                throw PSTraceSource.NewArgumentNullException(nameof(component));274            }275 276            PSObject mshObj = GetComponentPSObject(component);277            try278            {279                PSPropertyInfo property = mshObj.Properties[this.Name] as PSPropertyInfo;280                if (property == null)281                {282                    PSObjectTypeDescriptor.typeDescriptor.WriteLine("Could not find property \"{0}\" to set its value.", this.Name);283                    ExtendedTypeSystemException e = new ExtendedTypeSystemException("PropertyNotFoundInPropertyDescriptorSetValue",284                        null,285                        ExtendedTypeSystem.PropertyNotFoundInTypeDescriptor, this.Name);286                    bool shouldThrow;287                    DealWithSetValueException(e, out shouldThrow);288                    if (shouldThrow)289                    {290                        throw e;291                    }292 293                    return;294                }295 296                property.Value = value;297            }298            catch (ExtendedTypeSystemException e)299            {300                PSObjectTypeDescriptor.typeDescriptor.WriteLine("Exception setting the value of the property \"{0}\": \"{1}\".", this.Name, e.Message);301                bool shouldThrow;302                DealWithSetValueException(e, out shouldThrow);303                if (shouldThrow)304                {305                    throw;306                }307            }308 309            OnValueChanged(component, EventArgs.Empty);310        }311 312        private void DealWithSetValueException(ExtendedTypeSystemException e, out bool shouldThrow)313        {314            SettingValueExceptionEventArgs eventArgs = new SettingValueExceptionEventArgs(e);315            if (SettingValueException != null)316            {317                SettingValueException.SafeInvoke(this, eventArgs);318                PSObjectTypeDescriptor.typeDescriptor.WriteLine(319                    "SettingValueException event has been triggered resulting in ShouldThrow:\"{0}\".",320                    eventArgs.ShouldThrow);321            }322 323            shouldThrow = eventArgs.ShouldThrow;324            return;325        }326    }327 328    /// <summary>329    /// Provides information about the properties for an object of the type <see cref="PSObject"/>.330    /// </summary>331    public class PSObjectTypeDescriptor : CustomTypeDescriptor332    {333        internal static readonly PSTraceSource typeDescriptor = PSTraceSource.GetTracer("TypeDescriptor", "Traces the behavior of PSObjectTypeDescriptor, PSObjectTypeDescriptionProvider and PSObjectPropertyDescriptor.", false);334 335        /// <summary>336        /// Occurs when there was an exception setting the value of a property.337        /// </summary>338        /// <remarks>339        /// The ShouldThrow property of the <see cref="SettingValueExceptionEventArgs"/> allows340        /// subscribers to prevent the exception from being thrown.341        /// </remarks>342        public event EventHandler<SettingValueExceptionEventArgs> SettingValueException;343 344        /// <summary>345        /// Occurs when there was an exception getting the value of a property.346        /// </summary>347        /// <remarks>348        /// The ShouldThrow property of the <see cref="GettingValueExceptionEventArgs"/> allows349        /// subscribers to prevent the exception from being thrown.350        /// </remarks>351        public event EventHandler<GettingValueExceptionEventArgs> GettingValueException;352 353        /// <summary>354        /// Initializes a new instance of the <see cref="PSObjectTypeDescriptor"/> that provides355        /// property information about <paramref name="instance"/>.356        /// </summary>357        /// <param name="instance">The <see cref="PSObject"/> this class retrieves property information from.</param>358        public PSObjectTypeDescriptor(PSObject instance)359        {360            Instance = instance;361        }362 363        /// <summary>364        /// Gets the <see cref="PSObject"/> this class retrieves property information from.365        /// </summary>366        public PSObject Instance { get; }367 368        private void CheckAndAddProperty(PSPropertyInfo propertyInfo, Attribute[] attributes, ref PropertyDescriptorCollection returnValue)369        {370            using (typeDescriptor.TraceScope("Checking property \"{0}\".", propertyInfo.Name))371            {372                // WriteOnly properties are not returned in TypeDescriptor.GetProperties, so we do the same.373                if (!propertyInfo.IsGettable)374                {375                    typeDescriptor.WriteLine("Property \"{0}\" is write-only so it has been skipped.", propertyInfo.Name);376                    return;377                }378 379                AttributeCollection propertyAttributes = null;380                Type propertyType = typeof(object);381                if (attributes != null && attributes.Length != 0)382                {383                    PSProperty property = propertyInfo as PSProperty;384                    if (property != null)385                    {386                        DotNetAdapter.PropertyCacheEntry propertyEntry = property.adapterData as DotNetAdapter.PropertyCacheEntry;387                        if (propertyEntry == null)388                        {389                            typeDescriptor.WriteLine("Skipping attribute check for property \"{0}\" because it is an adapted property (not a .NET property).", property.Name);390                        }391                        else if (property.isDeserialized)392                        {393                            // At the moment we are not serializing attributes, so we can skip394                            // the attribute check if the property is deserialized.395                            typeDescriptor.WriteLine("Skipping attribute check for property \"{0}\" because it has been deserialized.", property.Name);396                        }397                        else398                        {399                            propertyType = propertyEntry.propertyType;400                            propertyAttributes = propertyEntry.Attributes;401                            foreach (Attribute attribute in attributes)402                            {403                                if (!propertyAttributes.Contains(attribute))404                                {405                                    typeDescriptor.WriteLine("Property \"{0}\" does not contain attribute \"{1}\" so it has been skipped.", property.Name, attribute);406                                    return;407                                }408                            }409                        }410                    }411                }412 413                propertyAttributes ??= new AttributeCollection();414 415                typeDescriptor.WriteLine("Adding property \"{0}\".", propertyInfo.Name);416 417                PSObjectPropertyDescriptor propertyDescriptor =418                    new PSObjectPropertyDescriptor(propertyInfo.Name, propertyType, !propertyInfo.IsSettable, propertyAttributes);419 420                propertyDescriptor.SettingValueException += this.SettingValueException;421                propertyDescriptor.GettingValueException += this.GettingValueException;422 423                returnValue.Add(propertyDescriptor);424            }425        }426 427        /// <summary>428        /// Returns a collection of property descriptors for the <see cref="PSObject"/> represented by this type descriptor.429        /// </summary>430        /// <returns>A PropertyDescriptorCollection containing the property descriptions for the <see cref="PSObject"/> represented by this type descriptor.</returns>431        public override PropertyDescriptorCollection GetProperties()432        {433            return GetProperties(null);434        }435 436        /// <summary>437        /// Returns a filtered collection of property descriptors for the <see cref="PSObject"/> represented by this type descriptor.438        /// </summary>439        /// <param name="attributes">An array of attributes to use as a filter. This can be a null reference (Nothing in Visual Basic).</param>440        /// <returns>A PropertyDescriptorCollection containing the property descriptions for the <see cref="PSObject"/> represented by this type descriptor.</returns>441        public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)442        {443            using (typeDescriptor.TraceScope("Getting properties."))444            {445                PropertyDescriptorCollection returnValue = new PropertyDescriptorCollection(null);446                if (Instance == null)447                {448                    return returnValue;449                }450 451                foreach (PSPropertyInfo property in Instance.Properties)452                {453                    CheckAndAddProperty(property, attributes, ref returnValue);454                }455 456                return returnValue;457            }458        }459 460        /// <summary>461        /// Determines whether the Instance property of <paramref name="obj"/> is equal to the current Instance.462        /// </summary>463        /// <param name="obj">The Object to compare with the current Object.</param>464        /// <returns>True if the Instance property of <paramref name="obj"/> is equal to the current Instance; otherwise, false.</returns>465        public override bool Equals(object obj)466        {467            if (obj is not PSObjectTypeDescriptor other)468            {469                return false;470            }471 472            if (this.Instance == null || other.Instance == null)473            {474                return ReferenceEquals(this, other);475            }476 477            return other.Instance.Equals(this.Instance);478        }479 480        /// <summary>481        /// Provides a value for hashing algorithms.482        /// </summary>483        /// <returns>A hash code for the current object.</returns>484        public override int GetHashCode()485        {486            if (this.Instance == null)487            {488                return base.GetHashCode();489            }490 491            return this.Instance.GetHashCode();492        }493 494        /// <summary>495        /// Returns the default property for this object.496        /// </summary>497        /// <returns>An <see cref="PSObjectPropertyDescriptor"/> that represents the default property for this object, or a null reference (Nothing in Visual Basic) if this object does not have properties.</returns>498        public override PropertyDescriptor GetDefaultProperty()499        {500            if (this.Instance == null)501            {502                return null;503            }504 505            string defaultProperty = null;506            PSMemberSet standardMembers = this.Instance.PSStandardMembers;507            if (standardMembers != null)508            {509                PSNoteProperty note = standardMembers.Properties[TypeTable.DefaultDisplayProperty] as PSNoteProperty;510                if (note != null)511                {512                    defaultProperty = note.Value as string;513                }514            }515 516            if (defaultProperty == null)517            {518                object[] defaultPropertyAttributes = this.Instance.BaseObject.GetType().GetCustomAttributes(typeof(DefaultPropertyAttribute), true);519                if (defaultPropertyAttributes.Length == 1)520                {521                    DefaultPropertyAttribute defaultPropertyAttribute = defaultPropertyAttributes[0] as DefaultPropertyAttribute;522                    if (defaultPropertyAttribute != null)523                    {524                        defaultProperty = defaultPropertyAttribute.Name;525                    }526                }527            }528 529            PropertyDescriptorCollection properties = this.GetProperties();530 531            if (defaultProperty != null)532            {533                // There is a defaultProperty, but let's check if it is actually one of the properties we are534                // returning in GetProperties535                foreach (PropertyDescriptor descriptor in properties)536                {537                    if (string.Equals(descriptor.Name, defaultProperty, StringComparison.OrdinalIgnoreCase))538                    {539                        return descriptor;540                    }541                }542            }543 544            return null;545        }546 547        /// <summary>548        /// Returns a type converter for this object.549        /// </summary>550        /// <returns>A <see cref="TypeConverter"/> that is the converter for this object, or a null reference (Nothing in Visual Basic) if there is no <see cref="TypeConverter"/> for this object.</returns>551        public override TypeConverter GetConverter()552        {553            if (this.Instance == null)554            {555                // If we return null, some controls will have an exception saying that this556                // GetConverter returned an illegal value557                return new TypeConverter();558            }559 560            object baseObject = this.Instance.BaseObject;561            TypeConverter retValue = LanguagePrimitives.GetConverter(baseObject.GetType(), null) as TypeConverter ??562                                     TypeDescriptor.GetConverter(baseObject);563            return retValue;564        }565 566        /// <summary>567        /// Returns the object that this value is a member of.568        /// </summary>569        /// <param name="pd">A <see cref="PropertyDescriptor"/> that represents the property whose owner is to be found.</param>570        /// <returns>An object that represents the owner of the specified property.</returns>571        public override object GetPropertyOwner(PropertyDescriptor pd)572        {573            return this.Instance;574        }575 576        #region Overrides Forwarded To BaseObject577 578        #region ReadMe579        // This region contains methods implemented like:580        //    TypeDescriptor.OverrideName(this.Instance.BaseObject)581        // They serve the purpose of exposing Attributes and other information from the BaseObject582        // of an PSObject, since the PSObject itself does not have the concept of class (or member)583        // attributes.584        // The calls are not recursive because the BaseObject was implemented so it is never585        // another PSObject. ImmediateBaseObject or PSObject.Base could cause the call to be586        // recursive in the case of an object like "new PSObject(new PSObject())".587        // Even if we used ImmediateBaseObject, the recursion would be finite since we would588        // keep getting an ImmediatebaseObject until it ceased to be an PSObject.589        #endregion ReadMe590 591        /// <summary>592        /// Returns the default event for this object.593        /// </summary>594        /// <returns>An <see cref="EventDescriptor"/> that represents the default event for this object, or a null reference (Nothing in Visual Basic) if this object does not have events.</returns>595        public override EventDescriptor GetDefaultEvent()596        {597            if (this.Instance == null)598            {599                return null;600            }601 602            return TypeDescriptor.GetDefaultEvent(this.Instance.BaseObject);603        }604 605        /// <summary>606        /// Returns the events for this instance of a component.607        /// </summary>608        /// <returns>An <see cref="EventDescriptorCollection"/> that represents the events for this component instance.</returns>609        public override EventDescriptorCollection GetEvents()610        {611            if (this.Instance == null)612            {613                return new EventDescriptorCollection(null);614            }615 616            return TypeDescriptor.GetEvents(this.Instance.BaseObject);617        }618 619        /// <summary>620        /// Returns the events for this instance of a component using the attribute array as a filter.621        /// </summary>622        /// <param name="attributes">An array of type <see cref="Attribute"/> that is used as a filter.</param>623        /// <returns>An <see cref="EventDescriptorCollection"/> that represents the events for this component instance that match the given set of attributes.</returns>624        public override EventDescriptorCollection GetEvents(Attribute[] attributes)625        {626            if (this.Instance == null)627            {628                return null;629            }630 631            return TypeDescriptor.GetEvents(this.Instance.BaseObject, attributes);632        }633 634        /// <summary>635        /// Returns a collection of type <see cref="Attribute"/> for this object.636        /// </summary>637        /// <returns>An <see cref="AttributeCollection"/> with the attributes for this object.</returns>638        public override AttributeCollection GetAttributes()639        {640            if (this.Instance == null)641            {642                return new AttributeCollection();643            }644 645            return TypeDescriptor.GetAttributes(this.Instance.BaseObject);646        }647 648        /// <summary>649        /// Returns the class name of this object.650        /// </summary>651        /// <returns>The class name of the object, or a null reference (Nothing in Visual Basic) if the class does not have a name.</returns>652        public override string GetClassName()653        {654            if (this.Instance == null)655            {656                return null;657            }658 659            return TypeDescriptor.GetClassName(this.Instance.BaseObject);660        }661 662        /// <summary>663        /// Returns the name of this object.664        /// </summary>665        /// <returns>The name of the object, or a null reference (Nothing in Visual Basic) if object does not have a name.</returns>666        public override string GetComponentName()667        {668            if (this.Instance == null)669            {670                return null;671            }672 673            return TypeDescriptor.GetComponentName(this.Instance.BaseObject);674        }675 676        /// <summary>677        /// Returns an editor of the specified type for this object.678        /// </summary>679        /// <param name="editorBaseType">A <see cref="Type"/> that represents the editor for this object.</param>680        /// <returns>An object of the specified type that is the editor for this object, or a null reference (Nothing in Visual Basic) if the editor cannot be found.</returns>681        public override object GetEditor(Type editorBaseType)682        {683            if (this.Instance == null)684            {685                return null;686            }687 688            return TypeDescriptor.GetEditor(this.Instance.BaseObject, editorBaseType);689        }690        #endregion Forwarded To BaseObject691    }692 693    /// <summary>694    /// Retrieves a <see cref="PSObjectTypeDescriptor"/> to provides information about the properties for an object of the type <see cref="PSObject"/>.695    /// </summary>696    public class PSObjectTypeDescriptionProvider : TypeDescriptionProvider697    {698        /// <summary>699        /// Occurs when there was an exception setting the value of a property.700        /// </summary>701        /// <remarks>702        /// The ShouldThrow property of the <see cref="SettingValueExceptionEventArgs"/> allows703        /// subscribers to prevent the exception from being thrown.704        /// </remarks>705        public event EventHandler<SettingValueExceptionEventArgs> SettingValueException;706 707        /// <summary>708        /// Occurs when there was an exception getting the value of a property.709        /// </summary>710        /// <remarks>711        /// The ShouldThrow property of the <see cref="GettingValueExceptionEventArgs"/> allows712        /// subscribers to prevent the exception from being thrown.713        /// </remarks>714        public event EventHandler<GettingValueExceptionEventArgs> GettingValueException;715 716        /// <summary>717        /// Initializes a new instance of <see cref="PSObjectTypeDescriptionProvider"/>718        /// </summary>719        public PSObjectTypeDescriptionProvider()720        {721        }722 723        /// <summary>724        /// Retrieves a <see cref="PSObjectTypeDescriptor"/> to provide information about the properties for an object of the type <see cref="PSObject"/>.725        /// </summary>726        /// <param name="objectType">The type of object for which to retrieve the type descriptor. If this parameter is not noll and is not the <see cref="PSObject"/>, the return of this method will be null.</param>727        /// <param name="instance">An instance of the type. If instance is null or has a type other than <see cref="PSObject"/>, this method returns null.</param>728        /// <returns>An <see cref="ICustomTypeDescriptor"/> that can provide property information for the729        /// type <see cref="PSObject"/>, or null if <paramref name="objectType"/> is not null,730        /// but has a type other than <see cref="PSObject"/>.</returns>731        public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)732        {733            PSObject mshObj = instance as PSObject;734 735            #region ReadMe736            // Instance can be null, in a couple of circumstances:737            //    1) In one of the many calls to this method caused by setting the SelectedObject738            // property of a PropertyGrid.739            //    2) If, by mistake, an object[] or Collection<PSObject> is used instead of an ArrayList740            // to set the DataSource property of a DataGrid or DatagridView.741            //742            // It would be nice to throw an exception for the case 2) instructing the user to use743            // an ArrayList, but since we have case 1) and maybe others we haven't found we return744            // an PSObjectTypeDescriptor(null). PSObjectTypeDescriptor's GetProperties745            // checks for null instance and returns an empty property collection.746            // All other overrides also check for null and return some default result.747            // Case 1), which is using a PropertyGrid seems to be unaffected by these results returned748            // by PSObjectTypeDescriptor overrides when the Instance is null, so we must conclude749            // that the TypeDescriptor returned by that call where instance is null is not used750            // for anything meaningful. That null instance PSObjectTypeDescriptor is only one751            // of the many PSObjectTypeDescriptor's returned by this method in a PropertyGrid use.752            // Some of the other calls to this method are passing a valid instance and the objects753            // returned by these calls seem to be the ones used for meaningful calls in the PropertyGrid.754            //755            // It might sound strange that we are not verifying the type of objectType or of instance756            // to be PSObject, but in this PropertyGrid use that passes a null instance (case 1), if757            // we return null we have an exception flagging the return as invalid. Since we cannot758            // return null and MSDN has a note saying that we should return null instead of throwing759            // exceptions, the safest behavior seems to be creating this PSObjectTypeDescriptor with760            // null instance.761            #endregion ReadMe762 763            PSObjectTypeDescriptor typeDescriptor = new PSObjectTypeDescriptor(mshObj);764            typeDescriptor.SettingValueException += this.SettingValueException;765            typeDescriptor.GettingValueException += this.GettingValueException;766            return typeDescriptor;767        }768    }769}770