Enhanced and fixed COM Property abstractions.

pull/10/head
David Hall 2018-01-27 14:40:26 -07:00
parent 5f5acea05b
commit 3b6d12c8e0
10 changed files with 326 additions and 142 deletions

View File

@ -39,7 +39,7 @@ namespace Vanara.PInvoke.Tests
{
var pv = new PROPVARIANT();
var g = Guid.NewGuid();
InitPropVariantFromCLSID(ref g, pv);
InitPropVariantFromCLSID(g, pv);
Assert.That(pv.VarType, Is.EqualTo(VarEnum.VT_CLSID));
Assert.That(pv.Value, Is.EqualTo(g));
pv.Dispose();

View File

@ -152,7 +152,7 @@ namespace Vanara.Collections.Tests
foreach (var pd in e)
{
Assert.IsInstanceOf<IPropertyDescription>(pd);
var s = pd.GetDisplayName();
pd.GetDisplayName(out var s);
l.Add(s);
TestContext.WriteLine(s);
c++;

View File

@ -1,21 +1,16 @@
using System;
using NUnit.Framework;
using System.Diagnostics;
using System.Runtime.InteropServices.ComTypes;
using Vanara.PInvoke;
using Vanara.Windows.Shell;
using static Vanara.PInvoke.Ole32;
using static Vanara.PInvoke.Shell32;
using System.IO;
using System.Linq;
namespace Vanara.Windows.Forms.Tests
namespace Vanara.Windows.Shell.Tests
{
[TestFixture]
public class ShellFolderTests
{
private const string testFile = @"C:\Temp\Test.doc";
private const string testFld = @"C:\Temp";
private const string testFile = ShellItemTests.testDoc;
private static string testFld = Path.GetDirectoryName(testFile);
[Test]
public void ShellFolderTest1()
@ -35,9 +30,9 @@ namespace Vanara.Windows.Forms.Tests
{
Assert.That(() =>
{
var d = new ShellFolder(KNOWNFOLDERID.FOLDERID_Desktop);
Assert.That(d, Is.EqualTo(ShellFolder.Desktop));
var i = new ShellFolder(KNOWNFOLDERID.FOLDERID_ProgramFiles);
var d = new ShellFolder(KNOWNFOLDERID.FOLDERID_Desktop);
Assert.That(d, Is.EqualTo(ShellFolder.Desktop));
var i = new ShellFolder(KNOWNFOLDERID.FOLDERID_ProgramFiles);
Assert.That(i.FileSystemPath, Is.EqualTo(Environment.GetEnvironmentVariable("ProgramFiles")));
}, Throws.Nothing);
Assert.That(() => new ShellFolder((KNOWNFOLDERID)int.MaxValue), Throws.TypeOf<FileNotFoundException>());
@ -66,7 +61,7 @@ namespace Vanara.Windows.Forms.Tests
Assert.That(i.FileSystemPath, Is.EqualTo(testFld));
}, Throws.Nothing);
Assert.That(() => new ShellFolder((ShellItem) null), Throws.Exception);
Assert.That(() => new ShellFolder(new ShellItem(@"C:\Temp\Test.doc")), Throws.Exception);
Assert.That(() => new ShellFolder(new ShellItem(testFile)), Throws.Exception);
}
[Test]
@ -113,7 +108,7 @@ namespace Vanara.Windows.Forms.Tests
Assert.That(lnk, Is.Not.Null.And.InstanceOf<ShellLink>());
}
}
}, Throws.Nothing);
}, Throws.Nothing);
Assert.That(() => new ShellFolder(KNOWNFOLDERID.FOLDERID_Windows).EnumerateChildren((FolderItemFilter)0x80000), Is.Empty);
}
@ -131,6 +126,6 @@ namespace Vanara.Windows.Forms.Tests
Assert.That(() => f.GetChildrenUIObjects<IShellLibrary>(null, i), Throws.TypeOf<NotImplementedException>());
Assert.That(() => f.GetViewObject<IShellLibrary>(null), Throws.TypeOf<NotImplementedException>());
}
}
}
}
}
}

View File

