using System.Collections.Generic; using System.ComponentModel; using Vanara.PInvoke; using static Vanara.PInvoke.Ole32; using static Vanara.PInvoke.PropSys; namespace Vanara.Windows.Shell; /// A property store for a . /// public sealed class ShellItemPropertyStore : PropertyStore { /// The shell item private ShellItem shellItem; /// The flags. private GETPROPERTYSTOREFLAGS flags = GETPROPERTYSTOREFLAGS.GPS_BESTEFFORT; /// Initializes a new instance of the class. /// The ShellItem instance. /// The optional property changed handler. internal ShellItemPropertyStore(ShellItem item, PropertyChangedEventHandler? propChangedHandler = null) { //item.ThrowIfNoShellItem2(); shellItem = item; if (propChangedHandler != null) PropertyChanged += propChangedHandler; } /// /// Gets or sets the ICreateObject used instead of CoCreateInstance to create an instance of the property handler associated with /// the Shell item on which this method is called. If this value is set, will be ignored. /// /// The creator object. [DefaultValue(null)] public ICreateObject? Creator { get; set; } /// Gets or sets a value indicating whether to include slow properties. /// true if including slow properties; otherwise, false. [DefaultValue(false)] public bool IncludeSlow { get => flags.IsFlagSet(GETPROPERTYSTOREFLAGS.GPS_OPENSLOWITEM); set { if (IncludeSlow == value) return; flags = flags.SetFlags(GETPROPERTYSTOREFLAGS.GPS_OPENSLOWITEM, value); if (value) flags = flags.SetFlags(GETPROPERTYSTOREFLAGS.GPS_TEMPORARY | GETPROPERTYSTOREFLAGS.GPS_FASTPROPERTIESONLY, false); } } /// Gets a value indicating whether the is read-only. public override bool IsReadOnly => ReadOnly; /// Gets or sets a value indicating whether to include only properties directly from the property handler. /// true if no inherited properties; otherwise, false. [DefaultValue(false)] public bool NoInheritedProperties { get => flags.IsFlagSet(GETPROPERTYSTOREFLAGS.GPS_HANDLERPROPERTIESONLY); set { if (NoInheritedProperties == value) return; flags = flags.SetFlags(GETPROPERTYSTOREFLAGS.GPS_HANDLERPROPERTIESONLY, value); if (value) flags = flags.SetFlags(GETPROPERTYSTOREFLAGS.GPS_TEMPORARY | GETPROPERTYSTOREFLAGS.GPS_BESTEFFORT | GETPROPERTYSTOREFLAGS.GPS_FASTPROPERTIESONLY, false); } } /// /// Gets or sets a set of properties used to filter the property store. This value can be . This value will be /// ignored if is also set. /// /// The list of properties used to filter. [DefaultValue(null)] public PROPERTYKEY[]? PropertyFilter { get; set; } /// Gets or sets a value indicating whether properties can be read and written. /// true if properties are read/write; otherwise, false. [DefaultValue(true)] public bool ReadOnly { get => !flags.IsFlagSet(GETPROPERTYSTOREFLAGS.GPS_READWRITE); set { if (ReadOnly == value) return; flags = flags.SetFlags(GETPROPERTYSTOREFLAGS.GPS_READWRITE, !value); if (!value) flags = flags.SetFlags(GETPROPERTYSTOREFLAGS.GPS_DELAYCREATION | GETPROPERTYSTOREFLAGS.GPS_TEMPORARY | GETPROPERTYSTOREFLAGS.GPS_BESTEFFORT | GETPROPERTYSTOREFLAGS.GPS_FASTPROPERTIESONLY, false); else flags = flags.SetFlags(GETPROPERTYSTOREFLAGS.GPS_HANDLERPROPERTIESONLY); } } /// /// Gets or sets a value indicating whether this provides a writable store, with no initial /// properties, that exists for the lifetime of the Shell item instance; basically, a property bag attached to the item instance. /// /// true if temporary; otherwise, false. [DefaultValue(false)] public bool Temporary { get => flags.IsFlagSet(GETPROPERTYSTOREFLAGS.GPS_TEMPORARY); set { if (Temporary == value) return; flags = flags.SetFlags(GETPROPERTYSTOREFLAGS.GPS_TEMPORARY, value); if (value) { flags = GETPROPERTYSTOREFLAGS.GPS_TEMPORARY; ReadOnly = false; } else { flags = flags.SetFlags(GETPROPERTYSTOREFLAGS.GPS_TEMPORARY, false); } } } /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. public override void Dispose() { base.Dispose(); GC.SuppressFinalize(this); } /// Gets the CLSID of a supplied property key. /// The property key. /// The CLSID related to the property key. public Guid GetCLSID(PROPERTYKEY propertyKey) => shellItem?.iShellItem2?.GetCLSID(propertyKey) ?? Guid.Empty; /// The IPropertyStore instance. This can be null. protected override IPropertyStore? GetIPropertyStore() { if (shellItem is ShellLink lnk) return (IPropertyStore)lnk.link; try { if (Creator != null) return shellItem?.iShellItem2?.GetPropertyStoreWithCreateObject(flags, Creator, typeof(IPropertyStore).GUID); if (PropertyFilter != null && PropertyFilter.Length > 0) return shellItem?.iShellItem2?.GetPropertyStoreForKeys(PropertyFilter, (uint)PropertyFilter.Length, flags, typeof(IPropertyStore).GUID); return shellItem?.iShellItem2?.GetPropertyStore(flags, typeof(IPropertyStore).GUID); } catch (COMException comex) when (comex.ErrorCode == HRESULT.E_FAIL) { throw new InvalidOperationException($"The ShellItem does not support a property store with the flags: {flags}", comex); } } }