MegaBites-AI/Windows-powershell
0308
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 