@ -0,0 +1,94 @@
using System;
using NUnit.Framework;
using System.Diagnostics;
using System.Runtime.InteropServices.ComTypes;
using Vanara.PInvoke;
using Vanara.Windows.Shell;
using static Vanara.PInvoke.Ole32;
using static Vanara.PInvoke.Shell32;
using System.Runtime.InteropServices;
namespace Vanara.Windows.Shell.Tests
{
[TestFixture]
public class ShellItemPropStoreTests
{
private const string testDoc = ShellItemTests.testDoc;
[Test]
public void ShellItemPropertyStoreTest1()
{
using (var i = new ShellItem(testDoc))
{
Assert.That(i.Properties, Is.Not.Null.And.Count.GreaterThan(0));
Assert.That(i.Properties, Is.EqualTo(i.Properties));
Assert.That(i.Properties.IncludeSlow, Is.False);
Assert.That(i.Properties.IsDirty, Is.False);
Assert.That(i.Properties.NoInheritedProperties, Is.False);
Assert.That(i.Properties.ReadOnly, Is.True);
Assert.That(i.Properties.Temporary, Is.False);
}
}
[Test]
public void EnumTest()
{
using (var i = new ShellItem(testDoc))
{
var c = 0;
foreach (var key in i.Properties.Keys)
{
c++;
using (var d = i.Properties.Descriptions[key])
{
Assert.That(d, Is.Not.Null);
Assert.That(() => {
var val = i.Properties[key];
TestContext.WriteLine($"({c}) {key} = {val}");
TestContext.WriteLine($" {d.FormatForDisplay(val)}");
TestContext.WriteLine($" DispN:{d.DisplayName}; DispT:{d.DisplayType}");
}, Throws.Nothing);
}
TestContext.WriteLine("");
}
Assert.That(c, Is.EqualTo(i.Properties.Count));
}
}
[Test]
public void DescriptionTest()
{
using (var i = new ShellItem(testDoc))
{
var c = 0;
foreach (var d in i.Properties.Descriptions)
{
c++;
Assert.That(d, Is.Not.Null);
Assert.That(() =>
{
TestContext.WriteLine($"Agg:{d.AggregationType}; Can:{d.CanonicalName}; ColSt:{d.ColumnState}");
TestContext.WriteLine($"Cond:{d.ConditionType}; ColW:{d.DefaultColumnWidth}; DispN:{d.DisplayName}");
TestContext.WriteLine($"DispT:{d.DisplayType}; EditInv:{d.EditInvitation}; SortDesc:{d.GetSortDescriptionLabel()}");
TestContext.WriteLine($"GrpRng:{d.GroupingRange}; PropType:{d.PropertyType}; RelDescType:{d.RelativeDescriptionType}");
TestContext.WriteLine($"SortDesc:{d.SortDescription}; TypeFlags:{d.TypeFlags}; ViewFlags:{d.ViewFlags}");
using (var pv = i.Properties.GetPropVariant(d.PropertyKey))
{
Assert.That(pv, Is.Not.Null);
TestContext.WriteLine($" IsCan:{d.IsValueCanonical(pv)}; ImgLoc:{d.GetImageLocationForValue(pv)}");
}
}, Throws.Nothing);
if (d.TypeList.Count > 0) Debug.WriteLine(" Prop Types:");
foreach (var t in d.TypeList)
{
Debug.WriteLine($" DispTx:{t.DisplayText}; EnumT:{t.EnumType}");
Debug.WriteLine($" ImgLoc:{t.ImageReference}; RngMin:{t.RangeMinValue}");
Debug.WriteLine($" RngSet:{t.RangeSetValue}; Val:{t.Value}");
}
}
TestContext.WriteLine("");
Assert.That(c, Is.EqualTo(i.Properties.Descriptions.Count));
}
}
}
}

View File

