mirror of https://github.com/dahall/Vanara.git
First pass at adding support for Windows Search
parent
4097090539
commit
8302691c8e
|
@ -1,8 +1,8 @@
|
|||
using System;
|
||||
using NUnit.Framework;
|
||||
using static Vanara.PInvoke.Shell32;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using static Vanara.PInvoke.Shell32;
|
||||
|
||||
namespace Vanara.Windows.Shell.Tests
|
||||
{
|
||||
|
@ -10,17 +10,13 @@ namespace Vanara.Windows.Shell.Tests
|
|||
public class ShellFolderTests
|
||||
{
|
||||
private const string testFile = ShellItemTests.testDoc;
|
||||
private static string testFld = Path.GetDirectoryName(testFile);
|
||||
private static readonly string testFld = Path.GetDirectoryName(testFile);
|
||||
|
||||
[Test]
|
||||
public void ShellFolderTest1()
|
||||
{
|
||||
Assert.That(() =>
|
||||
{
|
||||
var i = new ShellFolder(testFld);
|
||||
Assert.That(i.FileSystemPath, Is.EqualTo(testFld));
|
||||
}, Throws.Nothing);
|
||||
Assert.That(() => new ShellFolder((string) null), Throws.Exception);
|
||||
Assert.That(() => { Assert.That(new ShellFolder(testFld).FileSystemPath, Is.EqualTo(testFld)); }, Throws.Nothing);
|
||||
Assert.That(() => new ShellFolder((string)null), Throws.Exception);
|
||||
Assert.That(() => new ShellFolder(@"C:\Tamp"), Throws.Exception);
|
||||
Assert.That(() => new ShellFolder(testFile), Throws.Nothing);
|
||||
}
|
||||
|
@ -41,12 +37,8 @@ namespace Vanara.Windows.Shell.Tests
|
|||
[Test]
|
||||
public void ShellFolderTest3()
|
||||
{
|
||||
var pidl = new PIDL(testFld);
|
||||
Assert.That(() =>
|
||||
{
|
||||
var i = new ShellFolder(pidl);
|
||||
Assert.That(i.FileSystemPath, Is.EqualTo(testFld));
|
||||
}, Throws.Nothing);
|
||||
using var pidl = new PIDL(testFld);
|
||||
Assert.That(() => { Assert.That(new ShellFolder(pidl).FileSystemPath, Is.EqualTo(testFld)); }, Throws.Nothing);
|
||||
Assert.That(() => new ShellFolder((PIDL)null), Throws.Exception);
|
||||
Assert.That(() => new ShellFolder(new PIDL(@"C:\Tamp")), Throws.Exception);
|
||||
Assert.That(() => new ShellFolder(new PIDL(testFile)), Throws.Nothing);
|
||||
|
@ -55,12 +47,8 @@ namespace Vanara.Windows.Shell.Tests
|
|||
[Test]
|
||||
public void ShellFolderTest4()
|
||||
{
|
||||
Assert.That(() =>
|
||||
{
|
||||
var i = new ShellFolder(new ShellItem(testFld));
|
||||
Assert.That(i.FileSystemPath, Is.EqualTo(testFld));
|
||||
}, Throws.Nothing);
|
||||
Assert.That(() => new ShellFolder((ShellItem) null), Throws.Exception);
|
||||
Assert.That(() => { Assert.That(new ShellFolder(new ShellItem(testFld)).FileSystemPath, Is.EqualTo(testFld)); }, Throws.Nothing);
|
||||
Assert.That(() => new ShellFolder((ShellItem)null), Throws.Exception);
|
||||
Assert.That(() => new ShellFolder(new ShellItem(testFile)), Throws.Nothing);
|
||||
}
|
||||
|
||||
|
@ -69,21 +57,19 @@ namespace Vanara.Windows.Shell.Tests
|
|||
{
|
||||
using (var si = new ShellItem(testFile))
|
||||
{
|
||||
using (var i = new ShellFolder(testFld))
|
||||
{
|
||||
Assert.That(i[Path.GetFileName(testFile)], Is.EqualTo(si));
|
||||
Assert.That(() => i[testFile], Throws.Exception);
|
||||
Assert.That(() => i[(string)null], Throws.Exception);
|
||||
Assert.That(() => i[""], Throws.Exception);
|
||||
Assert.That(() => i["bad.bad"], Throws.Exception);
|
||||
using var i = new ShellFolder(testFld);
|
||||
Assert.That(i[Path.GetFileName(testFile)], Is.EqualTo(si));
|
||||
Assert.That(() => i[testFile], Throws.Exception);
|
||||
Assert.That(() => i[(string)null], Throws.Exception);
|
||||
Assert.That(() => i[""], Throws.Exception);
|
||||
Assert.That(() => i["bad.bad"], Throws.Exception);
|
||||
|
||||
using (var pidl = new PIDL(testFile))
|
||||
{
|
||||
Assert.That(i[pidl.LastId], Is.EqualTo(si));
|
||||
Assert.That(i[pidl], Is.EqualTo(si));
|
||||
}
|
||||
Assert.That(() => i[(PIDL)null], Throws.Exception);
|
||||
using (var pidl = new PIDL(testFile))
|
||||
{
|
||||
Assert.That(i[pidl.LastId], Is.EqualTo(si));
|
||||
Assert.That(i[pidl], Is.EqualTo(si));
|
||||
}
|
||||
Assert.That(() => i[(PIDL)null], Throws.Exception);
|
||||
}
|
||||
using (var i = new ShellFolder(KNOWNFOLDERID.FOLDERID_Desktop))
|
||||
Assert.That(i, Is.EqualTo(ShellFolder.Desktop));
|
||||
|
@ -99,34 +85,27 @@ namespace Vanara.Windows.Shell.Tests
|
|||
var ie2 = ie1.EnumerateChildren(FolderItemFilter.NonFolders);
|
||||
Assert.That(ie1.Intersect(ie2).OrderBy(s => s.Name), Is.EquivalentTo(ie2.OrderBy(s => s.Name)));
|
||||
}
|
||||
using (var d = new ShellFolder(@"C:\"))
|
||||
{
|
||||
using (var libs = (ShellFolder)d["Temp"])
|
||||
{
|
||||
Assert.That(libs, Is.Not.Null.And.InstanceOf<ShellFolder>());
|
||||
using (var lnk = libs["Test.lnk"])
|
||||
Assert.That(lnk, Is.Not.Null.And.InstanceOf<ShellLink>());
|
||||
}
|
||||
}
|
||||
using var d = new ShellFolder(@"C:\");
|
||||
using var libs = (ShellFolder)d["Temp"];
|
||||
Assert.That(libs, Is.Not.Null.And.InstanceOf<ShellFolder>());
|
||||
using var lnk = libs["Test.lnk"];
|
||||
Assert.That(lnk, Is.Not.Null.And.InstanceOf<ShellLink>());
|
||||
}, Throws.Nothing);
|
||||
Assert.That(() => new ShellFolder(KNOWNFOLDERID.FOLDERID_Windows).EnumerateChildren((FolderItemFilter)0x80000), Is.Empty);
|
||||
Assert.That(() => new ShellFolder(KNOWNFOLDERID.FOLDERID_ComputerFolder).EnumerateChildren(), Is.Not.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetObjectTest()
|
||||
{
|
||||
using (var f = new ShellFolder(testFld))
|
||||
using (var i = new ShellItem(testFile))
|
||||
{
|
||||
var qi = f.GetChildrenUIObjects<IQueryInfo>(null, i);
|
||||
Assert.That(qi, Is.Not.Null.And.InstanceOf<IQueryInfo>());
|
||||
System.Runtime.InteropServices.Marshal.ReleaseComObject(qi);
|
||||
var sv = f.GetViewObject<IShellView>(null);
|
||||
Assert.That(sv, Is.Not.Null.And.InstanceOf<IShellView>());
|
||||
Assert.That(() => f.GetChildrenUIObjects<IShellLibrary>(null, i), Throws.TypeOf<NotImplementedException>());
|
||||
Assert.That(() => f.GetViewObject<IShellLibrary>(null), Throws.TypeOf<NotImplementedException>());
|
||||
}
|
||||
using var f = new ShellFolder(testFld);
|
||||
using var i = new ShellItem(testFile);
|
||||
var qi = f.GetChildrenUIObjects<IQueryInfo>(null, i);
|
||||
Assert.That(qi, Is.Not.Null.And.InstanceOf<IQueryInfo>());
|
||||
System.Runtime.InteropServices.Marshal.ReleaseComObject(qi);
|
||||
var sv = f.GetViewObject<IShellView>(null);
|
||||
Assert.That(sv, Is.Not.Null.And.InstanceOf<IShellView>());
|
||||
Assert.That(() => f.GetChildrenUIObjects<IShellLibrary>(null, i), Throws.TypeOf<NotImplementedException>());
|
||||
Assert.That(() => f.GetViewObject<IShellLibrary>(null), Throws.TypeOf<NotImplementedException>());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Vanara.InteropServices;
|
||||
using Vanara.PInvoke;
|
||||
using Vanara.PInvoke.Tests;
|
||||
using static Vanara.PInvoke.Shell32;
|
||||
|
||||
namespace Vanara.Windows.Shell.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class ShellSearchTests
|
||||
{
|
||||
[Test]
|
||||
public void SearchTests()
|
||||
{
|
||||
using var c = SearchCondition.CreateFromStructuredQuery("customer *.pptx");
|
||||
GetResults(c);
|
||||
using var shf = ShellSearch.GetSearchResults(c, "Test", new[] { ShellFolder.Desktop }) as ShellFolder;
|
||||
var i = 50;
|
||||
foreach (var item in shf)
|
||||
if (--i > 0)
|
||||
TestContext.WriteLine(item.FileSystemPath);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConditionTest()
|
||||
{
|
||||
using var c = SearchCondition.CreateFromStructuredQuery("LONG kind:text");
|
||||
GetResults(c);
|
||||
}
|
||||
|
||||
private static void GetResults(SearchCondition c)
|
||||
{
|
||||
foreach (var r in c.GetLeveledResults())
|
||||
TestContext.WriteLine(r);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -46,6 +46,7 @@
|
|||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ShellSearchTests.cs" />
|
||||
<Compile Include="ControlPanelTests.cs" />
|
||||
<Compile Include="ShellAssocTests.cs" />
|
||||
<Compile Include="ShellFileOperationsTests.cs" />
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Vanara.InteropServices;
|
||||
using Vanara.PInvoke;
|
||||
using static Vanara.PInvoke.Ole32;
|
||||
using static Vanara.PInvoke.SearchApi;
|
||||
using static Vanara.PInvoke.Shell32;
|
||||
|
||||
namespace Vanara.Windows.Shell
|
||||
{
|
||||
/// <summary>Represents functionality of the Windows Search Service.</summary>
|
||||
public static class ShellSearch
|
||||
{
|
||||
private const string systemCatalog = "SystemIndex";
|
||||
private static readonly ISearchCatalogManager catMgr;
|
||||
private static readonly ISearchManager mgr = new ISearchManager();
|
||||
private static readonly IQueryParserManager queryMgr = new IQueryParserManager();
|
||||
|
||||
static ShellSearch() => catMgr = mgr.GetCatalog(systemCatalog);
|
||||
|
||||
public static ShellItem GetSearchResults(SearchCondition condition, string displayName, IEnumerable<ShellFolder> searchFolders, ShellSearchViewSettings settings = null)
|
||||
{
|
||||
using var cfactory = ComReleaserFactory.Create(new ISearchFolderItemFactory());
|
||||
cfactory.Item.SetPaths(searchFolders);
|
||||
return GetSearchResults(cfactory.Item, condition, displayName, settings);
|
||||
}
|
||||
|
||||
public static ShellItem GetSearchResults(SearchCondition condition, string displayName, IEnumerable<string> searchFolders, ShellSearchViewSettings settings = null)
|
||||
{
|
||||
using var cfactory = ComReleaserFactory.Create(new ISearchFolderItemFactory());
|
||||
cfactory.Item.SetPaths(searchFolders);
|
||||
return GetSearchResults(cfactory.Item, condition, displayName, settings);
|
||||
}
|
||||
|
||||
private static ShellItem GetSearchResults(ISearchFolderItemFactory factory, SearchCondition condition, string displayName, ShellSearchViewSettings settings)
|
||||
{
|
||||
factory.SetDisplayName(displayName ?? "");
|
||||
factory.SetCondition(condition?.condition ?? throw new ArgumentNullException(nameof(condition)));
|
||||
if (settings != null)
|
||||
{
|
||||
factory.SetFolderTypeID(settings.FolderTypeID.Guid());
|
||||
if (settings.FolderLogicalViewMode.HasValue) factory.SetFolderLogicalViewMode(settings.FolderLogicalViewMode.Value);
|
||||
if (settings.IconSize.HasValue) factory.SetIconSize(settings.IconSize.Value);
|
||||
if (settings.VisibleColumns != null) factory.SetVisibleColumns((uint)settings.VisibleColumns.Length, settings.VisibleColumns);
|
||||
if (settings.SortColumns != null) factory.SetSortColumns((uint)settings.SortColumns.Length, settings.SortColumns);
|
||||
if (settings.GroupColumn.HasValue) factory.SetGroupColumn(settings.GroupColumn.Value);
|
||||
if (settings.StackKeys != null) factory.SetStacks((uint)settings.StackKeys.Length, settings.StackKeys);
|
||||
}
|
||||
return factory.GetShellItem();
|
||||
}
|
||||
|
||||
private static ShellItem GetShellItem(this ISearchFolderItemFactory f) => ShellItem.Open(f.GetShellItem<IShellItem>());
|
||||
|
||||
private static void SetPaths(this ISearchFolderItemFactory f, IEnumerable<string> paths) =>
|
||||
SetPaths(f, paths.Select(p => new ShellFolder(p)));
|
||||
|
||||
private static void SetPaths(this ISearchFolderItemFactory f, IEnumerable<ShellFolder> paths)
|
||||
{
|
||||
var pa = paths?.ToArray();
|
||||
if (pa != null && pa.Length > 0)
|
||||
{
|
||||
SHCreateShellItemArrayFromIDLists((uint)pa.Length, Array.ConvertAll(pa, i => (IntPtr)i.PIDL), out var ia).ThrowIfFailed();
|
||||
f.SetScope(ia);
|
||||
}
|
||||
else
|
||||
f.SetScope(null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Settings that change the folder view of a search.</summary>
|
||||
public class ShellSearchViewSettings
|
||||
{
|
||||
/// <summary>The folder logical view mode. The default settings are based on the which is set by the FolderTypeID property.</summary>
|
||||
public FOLDERLOGICALVIEWMODE? FolderLogicalViewMode { get; set; }
|
||||
|
||||
/// <summary>The search folder type ID.</summary>
|
||||
public FOLDERTYPEID FolderTypeID { get; set; } = FOLDERTYPEID.FOLDERTYPEID_GenericLibrary;
|
||||
|
||||
/// <summary>The group column. If no group column is specified, no grouping occurs.</summary>
|
||||
public PROPERTYKEY? GroupColumn { get; set; }
|
||||
|
||||
/// <summary>The search folder icon size. The default settings are based on the which is set by the FolderTypeID property.</summary>
|
||||
public int? IconSize { get; set; }
|
||||
|
||||
/// <summary>A list of sort column directions.</summary>
|
||||
public SORTCOLUMN[] SortColumns { get; set; }
|
||||
|
||||
/// <summary>A list of stack keys, as specified. If <see langword="null"/>, the folder will not be stacked.</summary>
|
||||
public PROPERTYKEY[] StackKeys { get; set; }
|
||||
|
||||
/// <summary>A list of which columns are all visible. The default is based on <c>FolderTypeID</c>.</summary>
|
||||
public PROPERTYKEY[] VisibleColumns { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,214 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using Vanara.InteropServices;
|
||||
using Vanara.PInvoke;
|
||||
using static Vanara.PInvoke.Ole32;
|
||||
using static Vanara.PInvoke.SearchApi;
|
||||
|
||||
namespace Vanara.Windows.Shell
|
||||
{
|
||||
/// <summary>Provides properties and methods for retrieving information about a search condition.</summary>
|
||||
/// <seealso cref="System.ICloneable"/>
|
||||
public class SearchCondition : ICloneable, IDisposable
|
||||
{
|
||||
internal ICondition condition;
|
||||
private const string systemCatalog = "SystemIndex";
|
||||
|
||||
internal SearchCondition(ICondition c) => condition = c;
|
||||
|
||||
/// <summary>Gets the CLSID for the search condition.</summary>
|
||||
/// <value>The CLSID.</value>
|
||||
public Guid CLSID => condition.GetClassID();
|
||||
|
||||
/// <summary>Retrieves the property name, operation, and value from a leaf search condition node.</summary>
|
||||
/// <value>The comparison information.</value>
|
||||
public (string propName, CONDITION_OPERATION op, object propValue) ComparisonInfo
|
||||
{
|
||||
get
|
||||
{
|
||||
var pv = new PROPVARIANT();
|
||||
condition.GetComparisonInfo(out var n, out var o, pv);
|
||||
return (n, o, pv.Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the condition type for this search condition node, identifying it as a logical AND, OR, or NOT, or as a leaf node.
|
||||
/// </summary>
|
||||
public CONDITION_TYPE ConditionType => condition.GetConditionType();
|
||||
|
||||
/// <summary>Retrieves the property name, operation, and value from a leaf search condition node.</summary>
|
||||
/// <value>The leaf condition information.</value>
|
||||
public (PROPERTYKEY propKey, CONDITION_OPERATION op, object propValue) LeafConditionInfo
|
||||
{
|
||||
get
|
||||
{
|
||||
var pv = new PROPVARIANT();
|
||||
((ICondition2)condition).GetLeafConditionInfo(out var n, out var o, pv);
|
||||
return (n, o, pv.Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Retrieves the size of the stream needed to save the object.</summary>
|
||||
/// <value>The size in bytes of the stream needed to save this object, in bytes.</value>
|
||||
public long MaxSize => (long)condition.GetSizeMax();
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a collection of the subconditions of the search condition node and the IID of the interface for enumerating the collection.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A collection of zero or more SearchCondition objects. Each object is a subcondition of this condition node. If this is a negation
|
||||
/// condition, this parameter receives the single subcondition.
|
||||
/// </value>
|
||||
public IEnumerable<SearchCondition> SubConditions => condition.GetSubConditions<IEnumUnknown>().Enumerate<ICondition>().Select(ic => new SearchCondition(ic));
|
||||
|
||||
/// <summary>Retrieves the character-normalized value of the search condition node.</summary>
|
||||
/// <value>The normalized value of the condition.</value>
|
||||
public string ValueNormalization => condition.GetValueNormalization();
|
||||
|
||||
/// <summary>Retrieves the semantic type of the value of the search condition node.</summary>
|
||||
/// <value>The semantic type of the value.</value>
|
||||
public string ValueType => condition.GetValueType();
|
||||
|
||||
/// <summary>Creates a condition node that is a logical conjunction (AND) or disjunction (OR) of a collection of subconditions.</summary>
|
||||
/// <param name="conditionType">
|
||||
/// The CONDITION_TYPE of the condition node. The CONDITION_TYPE must be either CT_AND_CONDITION or CT_OR_CONDITION.
|
||||
/// </param>
|
||||
/// <param name="simplify">
|
||||
/// <see langword="true"/> to logically simplify the result, if possible; then the result will not necessarily to be of the specified
|
||||
/// kind. <see langword="false"/> if the result should have exactly the prescribed structure.
|
||||
/// <para>
|
||||
/// An application that plans to execute a query based on the condition tree would typically benefit from setting this parameter to <see langword="true"/>.
|
||||
/// </para>
|
||||
/// </param>
|
||||
/// <param name="subconditions">The SearchCondition sub-objects. This list can be left empty.</param>
|
||||
/// <returns>The new <see cref="SearchCondition"/> node.</returns>
|
||||
public static SearchCondition CreateAndOrCondition(CONDITION_TYPE conditionType, bool simplify, params SearchCondition[] subconditions)
|
||||
{
|
||||
using var ifactory = ComReleaserFactory.Create(new IConditionFactory());
|
||||
var ienumunk = new IEnumUnknownImpl<ICondition>(subconditions.Select(c => c.condition));
|
||||
var icond = ifactory.Item.MakeAndOr(conditionType, ienumunk, simplify);
|
||||
return new SearchCondition(icond);
|
||||
}
|
||||
|
||||
/// <summary>Creates a condition node from a structured query.</summary>
|
||||
/// <param name="query">An input string to be parsed.</param>
|
||||
/// <param name="cultureInfo">Used to select the localized language for keywords. By default, the current UI culture is used.</param>
|
||||
/// <returns>The new <see cref="SearchCondition"/> node.</returns>
|
||||
public static SearchCondition CreateFromStructuredQuery(string query, CultureInfo cultureInfo = null)
|
||||
{
|
||||
if (cultureInfo is null) cultureInfo = CultureInfo.CurrentUICulture;
|
||||
using var qm = ComReleaserFactory.Create(new IQueryParserManager());
|
||||
using var qp = ComReleaserFactory.Create(qm.Item.CreateLoadedParser<IQueryParser>(systemCatalog, (uint)cultureInfo.LCID));
|
||||
qm.Item.InitializeOptions(false, true, qp.Item);
|
||||
using var qs = ComReleaserFactory.Create(qp.Item.Parse(query));
|
||||
qs.Item.GetQuery(out var pc, out _);
|
||||
using var rpc = ComReleaserFactory.Create(pc);
|
||||
if (Environment.OSVersion.Version >= new Version(6, 1))
|
||||
{
|
||||
using var pcf = ComReleaserFactory.Create((IConditionFactory2)qs.Item);
|
||||
return new SearchCondition(pcf.Item.ResolveCondition<ICondition>(pc));
|
||||
}
|
||||
else
|
||||
{
|
||||
Kernel32.GetLocalTime(out var st);
|
||||
return new SearchCondition(qs.Item.Resolve(pc, STRUCTURED_QUERY_RESOLVE_OPTION.SQRO_DONT_SPLIT_WORDS, st));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Creates a leaf condition node that represents a comparison of property value and constant value.</summary>
|
||||
/// <typeparam name="T">The type of the property value.</typeparam>
|
||||
/// <param name="propertyName">
|
||||
/// The name of a property to be compared, or <see langword="null"/> for an unspecified property. The locale name of the leaf node is LOCALE_NAME_USER_DEFAULT.
|
||||
/// </param>
|
||||
/// <param name="value">The constant value against which the property value should be compared.</param>
|
||||
/// <param name="operation">A CONDITION_OPERATION enumeration.</param>
|
||||
/// <returns>The new <see cref="SearchCondition"/> node.</returns>
|
||||
public static SearchCondition CreateLeafCondition<T>(string propertyName, T value, CONDITION_OPERATION operation)
|
||||
{
|
||||
using var ifactory = ComReleaserFactory.Create(new IConditionFactory());
|
||||
if (string.IsNullOrEmpty(propertyName) || propertyName.ToUpperInvariant() == "SYSTEM.NULL")
|
||||
propertyName = null;
|
||||
var pv = new PROPVARIANT(value);
|
||||
var valType = pv.VarType switch
|
||||
{
|
||||
VarEnum.VT_I4 => "System.StructuredQuery.CustomProperty.Integer",
|
||||
VarEnum.VT_R8 => "System.StructuredQuery.CustomProperty.FloatingPoint",
|
||||
VarEnum.VT_DATE => "System.StructuredQuery.CustomProperty.DateTime",
|
||||
VarEnum.VT_BOOL => "System.StructuredQuery.CustomProperty.Boolean",
|
||||
VarEnum.VT_LPWSTR => null,
|
||||
_ => throw new ArgumentException("Type cannot be used as a condition.", nameof(value)),
|
||||
};
|
||||
var icond = ifactory.Item.MakeLeaf(propertyName, operation, valType, pv);
|
||||
return new SearchCondition(icond);
|
||||
}
|
||||
|
||||
/// <summary>Creates a condition node that is a logical negation (NOT) of another condition (a subnode of this node).</summary>
|
||||
/// <param name="conditionToNegate">The condition to negate.</param>
|
||||
/// <param name="simplify">
|
||||
/// <see langword="true"/> to logically simplify the result if possible; <see langword="false"/> otherwise. In a query builder
|
||||
/// scenario, <paramref name="simplify"/> should typically be set to <see langword="false"/>.
|
||||
/// </param>
|
||||
/// <returns>The new <see cref="SearchCondition"/> node.</returns>
|
||||
/// <exception cref="ArgumentNullException">conditionToNegate</exception>
|
||||
public static SearchCondition CreateNotCondition(SearchCondition conditionToNegate, bool simplify)
|
||||
{
|
||||
using var ifactory = ComReleaserFactory.Create(new IConditionFactory());
|
||||
var icond = ifactory.Item.MakeNot(conditionToNegate?.condition ?? throw new ArgumentNullException(nameof(conditionToNegate)), simplify);
|
||||
return new SearchCondition(icond);
|
||||
}
|
||||
|
||||
/// <summary>Creates a deep copy of this instance.</summary>
|
||||
/// <returns>A copy of this search condition.</returns>
|
||||
public SearchCondition Clone() => new SearchCondition(condition.Clone());
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (condition != null)
|
||||
{
|
||||
Marshal.ReleaseComObject(condition);
|
||||
condition = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Gets the results of the condition by level.</summary>
|
||||
/// <returns>An enumeration of the leaf nodes with their level and data.</returns>
|
||||
public IEnumerable<(int level, string propName, string semanticType, object propVar)> GetLeveledResults() => GetResults(condition);
|
||||
|
||||
private static IEnumerable<(int level, string propName, string semanticType, object propVar)> GetResults(ICondition pc, int l = 0)
|
||||
{
|
||||
switch (pc.GetConditionType())
|
||||
{
|
||||
case CONDITION_TYPE.CT_AND_CONDITION:
|
||||
case CONDITION_TYPE.CT_OR_CONDITION:
|
||||
foreach (var pcsub in pc.GetSubConditions<IEnumUnknown>().Enumerate<ICondition>().Where(i => i != null))
|
||||
foreach (var r in GetResults(pcsub, l + 1))
|
||||
yield return r;
|
||||
break;
|
||||
|
||||
case CONDITION_TYPE.CT_NOT_CONDITION:
|
||||
foreach (var r in GetResults(pc.GetSubConditions<ICondition>(), l + 1))
|
||||
yield return r;
|
||||
break;
|
||||
|
||||
case CONDITION_TYPE.CT_LEAF_CONDITION:
|
||||
var propvar = new PROPVARIANT();
|
||||
((ICondition2)pc).GetLeafConditionInfo(out var propkey, out _, propvar);
|
||||
yield return (l, propkey.GetCononicalName(), pc.GetValueType() ?? "", propvar.Value);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object ICloneable.Clone() => Clone();
|
||||
}
|
||||
}
|
|
@ -56,7 +56,6 @@ ChangeFilters, ExecutableType, FolderItemFilter, LibraryFolderFilter, LibraryVie
|
|||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="ShellObjects\~ShellSearch.cs" />
|
||||
<Compile Remove="TaskBar\AssocUtil.cs" />
|
||||
<Compile Remove="TaskBar\ImageIndexer.cs" />
|
||||
<Compile Remove="TaskBar\JumpList.cs" />
|
||||
|
@ -75,10 +74,8 @@ ChangeFilters, ExecutableType, FolderItemFilter, LibraryFolderFilter, LibraryVie
|
|||
<Compile Remove="TaskBar\~ThumbnailToolbar.cs" />
|
||||
<Compile Remove="TaskBar\~ThumbnailToolbarButton.cs" />
|
||||
<Compile Remove="TaskBar\~ThumbnailToolbarButtonCollection.cs" />
|
||||
<Compile Remove="~ShellSearchConditions.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="ShellObjects\~ShellSearch.cs" />
|
||||
<None Include="TaskBar\~AssocUtil.cs" />
|
||||
<None Include="TaskBar\~ImageIndexer.cs" />
|
||||
<None Include="TaskBar\~JumpList.cs" />
|
||||
|
@ -88,12 +85,12 @@ ChangeFilters, ExecutableType, FolderItemFilter, LibraryFolderFilter, LibraryVie
|
|||
<None Include="TaskBar\~ThumbnailToolbar.cs" />
|
||||
<None Include="TaskBar\~ThumbnailToolbarButton.cs" />
|
||||
<None Include="TaskBar\~ThumbnailToolbarButtonCollection.cs" />
|
||||
<None Include="~ShellSearchConditions.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Core\Vanara.Core.csproj" />
|
||||
<ProjectReference Include="..\PInvoke\Shared\Vanara.PInvoke.Shared.csproj" />
|
||||
<ProjectReference Include="..\PInvoke\ComCtl32\Vanara.PInvoke.ComCtl32.csproj" />
|
||||
<ProjectReference Include="..\PInvoke\Ole\Vanara.PInvoke.Ole.csproj" />
|
||||
<ProjectReference Include="..\PInvoke\Shell32\Vanara.PInvoke.Shell32.csproj" />
|
||||
<ProjectReference Include="..\PInvoke\User32\Vanara.PInvoke.User32.csproj" />
|
||||
<ProjectReference Include="..\PInvoke\SearchApi\Vanara.PInvoke.SearchApi.csproj" />
|
||||
|
|
Loading…
Reference in New Issue