@ -3,18 +3,17 @@ using NUnit.Framework;
using System.Diagnostics;
using System.Runtime.InteropServices.ComTypes;
using Vanara.PInvoke;
using Vanara.Windows.Shell;
using static Vanara.PInvoke.Ole32;
using static Vanara.PInvoke.Shell32;
namespace Vanara.Windows.Forms.Tests
namespace Vanara.Windows.Shell.Tests
{
[TestFixture]
public class ShellItemTests
{
private const string badTestDoc = @"C:\Temp\BadTest.doc";
private const string testDoc = @"C:\Temp\Test.doc";
private const string testLinkDoc = @"C:\Temp\Test.lnk";
internal const string badTestDoc = @"C:\Temp\BadTest.doc";
internal const string testDoc = @"C:\Temp\Test.docx";
internal const string testLinkDoc = @"C:\Temp\Test.lnk";
[Test]
public void ShellItemTest1()

View File

@ -117,6 +117,7 @@
<Compile Include="Security\AccessControl\SystemSecurityTests.cs" />
<Compile Include="Shell\ShellItemTests.cs" />
<Compile Include="Shell\ShellFolderTests.cs" />
<Compile Include="Shell\ShellItemPropStoreTests.cs" />
<Compile Include="System\VirtualDiskTests.cs" />
<Compile Include="UI\Controls\CredentialsDialogTests.cs" />
</ItemGroup>

View File

@ -13,78 +13,96 @@ namespace Vanara.Windows.Shell
protected IPropertyDescription iDesc;
/// <summary>The IPropertyDescription2 object.</summary>
protected IPropertyDescription2 iDesc2;
/// <summary>The property key for this property.</summary>
protected PROPERTYKEY key;
/// <summary>Gets the type list.</summary>
protected PropertyTypeList typeList;
/// <summary>Initializes a new instance of the <see cref="PropertyDescription"/> class.</summary>
/// <param name="propkey">A valid <see cref="PROPERTYKEY"/>.</param>
public PropertyDescription(PROPERTYKEY propkey)
{
key = propkey;
if (PSGetPropertyDescription(ref propkey, typeof(IPropertyDescription).GUID, out var ppv).Succeeded)
iDesc = (IPropertyDescription)ppv;
}
/// <summary>Initializes a new instance of the <see cref="PropertyDescription"/> class.</summary>
/// <param name="propertyDescription">The property description.</param>
internal protected PropertyDescription(IPropertyDescription propertyDescription)
{
iDesc = propertyDescription ?? throw new ArgumentNullException(nameof(propertyDescription));
iDesc = propertyDescription;
key = iDesc.GetPropertyKey();
}
/// <summary>Gets a value that describes how the property values are displayed when multiple items are selected in the UI.</summary>
public PROPDESC_AGGREGATION_TYPE AggregationType => iDesc.GetAggregationType();
public PROPDESC_AGGREGATION_TYPE AggregationType => iDesc?.GetAggregationType() ?? 0;
/// <summary>Gets the case-sensitive name by which a property is known to the system, regardless of its localized name.</summary>
public string CanonicalName => iDesc.GetCanonicalName();
public string CanonicalName => iDesc?.GetCanonicalName();
/// <summary>Gets the column state flag, which describes how the property should be treated by interfaces or APIs that use this flag.</summary>
public SHCOLSTATE ColumnState => iDesc.GetColumnState();
public SHCOLSTATE ColumnState => iDesc?.GetColumnState() ?? 0;
/// <summary>Gets the condition type and default condition operation to use when displaying the property in the query builder UI. This influences the list of predicate conditions (for example, equals, less than, and contains) that are shown for this property.</summary>
public Tuple<PROPDESC_CONDITION_TYPE, CONDITION_OPERATION> ConditionType
{
get { iDesc.GetConditionType(out var ct, out var co); return new Tuple<PROPDESC_CONDITION_TYPE, CONDITION_OPERATION>(ct, co); }
get
{
PROPDESC_CONDITION_TYPE ct = 0;
CONDITION_OPERATION co = 0;
if (iDesc != null)
iDesc.GetConditionType(out ct, out co);
return new Tuple<PROPDESC_CONDITION_TYPE, CONDITION_OPERATION>(ct, co);
}
}
/// <summary>Gets the default column width of the property in a list view.</summary>
/// <returns>A pointer to the column width value, in characters.</returns>
public uint DefaultColumnWidth => iDesc.GetDefaultColumnWidth();
public uint DefaultColumnWidth => iDesc?.GetDefaultColumnWidth() ?? 0;
/// <summary>Gets the display name of the property as it is shown in any UI.</summary>
public string DisplayName => iDesc.GetDisplayName();
public string DisplayName => iDesc != null && iDesc.GetDisplayName(out var s).Succeeded ? s : null;
/// <summary>Gets the current data type used to display the property.</summary>
public PROPDESC_DISPLAYTYPE DisplayType => iDesc.GetDisplayType();
public PROPDESC_DISPLAYTYPE DisplayType { get { try { return iDesc?.GetDisplayType() ?? 0; } catch { return 0; } } }
/// <summary>Gets the text used in edit controls hosted in various dialog boxes.</summary>
public string EditInvitation => iDesc.GetEditInvitation();
public string EditInvitation => iDesc?.GetEditInvitation();
/// <summary>Gets the grouping method to be used when a view is grouped by a property, and retrieves the grouping type.</summary>
public PROPDESC_GROUPING_RANGE GroupingRange => iDesc.GetGroupingRange();
public PROPDESC_GROUPING_RANGE GroupingRange => iDesc?.GetGroupingRange() ?? 0;
/// <summary>Gets a structure that acts as a property's unique identifier.</summary>
public PROPERTYKEY PropertyKey => iDesc.GetPropertyKey();
public PROPERTYKEY PropertyKey => key;
/// <summary>Gets the variant type of the property. If the type cannot be determined, this property returns <c>null</c>.</summary>
public Type PropertyType => PROPVARIANT.GetType(iDesc.GetPropertyType());
public Type PropertyType => PROPVARIANT.GetType(iDesc?.GetPropertyType() ?? VARTYPE.VT_EMPTY);
/// <summary>Gets the relative description type for a property description.</summary>
public PROPDESC_RELATIVEDESCRIPTION_TYPE RelativeDescriptionType => iDesc.GetRelativeDescriptionType();
public PROPDESC_RELATIVEDESCRIPTION_TYPE RelativeDescriptionType => iDesc?.GetRelativeDescriptionType() ?? 0;
/// <summary>Gets the current sort description flags for the property, which indicate the particular wordings of sort offerings.</summary>
public PROPDESC_SORTDESCRIPTION SortDescription => iDesc.GetSortDescription();
public PROPDESC_SORTDESCRIPTION SortDescription => iDesc?.GetSortDescription() ?? 0;
/// <summary>Gets a set of flags that describe the uses and capabilities of the property.</summary>
public PROPDESC_TYPE_FLAGS TypeFlags => iDesc.GetTypeFlags(PROPDESC_TYPE_FLAGS.PDTF_MASK_ALL);
public PROPDESC_TYPE_FLAGS TypeFlags => iDesc?.GetTypeFlags(PROPDESC_TYPE_FLAGS.PDTF_MASK_ALL) ?? 0;
/// <summary>Gets an instance of an PropertyTypeList, which can be used to enumerate the possible values for a property.</summary>
public PropertyTypeList TypeList => typeList ?? (typeList = new PropertyTypeList(iDesc.GetEnumTypeList() as IPropertyEnumTypeList));
// /// <summary>Gets an instance of an PropertyTypeList, which can be used to enumerate the possible values for a property.</summary>
public PropertyTypeList TypeList => typeList ?? (typeList = new PropertyTypeList(iDesc?.GetEnumTypeList(typeof(IPropertyEnumTypeList).GUID)));
/// <summary>Gets the current set of flags governing the property's view.</summary>
/// <returns>When this method returns, contains a pointer to a value that includes one or more of the following flags. See PROPDESC_VIEW_FLAGS for valid values.</returns>
public PROPDESC_VIEW_FLAGS ViewFlags => iDesc.GetViewFlags();
public PROPDESC_VIEW_FLAGS ViewFlags => iDesc?.GetViewFlags() ?? 0;
/// <summary>Coerces the value to the canonical value, according to the property description.</summary>
/// <param name="propvar">On entry, contains a PROPVARIANT that contains the original value. When this method returns, contains the canonical value.</param>
public bool CoerceToCanonicalValue(PROPVARIANT propvar) => iDesc.CoerceToCanonicalValue(propvar).Succeeded;
public bool CoerceToCanonicalValue(PROPVARIANT propvar) => iDesc?.CoerceToCanonicalValue(propvar).Succeeded ?? false;
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
public virtual void Dispose()
{
typeList?.Dispose();
// typeList?.Dispose();
if (iDesc2 != null)
{
Marshal.ReleaseComObject(iDesc2);
@ -96,44 +114,52 @@ namespace Vanara.Windows.Shell
iDesc = null;
}
}
/// <summary>Gets a formatted string representation of a property value.</summary>
/// <param name="propvar">A PROPVARIANT that contains the type and value of the property.</param>
/// <param name="obj">A object that contains the type and value of the property.</param>
/// <param name="pdfFlags">One or more of the PROPDESC_FORMAT_FLAGS flags, which are either bitwise or multiple values, that indicate the property string format.</param>
/// <returns>The formatted value.</returns>
public string FormatForDisplay(PROPVARIANT propvar, PROPDESC_FORMAT_FLAGS pdfFlags = PROPDESC_FORMAT_FLAGS.PDFF_DEFAULT)
{
var sb = new System.Text.StringBuilder(0x8000);
var key = PropertyKey;
iDesc.FormatForDisplay(ref key, propvar, pdfFlags, sb, (uint)sb.Capacity);
return sb.ToString();
}
public string FormatForDisplay(object obj, PROPDESC_FORMAT_FLAGS pdfFlags = PROPDESC_FORMAT_FLAGS.PDFF_DEFAULT) => iDesc?.FormatForDisplay(new PROPVARIANT(obj), pdfFlags) ?? obj?.ToString();
/// <summary>Gets a formatted string representation of a property value.</summary>
/// <param name="obj">A object that contains the type and value of the property.</param>
/// <param name="pdfFlags">One or more of the PROPDESC_FORMAT_FLAGS flags, which are either bitwise or multiple values, that indicate the property string format.</param>
/// <returns>The formatted value.</returns>
internal string FormatForDisplay(PROPVARIANT pv, PROPDESC_FORMAT_FLAGS pdfFlags = PROPDESC_FORMAT_FLAGS.PDFF_DEFAULT) => iDesc?.FormatForDisplay(pv, pdfFlags) ?? pv?.ToString();
/// <summary>Gets the image location for a value.</summary>
/// <param name="propvar">The value.</param>
/// <param name="obj">The value.</param>
/// <returns>An IconLocation for the image associated with the property value.</returns>
public IconLocation GetImageLocationForValue(PROPVARIANT propvar)
public IconLocation GetImageLocationForValue(object obj)
{
if (iDesc2 == null) iDesc2 = iDesc as IPropertyDescription2;
return IconLocation.TryParse(iDesc2?.GetImageReferenceForValue(propvar), out var loc) ? loc : new IconLocation();
return iDesc2 != null && IconLocation.TryParse(iDesc2.GetImageReferenceForValue(new PROPVARIANT(obj), out var img).Succeeded ? img : null, out var loc) ? loc : new IconLocation();
}
/// <summary>Compares two property values in the manner specified by the property description. Returns two display strings that describe how the two properties compare.</summary>
/// <param name="propvar1">A reference to a PROPVARIANT structure that contains the type and value of the first property.</param>
/// <param name="propvar2">A reference to a PROPVARIANT structure that contains the type and value of the second property.</param>
public Tuple<string, string> GetRelativeDescription(PROPVARIANT propvar1, PROPVARIANT propvar2)
/// <param name="obj1">An object for the first property.</param>
/// <param name="obj2">An object for the second property.</param>
public Tuple<string, string> GetRelativeDescription(object obj1, object obj2)
{
iDesc.GetRelativeDescription(propvar1, propvar2, out var d1, out var d2);
string d1 = null, d2 = null;
iDesc?.GetRelativeDescription(new PROPVARIANT(obj1), new PROPVARIANT(obj2), out d1, out d2);
return new Tuple<string, string>(d1, d2);
}
/// <summary>Gets the localized display string that describes the current sort order.</summary>
/// <param name="descending">TRUE if ppszDescription should reference the string "Z on top"; FALSE to reference the string "A on top".</param>
/// <returns>When this method returns, contains the address of a pointer to the sort description as a null-terminated Unicode string.</returns>
public string GetSortDescriptionLabel(bool descending = false) => iDesc.GetSortDescriptionLabel(descending);
public string GetSortDescriptionLabel(bool descending = false) => iDesc?.GetSortDescriptionLabel(descending);
/// <summary>Gets a value that indicates whether a property is canonical according to the definition of the property description.</summary>
/// <param name="propvar">A PROPVARIANT that contains the type and value of the property.</param>
public bool IsValueCanonical(PROPVARIANT propvar) => iDesc.IsValueCanonical(propvar).Succeeded;
public bool IsValueCanonical(PROPVARIANT propvar) => iDesc?.IsValueCanonical(propvar).Succeeded ?? false;
/// <summary>Returns a <see cref="System.String" /> that represents this instance.</summary>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
public override string ToString() => CanonicalName;
public IPropertyDescription Raw => iDesc;
}
/// <summary>Exposes methods that extract information from a collection of property descriptions presented as a list.</summary>
@ -148,18 +174,25 @@ namespace Vanara.Windows.Shell
/// <param name="list">The COM interface pointer.</param>
internal protected PropertyDescriptionList(IPropertyDescriptionList list)
{
iList = list ?? throw new ArgumentNullException(nameof(list));
iList = list;
}
/// <summary>Gets the number of elements in the collection.</summary>
/// <value>The number of elements in the collection.</value>
public virtual int Count => (int)iList.GetCount();
public virtual int Count => (int)(iList?.GetCount() ?? 0);
/// <summary>Gets the <see cref="PropertyDescription"/> at the specified index.</summary>
/// <value>The <see cref="PropertyDescription"/>.</value>
/// <param name="index">The index.</param>
/// <returns>The <see cref="PropertyDescription"/> at the specified index.</returns>
public virtual PropertyDescription this[int index] => new PropertyDescription(iList.GetAt((uint)index, typeof(IPropertyDescription).GUID));
public virtual PropertyDescription this[int index] =>
new PropertyDescription(iList?.GetAt((uint)index, typeof(IPropertyDescription).GUID));
/// <summary>Gets the <see cref="PropertyDescription"/> for the specified key.</summary>
/// <value>The <see cref="PropertyDescription"/>.</value>
/// <param name="index">The PROPERTYKEY.</param>
/// <returns>The <see cref="PropertyDescription"/> for the specified key.</returns>
public virtual PropertyDescription this[PROPERTYKEY propkey] => new PropertyDescription(propkey);
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
public virtual void Dispose()
@ -202,16 +235,16 @@ namespace Vanara.Windows.Shell
/// <param name="type">The IPropertyEnumType object.</param>
internal protected PropertyType(IPropertyEnumType type)
{
iType = type ?? throw new ArgumentNullException(nameof(type));
iType = type;
}
/// <summary>Gets the display text.</summary>
/// <value>The display text.</value>
public string DisplayText => iType.GetDisplayText();
public string DisplayText { get { try { iType.GetDisplayText(out var s); return s; } catch { return null; } } }
/// <summary>Gets an enumeration type.</summary>
/// <value>The enumeration type.</value>
public PROPENUMTYPE EnumType => iType.GetEnumType();
public PROPENUMTYPE EnumType => iType?.GetEnumType() ?? 0;
/// <summary>Gets the image reference.</summary>
/// <value>The image reference.</value>
@ -220,38 +253,41 @@ namespace Vanara.Windows.Shell
get
{
if (iType2 == null) iType2 = iType as IPropertyEnumType2;
return IconLocation.TryParse(iType2?.GetImageReference(), out var loc) ? loc : new IconLocation();
string img = null;
return IconLocation.TryParse(iType2?.GetImageReference(out img).Succeeded ?? false ? img : null, out var loc) ? loc : new IconLocation();
}
}
/// <summary>Gets a minimum value.</summary>
/// <value>The minimum value.</value>
public PROPVARIANT RangeMinValue => iType.GetRangeMinValue();
public object RangeMinValue { get { try { var t = new PROPVARIANT(); iType.GetRangeMinValue(t); return t.Value; } catch { return null; } } }
/// <summary>Gets a set value.</summary>
/// <value>The set value.</value>
public PROPVARIANT RangeSetValue => iType.GetRangeSetValue();
public object RangeSetValue { get { try { var t = new PROPVARIANT(); iType.GetRangeSetValue(t); return t.Value; } catch { return null; } } }
/// <summary>Gets a value.</summary>
/// <value>The value.</value>
public PROPVARIANT Value => iType.GetValue();
/// <value>The value.</value>EnumType != PROPENUMTYPE.PET_DEFAULTVALUE ?
public object Value { get { try { var t = new PROPVARIANT(); iType.GetValue(t); return t.Value; } catch { return null; } } }
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
public virtual void Dispose()
{
if (iType != null)
{
Marshal.ReleaseComObject(iType);
iType = null;
}
if (iType2 != null)
{
Marshal.ReleaseComObject(iType2);
iType2 = null;
}
if (iType != null)
{
Marshal.ReleaseComObject(iType);
iType = null;
}
}
/// <summary>Returns a <see cref="System.String" /> that represents this instance.</summary>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
public override string ToString() => DisplayText ?? "";
}
/// <summary>Exposes methods that enumerate the possible values for a property.</summary>
@ -266,22 +302,20 @@ namespace Vanara.Windows.Shell
/// <param name="list">The IPropertyEnumTypeList object.</param>
internal protected PropertyTypeList(IPropertyEnumTypeList list)
{
iList = list ?? throw new ArgumentNullException(nameof(list));
iList = list;
}
/// <summary>Gets the number of elements in the collection.</summary>
/// <value>The number of elements in the collection.</value>
public virtual int Count => (int)iList.GetCount();
public virtual int Count => (int)(iList?.GetCount() ?? 0);
/// <summary>Gets the <see cref="PropertyType"/> at the specified index.</summary>
/// <value>The <see cref="PropertyType"/>.</value>
/// <param name="index">The index.</param>
/// <returns>The <see cref="PropertyType"/> at the specified index.</returns>
public virtual PropertyType this[int index] => new PropertyType(iList.GetAt((uint)index, typeof(IPropertyDescription).GUID));
public virtual PropertyType this[int index] => new PropertyType(iList?.GetAt((uint)index, typeof(IPropertyEnumType).GUID));
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
public virtual void Dispose()
{
if (iList != null)
@ -292,9 +326,9 @@ namespace Vanara.Windows.Shell
}
/// <summary>Determines the index of a specific item in the list.</summary>
/// <param name="pv">The object to locate in the list.</param>
/// <param name="obj">The object to locate in the list.</param>
/// <returns>The index of item if found in the list; otherwise, -1.</returns>
public virtual int IndexOf(PROPVARIANT pv) => iList.FindMatchingIndex(pv, out var idx).Succeeded ? (int)idx : -1;
public virtual int IndexOf(object obj) => iList == null ? -1 : (iList.FindMatchingIndex(new PROPVARIANT(obj), out var idx).Succeeded ? (int)idx : -1);
/// <summary>Returns an enumerator that iterates through the collection.</summary>
/// <returns>

View File

@ -2,7 +2,6 @@
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.InteropServices;
using Vanara.PInvoke;
using static Vanara.PInvoke.Ole32;
@ -88,6 +87,8 @@ namespace Vanara.Windows.Shell
{
if (iprops == null)
throw new InvalidOperationException("Property store does not exist.");
if (IsReadOnly)
throw new InvalidOperationException("Property store is read-only.");
iprops.SetValue(key, new PROPVARIANT(value));
OnPropertyChanged(key.ToString());
}
@ -138,8 +139,7 @@ namespace Vanara.Windows.Shell
public void CopyTo(KeyValuePair<PROPERTYKEY, object>[] array, int arrayIndex)
{
if (array.Length < (arrayIndex + Count))
throw new ArgumentOutOfRangeException(nameof(arrayIndex),
"The number of items exceeds the length of the supplied array.");
throw new ArgumentOutOfRangeException(nameof(arrayIndex), "The number of items exceeds the length of the supplied array.");
if (array == null)
throw new ArgumentNullException(nameof(array));
var i = arrayIndex;
@ -160,7 +160,7 @@ namespace Vanara.Windows.Shell
/// <summary>Returns an enumerator that iterates through the collection.</summary>
/// <returns>A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.</returns>
public IEnumerator<KeyValuePair<PROPERTYKEY, object>> GetEnumerator() => Keys.Select(k => new KeyValuePair<PROPERTYKEY, object>(k, this[k])).GetEnumerator();
public IEnumerator<KeyValuePair<PROPERTYKEY, object>> GetEnumerator() => Enum().GetEnumerator();
/// <summary>Gets the property.</summary>
/// <typeparam name="TVal">The type of the value.</typeparam>
@ -174,6 +174,29 @@ namespace Vanara.Windows.Shell
return ret;
}
/// <summary>Gets the string value of the property.</summary>
/// <param name="key">The key.</param>
/// <param name="flags">The formatting flags.</param>
/// <returns>The string value of the property.</returns>
/// <exception cref="ArgumentOutOfRangeException">key</exception>
public string GetPropertyString(PROPERTYKEY key, PROPDESC_FORMAT_FLAGS flags = PROPDESC_FORMAT_FLAGS.PDFF_DEFAULT)
{
if (!TryGetValue(key, out PROPVARIANT ret))
throw new ArgumentOutOfRangeException(nameof(key));
return new PropertyDescription(key).FormatForDisplay(ret, flags);
}
/// <summary>Gets the PROPVARIANT value for a key.</summary>
/// <param name="key">The key.</param>
/// <returns>The PROPVARIANT value.</returns>
/// <exception cref="ArgumentOutOfRangeException">key</exception>
public PROPVARIANT GetPropVariant(PROPERTYKEY key)
{
if (!TryGetValue(key, out PROPVARIANT ret))
throw new ArgumentOutOfRangeException(nameof(key));
return ret;
}
/// <summary>Gets the value associated with the specified key.</summary>
/// <param name="key">The key whose value to get.</param>
/// <param name="value">
@ -185,22 +208,9 @@ namespace Vanara.Windows.Shell
/// </returns>
public bool TryGetValue(PROPERTYKEY key, out object value)
{
if (iprops != null)
{
try
{
var pv = new PROPVARIANT();
iprops.GetValue(ref key, pv);
if (pv.VarType != VarEnum.VT_EMPTY)
{
value = pv.Value;
return true;
}
}
catch { }
}
value = null;
return false;
var ret = TryGetValue(key, out PROPVARIANT pv);
value = ret ? pv.Value : null;
return ret;
}
/// <summary>Gets the value associated with the specified key.</summary>
@ -215,8 +225,8 @@ namespace Vanara.Windows.Shell
/// </returns>
public bool TryGetValue<TVal>(PROPERTYKEY key, out TVal value)
{
var ret = TryGetValue(key, out var val);
value = ret ? (TVal)val : default(TVal);
var ret = TryGetValue(key, out PROPVARIANT val);
value = ret ? (TVal)val.Value : default(TVal);
return ret;
}
@ -260,5 +270,32 @@ namespace Vanara.Windows.Shell
IsDirty = true;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private IEnumerable<KeyValuePair<PROPERTYKEY, object>> Enum()
{
for (uint i = 0; i < Count; i++)
{
var k = iprops.GetAt(i);
yield return new KeyValuePair<PROPERTYKEY, object>(k, this[k]);
}
yield break;
}
private bool TryGetValue(PROPERTYKEY key, out PROPVARIANT value)
{
if (iprops != null)
{
try
{
var pv = new PROPVARIANT();
iprops.GetValue(ref key, pv);
value = pv;
return true;
}
catch { }
}
value = null;
return false;
}
}
}

View File

@ -337,7 +337,6 @@ namespace Vanara.Windows.Shell
SingleLine = 0x00000010,
}
// TODO: object GetPropertyDescriptionList(IntPtr keyType, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid);
// TODO: object GetPropertyStoreForKeys(IntPtr rgKeys, uint cKeys, GPS flags, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid);
// TODO: object GetPropertyStoreWithCreateObject(GPS flags, object punkCreateObject, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid);
/// <summary>Encapsulates an item in the Windows Shell.</summary>
@ -354,7 +353,8 @@ namespace Vanara.Windows.Shell
internal IShellItem2 iShellItem2;
private static Dictionary<Type, BHID> bhidLookup;
private IQueryInfo qi;
private ShellItemPropertyStore values;
private ShellItemPropertyStore props;
private PropertyDescriptionList propDescList;
/// <summary>Initializes a new instance of the <see cref="ShellItem"/> class.</summary>
/// <param name="path">The file system path of the item.</param>
@ -408,15 +408,8 @@ namespace Vanara.Windows.Shell
/// <summary>Occurs when a property value changes.</summary>
public event PropertyChangedEventHandler PropertyChanged
{
add
{
((INotifyPropertyChanged)Properties).PropertyChanged += value;
}
remove
{
((INotifyPropertyChanged)Properties).PropertyChanged -= value;
}
add { ((INotifyPropertyChanged)Properties).PropertyChanged += value; }
remove { ((INotifyPropertyChanged)Properties).PropertyChanged -= value; }
}
/// <summary>Gets the attributes for the Shell item.</summary>
@ -481,7 +474,11 @@ namespace Vanara.Windows.Shell
/// <summary>Gets the property store for the item.</summary>
/// <value>The dictionary of properties.</value>
public ShellItemPropertyStore Properties => values ?? (values = new ShellItemPropertyStore(this));
public ShellItemPropertyStore Properties => props ?? (props = new ShellItemPropertyStore(this));
/// <summary>Gets a property description list object containing descriptions of all properties.</summary>
/// <returns>A complete <see cref="PropertyDescriptionList"/> instance.</returns>
public PropertyDescriptionList PropertyDescriptions => propDescList ?? (propDescList = GetPropertyDescriptionList(PROPERTYKEY.System.PropList.FullDetails));
/// <summary>Gets the normal tool tip text associated with this item.</summary>
/// <value>The tool tip text.</value>
@ -526,7 +523,8 @@ namespace Vanara.Windows.Shell
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
public virtual void Dispose()
{
values?.Dispose();
if (props != null) { props?.Dispose(); props = null; }
if (propDescList != null) { propDescList?.Dispose(); propDescList = null; }
if (qi != null) { Marshal.ReleaseComObject(qi); qi = null; }
if (iShellItem2 != null) { Marshal.ReleaseComObject(iShellItem2); iShellItem2 = null; }
if (iShellItem != null) { Marshal.ReleaseComObject(iShellItem); iShellItem = null; }
@ -595,10 +593,6 @@ namespace Vanara.Windows.Shell
throw new InvalidOperationException("Unable to retrieve an image for this item.");
}
/// <summary>Gets a property description list object containing descriptions of all properties.</summary>
/// <returns>A complete <see cref="PropertyDescriptionList"/> instance.</returns>
public PropertyDescriptionList GetPropertyDescriptionList() => GetPropertyDescriptionList(PROPERTYKEY.System.PropList.FullDetails);
/// <summary>Gets a property description list object given a reference to a property key.</summary>
/// <param name="keyType">A reference to a PROPERTYKEY structure. The values in <see cref="PROPERTYKEY.System.PropList"/> are all valid. <see cref="PROPERTYKEY.System.PropList.FullDetails"/> will return all properties.</param>
/// <returns>A <see cref="PropertyDescriptionList"/> instance for the supplied key.</returns>
@ -631,7 +625,7 @@ namespace Vanara.Windows.Shell
/// <summary>Ensures that all cached information for this item is updated.</summary>
public void Update()
{
values?.Commit();
props?.Commit();
ThrowIfNoShellItem2();
iShellItem2.Update(BindContext);
}

View File

@ -7,11 +7,18 @@ using static Vanara.PInvoke.PropSys;
namespace Vanara.Windows.Shell
{
/// <summary>A property store for a <see cref="ShellItem"/>.</summary>
/// <seealso cref="Vanara.Windows.Shell.PropertyStore"/>
public sealed class ShellItemPropertyStore : PropertyStore
{
/// <summary>The shell item</summary>
private readonly ShellItem shellItem;
/// <summary>The flags.</summary>
private GETPROPERTYSTOREFLAGS flags = GETPROPERTYSTOREFLAGS.GPS_DEFAULT;
/// <summary>Initializes a new instance of the <see cref="ShellItemPropertyStore"/> class.</summary>
/// <param name="item">The ShellItem instance.</param>
/// <param name="propChangedHandler">The optional property changed handler.</param>
internal ShellItemPropertyStore(ShellItem item, PropertyChangedEventHandler propChangedHandler = null)
{
shellItem = item;
@ -20,17 +27,9 @@ namespace Vanara.Windows.Shell
PropertyChanged += propChangedHandler;
}
[DefaultValue(false)]
public bool NoInheritedProperties
{
get => flags.IsFlagSet(GETPROPERTYSTOREFLAGS.GPS_HANDLERPROPERTIESONLY);
set
{
if (NoInheritedProperties == value) return;
flags = flags.SetFlags(GETPROPERTYSTOREFLAGS.GPS_HANDLERPROPERTIESONLY, value);
Refresh();
}
}
/// <summary>Gets a property description list object containing descriptions of all properties.</summary>
/// <returns>A complete <see cref="PropertyDescriptionList"/> instance.</returns>
public PropertyDescriptionList Descriptions => shellItem.PropertyDescriptions;
/// <summary>Gets or sets a value indicating whether to include slow properties.</summary>
/// <value><c>true</c> if including slow properties; otherwise, <c>false</c>.</value>
@ -42,12 +41,31 @@ namespace Vanara.Windows.Shell
{
if (IncludeSlow == value) return;
flags = flags.SetFlags(GETPROPERTYSTOREFLAGS.GPS_OPENSLOWITEM, value);
if (value)
flags = flags.SetFlags(GETPROPERTYSTOREFLAGS.GPS_TEMPORARY | GETPROPERTYSTOREFLAGS.GPS_FASTPROPERTIESONLY, false);
Refresh();
}
}
/// <summary>Gets a value indicating whether the <see cref="ICollection{T}" /> is read-only.</summary>
public override bool IsReadOnly => ReadOnly;
/// <summary>Gets or sets a value indicating whether to include only properties directly from the property handler.</summary>
/// <value><c>true</c> if no inherited properties; otherwise, <c>false</c>.</value>
[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);
Refresh();
}
}
/// <summary>Gets or sets a value indicating whether properties can be read and written.</summary>
/// <value><c>true</c> if properties are read/write; otherwise, <c>false</c>.</value>
[DefaultValue(true)]
@ -59,11 +77,18 @@ namespace Vanara.Windows.Shell
if (ReadOnly == value) return;
flags = flags.SetFlags(GETPROPERTYSTOREFLAGS.GPS_READWRITE, !value);
if (!value)
flags = flags.SetFlags(GETPROPERTYSTOREFLAGS.GPS_DELAYCREATION | GETPROPERTYSTOREFLAGS.GPS_TEMPORARY, false);
flags = flags.SetFlags(GETPROPERTYSTOREFLAGS.GPS_DELAYCREATION | GETPROPERTYSTOREFLAGS.GPS_TEMPORARY | GETPROPERTYSTOREFLAGS.GPS_BESTEFFORT | GETPROPERTYSTOREFLAGS.GPS_FASTPROPERTIESONLY, false);
else
flags = flags.SetFlags(GETPROPERTYSTOREFLAGS.GPS_HANDLERPROPERTIESONLY);
Refresh();
}
}
/// <summary>
/// Gets or sets a value indicating whether this <see cref="ShellItemPropertyStore"/> 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..
/// </summary>
/// <value><c>true</c> if temporary; otherwise, <c>false</c>.</value>
[DefaultValue(false)]
public bool Temporary
{
@ -74,10 +99,14 @@ namespace Vanara.Windows.Shell
flags = flags.SetFlags(GETPROPERTYSTOREFLAGS.GPS_TEMPORARY, value);
if (value)
{
flags = flags.SetFlags(GETPROPERTYSTOREFLAGS.GPS_READWRITE, true);
flags = flags.SetFlags(GETPROPERTYSTOREFLAGS.GPS_DELAYCREATION | GETPROPERTYSTOREFLAGS.GPS_OPENSLOWITEM, false);
flags = GETPROPERTYSTOREFLAGS.GPS_TEMPORARY;
ReadOnly = false;
}
else
{
flags = flags.SetFlags(GETPROPERTYSTOREFLAGS.GPS_TEMPORARY, false);
Refresh();
}
Refresh();
}
}
@ -90,6 +119,7 @@ namespace Vanara.Windows.Shell
return shellItem.iShellItem2.GetCLSID(ref propertyKey);
}
/// <summary>Refreshes this instance. This call is intended for internal use only and should not need to be called.</summary>
public void Refresh()
{
shellItem.ThrowIfNoShellItem2();