diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/ContextMenu.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/ContextMenu.cs new file mode 100644 index 0000000000..34c86d9de6 --- /dev/null +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/ContextMenu.cs @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Plugin.Indexer +{ + public class ContextMenu + { + public string Name { get; set; } + + public string Command { get; set; } + + public string Argument { get; set; } + + public string ImagePath { get; set; } + } +} diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/ContextMenuLoader.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/ContextMenuLoader.cs index 000ccc036b..c6005b2bed 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/ContextMenuLoader.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/ContextMenuLoader.cs @@ -1,114 +1,118 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Windows; -using Wox.Infrastructure.Logger; -using Wox.Plugin; -using Microsoft.Plugin.Indexer.SearchHelper; -using System.Windows.Input; -using System.Reflection; -using System.Threading.Tasks; -using Wox.Infrastructure; +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. -namespace Microsoft.Plugin.Indexer -{ - internal class ContextMenuLoader : IContextMenu - { +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Input; +using Microsoft.Plugin.Indexer.SearchHelper; +using Wox.Infrastructure; +using Wox.Infrastructure.Logger; +using Wox.Plugin; + +namespace Microsoft.Plugin.Indexer +{ + internal class ContextMenuLoader : IContextMenu + { private readonly PluginInitContext _context; public enum ResultType { Folder, - File - } - - // Extensions for adding run as admin context menu item for applications - private readonly string[] appExtensions = { ".exe", ".bat", ".appref-ms", ".lnk" }; - - public ContextMenuLoader(PluginInitContext context) - { - _context = context; + File, + } + + // Extensions for adding run as admin context menu item for applications + private readonly string[] appExtensions = { ".exe", ".bat", ".appref-ms", ".lnk" }; + + public ContextMenuLoader(PluginInitContext context) + { + _context = context; } [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "We want to keep the process alive, and instead log and show an error message")] - public List LoadContextMenus(Result selectedResult) - { - var contextMenus = new List(); - if (selectedResult.ContextData is SearchResult record) - { - ResultType type = Path.HasExtension(record.Path) ? ResultType.File : ResultType.Folder; - - if (type == ResultType.File) - { - contextMenus.Add(CreateOpenContainingFolderResult(record)); - } - - // Test to check if File can be Run as admin, if yes, we add a 'run as admin' context menu item + public List LoadContextMenus(Result selectedResult) + { + var contextMenus = new List(); + if (selectedResult.ContextData is SearchResult record) + { + ResultType type = Path.HasExtension(record.Path) ? ResultType.File : ResultType.Folder; + + if (type == ResultType.File) + { + contextMenus.Add(CreateOpenContainingFolderResult(record)); + } + + // Test to check if File can be Run as admin, if yes, we add a 'run as admin' context menu item if (CanFileBeRunAsAdmin(record.Path)) { contextMenus.Add(CreateRunAsAdminContextMenu(record)); - } - - contextMenus.Add(new ContextMenuResult - { - PluginName = Assembly.GetExecutingAssembly().GetName().Name, - Title = _context.API.GetTranslation("Microsoft_plugin_indexer_copy_path"), - Glyph = "\xE8C8", - FontFamily = "Segoe MDL2 Assets", + } + + contextMenus.Add(new ContextMenuResult + { + PluginName = Assembly.GetExecutingAssembly().GetName().Name, + Title = _context.API.GetTranslation("Microsoft_plugin_indexer_copy_path"), + Glyph = "\xE8C8", + FontFamily = "Segoe MDL2 Assets", AcceleratorKey = Key.C, - AcceleratorModifiers = ModifierKeys.Control, - - Action = (context) => - { - try - { - Clipboard.SetText(record.Path); - return true; - } - catch (Exception e) - { - var message = "Fail to set text in clipboard"; - LogException(message, e); - _context.API.ShowMsg(message); - return false; - } - } - }); - contextMenus.Add(new ContextMenuResult - { - PluginName = Assembly.GetExecutingAssembly().GetName().Name, - Title = _context.API.GetTranslation("Microsoft_plugin_indexer_open_in_console"), - Glyph = "\xE756", - FontFamily = "Segoe MDL2 Assets", + AcceleratorModifiers = ModifierKeys.Control, + + Action = (context) => + { + try + { + Clipboard.SetText(record.Path); + return true; + } + catch (Exception e) + { + var message = "Fail to set text in clipboard"; + LogException(message, e); + _context.API.ShowMsg(message); + return false; + } + }, + }); + contextMenus.Add(new ContextMenuResult + { + PluginName = Assembly.GetExecutingAssembly().GetName().Name, + Title = _context.API.GetTranslation("Microsoft_plugin_indexer_open_in_console"), + Glyph = "\xE756", + FontFamily = "Segoe MDL2 Assets", AcceleratorKey = Key.C, - AcceleratorModifiers = ModifierKeys.Control | ModifierKeys.Shift, - - Action = (context) => - { - try - { - if (type == ResultType.File) - { + AcceleratorModifiers = ModifierKeys.Control | ModifierKeys.Shift, + + Action = (context) => + { + try + { + if (type == ResultType.File) + { Helper.OpenInConsole(Path.GetDirectoryName(record.Path)); - } - else - { + } + else + { Helper.OpenInConsole(record.Path); - } - - return true; - } - catch (Exception e) - { - Log.Exception($"|Microsoft.Plugin.Indexer.ContextMenuLoader.LoadContextMenus| Failed to open {record.Path} in console, {e.Message}", e); - return false; - } - } - }); - } - - return contextMenus; + } + + return true; + } + catch (Exception e) + { + Log.Exception($"|Microsoft.Plugin.Indexer.ContextMenuLoader.LoadContextMenus| Failed to open {record.Path} in console, {e.Message}", e); + return false; + } + }, + }); + } + + return contextMenus; } // Function to add the context menu item to run as admin @@ -122,7 +126,7 @@ namespace Microsoft.Plugin.Indexer Glyph = "\xE7EF", FontFamily = "Segoe MDL2 Assets", AcceleratorKey = Key.Enter, - AcceleratorModifiers = (ModifierKeys.Control | ModifierKeys.Shift), + AcceleratorModifiers = ModifierKeys.Control | ModifierKeys.Shift, Action = _ => { try @@ -135,9 +139,8 @@ namespace Microsoft.Plugin.Indexer Log.Exception($"|Microsoft.Plugin.Indexer.ContextMenu| Failed to run {record.Path} as admin, {e.Message}", e); return false; } - - } - }; + }, + }; } // Function to test if the file can be run as admin @@ -151,43 +154,43 @@ namespace Microsoft.Plugin.Indexer return true; } } + return false; } [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "We want to keep the process alive, and instead log and show an error message")] - private ContextMenuResult CreateOpenContainingFolderResult(SearchResult record) - { - return new ContextMenuResult - { - PluginName = Assembly.GetExecutingAssembly().GetName().Name, - Title = _context.API.GetTranslation("Microsoft_plugin_indexer_open_containing_folder"), - Glyph = "\xE838", - FontFamily = "Segoe MDL2 Assets", - AcceleratorKey = Key.E, - AcceleratorModifiers = (ModifierKeys.Control | ModifierKeys.Shift), - Action = _ => - { - try - { - Process.Start("explorer.exe", $" /select,\"{record.Path}\""); - } - catch (Exception e) - { - var message = $"Fail to open file at {record.Path}"; - LogException(message, e); - _context.API.ShowMsg(message); - return false; - } - - return true; - }, - }; - } - - public static void LogException(string message, Exception e) - { - Log.Exception($"|Microsoft.Plugin.Folder.ContextMenu|{message}", e); - } + private ContextMenuResult CreateOpenContainingFolderResult(SearchResult record) + { + return new ContextMenuResult + { + PluginName = Assembly.GetExecutingAssembly().GetName().Name, + Title = _context.API.GetTranslation("Microsoft_plugin_indexer_open_containing_folder"), + Glyph = "\xE838", + FontFamily = "Segoe MDL2 Assets", + AcceleratorKey = Key.E, + AcceleratorModifiers = ModifierKeys.Control | ModifierKeys.Shift, + Action = _ => + { + try + { + Process.Start("explorer.exe", $" /select,\"{record.Path}\""); + } + catch (Exception e) + { + var message = $"Fail to open file at {record.Path}"; + LogException(message, e); + _context.API.ShowMsg(message); + return false; + } + + return true; + }, + }; + } + + public static void LogException(string message, Exception e) + { + Log.Exception($"|Microsoft.Plugin.Folder.ContextMenu|{message}", e); + } } - -} \ No newline at end of file +} diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/DriveDetection/IndexerDriveDetection.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/DriveDetection/IndexerDriveDetection.cs index 7bc4a8ac3b..28a7eadc29 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/DriveDetection/IndexerDriveDetection.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/DriveDetection/IndexerDriveDetection.cs @@ -1,9 +1,15 @@ -namespace Microsoft.Plugin.Indexer.DriveDetection +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Plugin.Indexer.DriveDetection { public class IndexerDriveDetection { private bool IsEnhancedModeEnabled { get; set; } = false; - private IRegistryWrapper _registryHelper; + + private readonly IRegistryWrapper _registryHelper; + public bool IsDriveDetectionWarningCheckBoxSelected { get; set; } = false; public IndexerDriveDetection(IRegistryWrapper registryHelper) @@ -18,7 +24,7 @@ return !(IsDriveDetectionWarningCheckBoxSelected || IsEnhancedModeEnabled); } - // To look up the registry entry for + // To look up the registry entry for enhanced search private void GetEnhancedModeStatus() { string registryLocation = @"Software\Microsoft\Windows Search\Gather\Windows\SystemIndex"; diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/DriveDetection/RegistryWrapper.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/DriveDetection/RegistryWrapper.cs index beb0142084..1c85d93c9e 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/DriveDetection/RegistryWrapper.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/DriveDetection/RegistryWrapper.cs @@ -1,8 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Windows.Input; -using Microsoft.Plugin.Indexer; +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; using Microsoft.Win32; namespace Microsoft.Plugin.Indexer.DriveDetection @@ -16,13 +16,14 @@ namespace Microsoft.Plugin.Indexer.DriveDetection { if (regKey != null) { - Object value = regKey.GetValue(valueName); + object value = regKey.GetValue(valueName); if (value != null) { return (int)value; } } } + return 0; } } diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Settings.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/IndexerSettings.cs similarity index 51% rename from src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Settings.cs rename to src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/IndexerSettings.cs index 93197bc7ed..a7cc87c7b4 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Settings.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/IndexerSettings.cs @@ -1,23 +1,17 @@ -using System; +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Microsoft.Plugin.Indexer { public class IndexerSettings { public List ContextMenus { get; } = new List(); + public int MaxSearchCount { get; set; } = 100; + public bool UseLocationAsWorkingDir { get; set; } = false; } - - public class ContextMenu - { - public string Name { get; set; } - public string Command { get; set; } - public string Argument { get; set; } - public string ImagePath { get; set; } - } } diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Interface/IRegistryWrapper.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Interface/IRegistryWrapper.cs index 1a29b974f5..46a4f15ef5 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Interface/IRegistryWrapper.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Interface/IRegistryWrapper.cs @@ -1,6 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Text; +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. namespace Microsoft.Plugin.Indexer { diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Interface/ISearch.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Interface/ISearch.cs index bbcea13b5a..e62cef3efa 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Interface/ISearch.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Interface/ISearch.cs @@ -1,5 +1,9 @@ -using Microsoft.Plugin.Indexer.SearchHelper; +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + using System.Collections.Generic; +using Microsoft.Plugin.Indexer.SearchHelper; namespace Microsoft.Plugin.Indexer { diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Main.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Main.cs index f29d313e26..31392f2b81 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Main.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Main.cs @@ -1,27 +1,27 @@ -using System; +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; using System.Collections.Generic; -using System.Linq; -using System.Diagnostics; -using System.Text; -using System.Threading.Tasks; -using Wox.Plugin; -using System.IO; using System.ComponentModel; -using Wox.Infrastructure.Storage; -using Microsoft.Plugin.Indexer.SearchHelper; -using Microsoft.Search.Interop; -using Microsoft.PowerToys.Settings.UI.Lib; -using System.Windows.Controls; -using Wox.Infrastructure.Logger; -using System.Text.RegularExpressions; -using Microsoft.Plugin.Indexer.DriveDetection; +using System.Diagnostics; using System.Globalization; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using System.Windows.Controls; +using Microsoft.Plugin.Indexer.DriveDetection; +using Microsoft.Plugin.Indexer.SearchHelper; +using Microsoft.PowerToys.Settings.UI.Lib; +using Wox.Infrastructure.Logger; +using Wox.Infrastructure.Storage; +using Wox.Plugin; namespace Microsoft.Plugin.Indexer { internal class Main : ISettingProvider, IPlugin, ISavable, IPluginI18n, IContextMenu, IDisposable { - // This variable contains metadata about the Plugin private PluginInitContext _context; @@ -39,8 +39,10 @@ namespace Microsoft.Plugin.Indexer private readonly IndexerDriveDetection _driveDetection = new IndexerDriveDetection(new RegistryWrapper()); // Reserved keywords in oleDB - private string ReservedStringPattern = @"^[\/\\\$\%]+$"; + private readonly string reservedStringPattern = @"^[\/\\\$\%]+$"; + private string WarningIconPath { get; set; } + private IContextMenu _contextMenuLoader; private bool disposedValue; @@ -50,105 +52,108 @@ namespace Microsoft.Plugin.Indexer _storage.Save(); } - // This function uses the Windows indexer and returns the list of results obtained [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "We want to keep the process alive but will log the exception")] public List Query(Query query) { var results = new List(); - if (!string.IsNullOrEmpty(query.Search)) - { - var searchQuery = query.Search; - if (_settings.MaxSearchCount <= 0) - { - _settings.MaxSearchCount = 50; - } - - var regexMatch = Regex.Match(searchQuery, ReservedStringPattern); - - if (!regexMatch.Success) - { - try - { - if(_driveDetection.DisplayWarning()) - { - results.Add(new Result - { - Title = _context.API.GetTranslation("Microsoft_plugin_indexer_drivedetectionwarning"), - SubTitle = _context.API.GetTranslation("Microsoft_plugin_indexer_disable_warning_in_settings"), - IcoPath = WarningIconPath, - Action = e => - { - try - { - Process.Start(GetWindowsSearchSettingsProcessInfo()); - } - catch (Exception ex) - { - Log.Exception("Microsoft.Plugin.Indexer", $"Unable to launch Windows Search Settings: {ex.Message}", ex, "Query"); - } - return true; - } - }); - } - - var searchResultsList = _api.Search(searchQuery, maxCount: _settings.MaxSearchCount).ToList(); - foreach (var searchResult in searchResultsList) - { - var path = searchResult.Path; - var toolTipTitle = string.Format(CultureInfo.CurrentCulture, "{0} : {1}", _context.API.GetTranslation("Microsoft_plugin_indexer_name"), searchResult.Title); - var toolTipText = string.Format(CultureInfo.CurrentCulture, "{0} : {1}", _context.API.GetTranslation("Microsoft_plugin_indexer_path"), path); - string workingDir = null; - if (_settings.UseLocationAsWorkingDir) - workingDir = Path.GetDirectoryName(path); - - Result r = new Result(); - r.Title = searchResult.Title; - r.SubTitle = "Search: " + path; - r.IcoPath = path; - r.ToolTipData = new ToolTipData(toolTipTitle, toolTipText); - r.Action = c => - { - bool hide; - try - { - Process.Start(new ProcessStartInfo - { - FileName = path, - UseShellExecute = true, - WorkingDirectory = workingDir - }); - hide = true; - } - catch (Win32Exception) - { - var name = $"Plugin: {_context.CurrentPluginMetadata.Name}"; - var msg = "Can't Open this file"; - _context.API.ShowMsg(name, msg, string.Empty); - hide = false; - } - return hide; - }; - r.ContextData = searchResult; - - //If the result is a directory, then it's display should show a directory. - if (Directory.Exists(path)) - { - r.QueryTextDisplay = path; - } - - results.Add(r); - } - } - catch (InvalidOperationException) - { - //The connection has closed, internal error of ExecuteReader() - //Not showing this exception to the users - } - catch (Exception ex) - { - Log.Info(ex.ToString()); + if (!string.IsNullOrEmpty(query.Search)) + { + var searchQuery = query.Search; + if (_settings.MaxSearchCount <= 0) + { + _settings.MaxSearchCount = 50; + } + + var regexMatch = Regex.Match(searchQuery, reservedStringPattern); + + if (!regexMatch.Success) + { + try + { + if (_driveDetection.DisplayWarning()) + { + results.Add(new Result + { + Title = _context.API.GetTranslation("Microsoft_plugin_indexer_drivedetectionwarning"), + SubTitle = _context.API.GetTranslation("Microsoft_plugin_indexer_disable_warning_in_settings"), + IcoPath = WarningIconPath, + Action = e => + { + try + { + Process.Start(GetWindowsSearchSettingsProcessInfo()); + } + catch (Exception ex) + { + Log.Exception("Microsoft.Plugin.Indexer", $"Unable to launch Windows Search Settings: {ex.Message}", ex, "Query"); + } + + return true; + }, + }); + } + + var searchResultsList = _api.Search(searchQuery, maxCount: _settings.MaxSearchCount).ToList(); + foreach (var searchResult in searchResultsList) + { + var path = searchResult.Path; + var toolTipTitle = string.Format(CultureInfo.CurrentCulture, "{0} : {1}", _context.API.GetTranslation("Microsoft_plugin_indexer_name"), searchResult.Title); + var toolTipText = string.Format(CultureInfo.CurrentCulture, "{0} : {1}", _context.API.GetTranslation("Microsoft_plugin_indexer_path"), path); + string workingDir = null; + if (_settings.UseLocationAsWorkingDir) + { + workingDir = Path.GetDirectoryName(path); + } + + Result r = new Result(); + r.Title = searchResult.Title; + r.SubTitle = "Search: " + path; + r.IcoPath = path; + r.ToolTipData = new ToolTipData(toolTipTitle, toolTipText); + r.Action = c => + { + bool hide; + try + { + Process.Start(new ProcessStartInfo + { + FileName = path, + UseShellExecute = true, + WorkingDirectory = workingDir, + }); + hide = true; + } + catch (Win32Exception) + { + var name = $"Plugin: {_context.CurrentPluginMetadata.Name}"; + var msg = "Can't Open this file"; + _context.API.ShowMsg(name, msg, string.Empty); + hide = false; + } + + return hide; + }; + r.ContextData = searchResult; + + // If the result is a directory, then it's display should show a directory. + if (Directory.Exists(path)) + { + r.QueryTextDisplay = path; + } + + results.Add(r); + } + } + catch (InvalidOperationException) + { + // The connection has closed, internal error of ExecuteReader() + // Not showing this exception to the users + } + catch (Exception ex) + { + Log.Info(ex.ToString()); } } } @@ -180,7 +185,7 @@ namespace Microsoft.Plugin.Indexer } } - private void OnThemeChanged(Theme _, Theme newTheme) + private void OnThemeChanged(Theme currentTheme, Theme newTheme) { UpdateIconPath(newTheme); } @@ -203,11 +208,13 @@ namespace Microsoft.Plugin.Indexer { return _contextMenuLoader.LoadContextMenus(selectedResult); } + public void UpdateSettings(PowerLauncherSettings settings) { _settings.MaxSearchCount = settings.Properties.MaximumNumberOfResults; _driveDetection.IsDriveDetectionWarningCheckBoxSelected = settings.Properties.DisableDriveDetectionWarning; } + public Control CreateSettingPanel() { throw new NotImplementedException(); @@ -219,7 +226,7 @@ namespace Microsoft.Plugin.Indexer var ps = new ProcessStartInfo("ms-settings:cortana-windowssearch") { UseShellExecute = true, - Verb = "open" + Verb = "open", }; return ps; diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Microsoft.Plugin.Indexer.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Microsoft.Plugin.Indexer.csproj index 6ab6155c85..23b5545c66 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Microsoft.Plugin.Indexer.csproj +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Microsoft.Plugin.Indexer.csproj @@ -1,119 +1,133 @@ - - - - netcoreapp3.1 - {F8B870EB-D5F5-45BA-9CF7-A5C459818820} - Properties - Microsoft.Plugin.Indexer - Microsoft.Plugin.Indexer - true - false - false - x64 - - - - true - ..\..\..\..\..\x64\Debug\modules\launcher\Plugins\Microsoft.Plugin.Indexer\ - DEBUG;TRACE - full - x64 - 7.3 - prompt - MinimumRecommendedRules.ruleset - 4 - false - true - - - - ..\..\..\..\..\x64\Release\modules\launcher\Plugins\Microsoft.Plugin.Indexer\ - TRACE - true - pdbonly - x64 - 7.3 - prompt - MinimumRecommendedRules.ruleset - 4 - - - + + + + netcoreapp3.1 + {F8B870EB-D5F5-45BA-9CF7-A5C459818820} + Properties + Microsoft.Plugin.Indexer + Microsoft.Plugin.Indexer + true + false + false + x64 + + + + true + ..\..\..\..\..\x64\Debug\modules\launcher\Plugins\Microsoft.Plugin.Indexer\ + DEBUG;TRACE + full + x64 + 7.3 + prompt + MinimumRecommendedRules.ruleset + 4 + false + true + + + + ..\..\..\..\..\x64\Release\modules\launcher\Plugins\Microsoft.Plugin.Indexer\ + TRACE + true + pdbonly + x64 + 7.3 + prompt + MinimumRecommendedRules.ruleset + 4 + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - NU1701 - - - - - - - - - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - - - - MSBuild:Compile - Designer - PreserveNewest - - - MSBuild:Compile - Designer - PreserveNewest - - - MSBuild:Compile - Designer - PreserveNewest - - - MSBuild:Compile - Designer - PreserveNewest - - - MSBuild:Compile - Designer - PreserveNewest - - - MSBuild:Compile - Designer - PreserveNewest - - - MSBuild:Compile - Designer - PreserveNewest - - - - - - <_Parameter1>Wox.Test - - - - + + + + NU1701 + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + + MSBuild:Compile + Designer + PreserveNewest + + + MSBuild:Compile + Designer + PreserveNewest + + + MSBuild:Compile + Designer + PreserveNewest + + + MSBuild:Compile + Designer + PreserveNewest + + + MSBuild:Compile + Designer + PreserveNewest + + + MSBuild:Compile + Designer + PreserveNewest + + + MSBuild:Compile + Designer + PreserveNewest + + + + + + <_Parameter1>Wox.Test + + + + + GlobalSuppressions.cs + + + StyleCop.json + + + + + 1.1.118 + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/SearchHelper/OleDBResult.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/SearchHelper/OleDBResult.cs index 384c2f41d8..609fd2e84d 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/SearchHelper/OleDBResult.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/SearchHelper/OleDBResult.cs @@ -1,8 +1,8 @@ -using System; +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + using System.Collections.Generic; -using System.Data.Common; -using System.Data.OleDb; -using System.Text; namespace Microsoft.Plugin.Indexer.SearchHelper { diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/SearchHelper/OleDBSearch.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/SearchHelper/OleDBSearch.cs index e9ba0cc145..ea4489da67 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/SearchHelper/OleDBSearch.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/SearchHelper/OleDBSearch.cs @@ -1,83 +1,89 @@ -using System; -using System.Collections.Generic; -using System.Data.OleDb; - -namespace Microsoft.Plugin.Indexer.SearchHelper -{ - public class OleDBSearch : ISearch, IDisposable - { - private OleDbCommand command; - private OleDbConnection conn; - private OleDbDataReader WDSResults; +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Data.OleDb; + +namespace Microsoft.Plugin.Indexer.SearchHelper +{ + public class OleDBSearch : ISearch, IDisposable + { + private OleDbCommand command; + private OleDbConnection conn; + private OleDbDataReader wDSResults; private bool disposedValue; - [System.Diagnostics.CodeAnalysis.SuppressMessage("Security", "CA2100:Review SQL queries for security vulnerabilities", - Justification = "sqlQuery does not come from user input but is generated via the ISearchQueryHelper::GenerateSqlFromUserQuery " + - " see: https://docs.microsoft.com/en-us/windows/win32/search/-search-3x-wds-qryidx-searchqueryhelper#using-the-generatesqlfromuserquery-method")] - public List Query(string connectionString, string sqlQuery) - { - List result = new List(); - - using (conn = new OleDbConnection(connectionString)) - { - // open the connection - conn.Open(); - - // now create an OleDB command object with the query we built above and the connection we just opened. - using (command = new OleDbCommand(sqlQuery, conn)) - { - using (WDSResults = command.ExecuteReader()) - { - if (WDSResults.HasRows) - { - while (WDSResults.Read()) - { - List fieldData = new List(); - for (int i = 0; i < WDSResults.FieldCount; i++) - { - fieldData.Add(WDSResults.GetValue(i)); - } - result.Add(new OleDBResult(fieldData)); - } - } - } - } - } - - return result; - } - - /// Checks if all the variables related to database connection have been properly disposed - public bool HaveAllDisposableItemsBeenDisposed() - { - bool commandDisposed = false; - bool connDisposed = false; - bool resultDisposed = false; - - try - { - command.ExecuteReader(); - } - catch (InvalidOperationException) - { - commandDisposed = true; - } - - try - { - WDSResults.Read(); - } - catch (InvalidOperationException) - { - resultDisposed = true; - } - - if (conn.State == System.Data.ConnectionState.Closed) - { - connDisposed = true; - } - - return commandDisposed && resultDisposed && connDisposed; + [System.Diagnostics.CodeAnalysis.SuppressMessage( + "Security", + "CA2100:Review SQL queries for security vulnerabilities", + Justification = "sqlQuery does not come from user input but is generated via the ISearchQueryHelper::GenerateSqlFromUserQuery see: https://docs.microsoft.com/en-us/windows/win32/search/-search-3x-wds-qryidx-searchqueryhelper#using-the-generatesqlfromuserquery-method")] + public List Query(string connectionString, string sqlQuery) + { + List result = new List(); + + using (conn = new OleDbConnection(connectionString)) + { + // open the connection + conn.Open(); + + // now create an OleDB command object with the query we built above and the connection we just opened. + using (command = new OleDbCommand(sqlQuery, conn)) + { + using (wDSResults = command.ExecuteReader()) + { + if (wDSResults.HasRows) + { + while (wDSResults.Read()) + { + List fieldData = new List(); + for (int i = 0; i < wDSResults.FieldCount; i++) + { + fieldData.Add(wDSResults.GetValue(i)); + } + + result.Add(new OleDBResult(fieldData)); + } + } + } + } + } + + return result; + } + + // Checks if all the variables related to database connection have been properly disposed + public bool HaveAllDisposableItemsBeenDisposed() + { + bool commandDisposed = false; + bool connDisposed = false; + bool resultDisposed = false; + + try + { + command.ExecuteReader(); + } + catch (InvalidOperationException) + { + commandDisposed = true; + } + + try + { + wDSResults.Read(); + } + catch (InvalidOperationException) + { + resultDisposed = true; + } + + if (conn.State == System.Data.ConnectionState.Closed) + { + connDisposed = true; + } + + return commandDisposed && resultDisposed && connDisposed; } protected virtual void Dispose(bool disposing) @@ -88,7 +94,7 @@ namespace Microsoft.Plugin.Indexer.SearchHelper { command?.Dispose(); conn?.Dispose(); - WDSResults?.Dispose(); + wDSResults?.Dispose(); } // TODO: free unmanaged resources (unmanaged objects) and override finalizer @@ -97,18 +103,11 @@ namespace Microsoft.Plugin.Indexer.SearchHelper } } - // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources - // ~OleDBSearch() - // { - // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - // Dispose(disposing: false); - // } - public void Dispose() { // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method Dispose(disposing: true); GC.SuppressFinalize(this); } - } -} + } +} diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/SearchHelper/SearchResult.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/SearchHelper/SearchResult.cs index 048191fa23..2d6f8fb209 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/SearchHelper/SearchResult.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/SearchHelper/SearchResult.cs @@ -1,8 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. namespace Microsoft.Plugin.Indexer.SearchHelper { @@ -13,6 +11,5 @@ namespace Microsoft.Plugin.Indexer.SearchHelper // Contains the Title of the file or folder public string Title { get; set; } - } } diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/SearchHelper/WindowsSearchAPI.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/SearchHelper/WindowsSearchAPI.cs index 72ff38808f..2f67a5b686 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/SearchHelper/WindowsSearchAPI.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/SearchHelper/WindowsSearchAPI.cs @@ -1,68 +1,72 @@ -using System; -using System.Collections.Generic; -using Microsoft.Search.Interop; - -namespace Microsoft.Plugin.Indexer.SearchHelper -{ - public class WindowsSearchAPI - { - public bool DisplayHiddenFiles { get; set; } - - private readonly ISearch WindowsIndexerSearch; - private readonly object _lock = new object(); - private readonly UInt32 FILE_ATTRIBUTE_HIDDEN = 0x2; - - public WindowsSearchAPI(ISearch windowsIndexerSearch, bool displayHiddenFiles = false) - { - this.WindowsIndexerSearch = windowsIndexerSearch; - this.DisplayHiddenFiles = displayHiddenFiles; - } - - public List ExecuteQuery(ISearchQueryHelper queryHelper, string keyword) - { - if(queryHelper == null) +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using Microsoft.Search.Interop; + +namespace Microsoft.Plugin.Indexer.SearchHelper +{ + public class WindowsSearchAPI + { + public bool DisplayHiddenFiles { get; set; } + + private readonly ISearch windowsIndexerSearch; + private readonly object _lock = new object(); + private const uint _fileAttributeHidden = 0x2; + + public WindowsSearchAPI(ISearch windowsIndexerSearch, bool displayHiddenFiles = false) + { + this.windowsIndexerSearch = windowsIndexerSearch; + DisplayHiddenFiles = displayHiddenFiles; + } + + public List ExecuteQuery(ISearchQueryHelper queryHelper, string keyword) + { + if (queryHelper == null) { throw new ArgumentNullException(paramName: nameof(queryHelper)); - } - - List _Result = new List(); - - // Generate SQL from our parameters, converting the userQuery from AQS->WHERE clause - string sqlQuery = queryHelper.GenerateSQLFromUserQuery(keyword); - - // execute the command, which returns the results as an OleDBResults. - List oleDBResults = WindowsIndexerSearch.Query(queryHelper.ConnectionString, sqlQuery); - - // Loop over all records from the database - foreach (OleDBResult oleDBResult in oleDBResults) - { - if (oleDBResult.FieldData[0] == DBNull.Value || oleDBResult.FieldData[1] == DBNull.Value || oleDBResult.FieldData[2] == DBNull.Value) - { - continue; - } - - UInt32 fileAttributes = (UInt32)((Int64)oleDBResult.FieldData[2]); - bool isFileHidden = (fileAttributes & FILE_ATTRIBUTE_HIDDEN) == FILE_ATTRIBUTE_HIDDEN; - - if (DisplayHiddenFiles || !isFileHidden) - { - var uri_path = new Uri((string)oleDBResult.FieldData[0]); - var result = new SearchResult - { - Path = uri_path.LocalPath, - Title = (string)oleDBResult.FieldData[1] - }; - _Result.Add(result); - } - } - - return _Result; - } - - - public static void ModifyQueryHelper(ref ISearchQueryHelper queryHelper, string pattern) - { - if(pattern == null) + } + + List results = new List(); + + // Generate SQL from our parameters, converting the userQuery from AQS->WHERE clause + string sqlQuery = queryHelper.GenerateSQLFromUserQuery(keyword); + + // execute the command, which returns the results as an OleDBResults. + List oleDBResults = windowsIndexerSearch.Query(queryHelper.ConnectionString, sqlQuery); + + // Loop over all records from the database + foreach (OleDBResult oleDBResult in oleDBResults) + { + if (oleDBResult.FieldData[0] == DBNull.Value || oleDBResult.FieldData[1] == DBNull.Value || oleDBResult.FieldData[2] == DBNull.Value) + { + continue; + } + + uint fileAttributes = (uint)((long)oleDBResult.FieldData[2]); + bool isFileHidden = (fileAttributes & _fileAttributeHidden) == _fileAttributeHidden; + + if (DisplayHiddenFiles || !isFileHidden) + { + var uri_path = new Uri((string)oleDBResult.FieldData[0]); + var result = new SearchResult + { + Path = uri_path.LocalPath, + Title = (string)oleDBResult.FieldData[1], + }; + + results.Add(result); + } + } + + return results; + } + + public static void ModifyQueryHelper(ref ISearchQueryHelper queryHelper, string pattern) + { + if (pattern == null) { throw new ArgumentNullException(paramName: nameof(pattern)); } @@ -70,62 +74,62 @@ namespace Microsoft.Plugin.Indexer.SearchHelper if (queryHelper == null) { throw new ArgumentNullException(paramName: nameof(queryHelper)); - } - - // convert file pattern if it is not '*'. Don't create restriction for '*' as it includes all files. - if (pattern != "*") - { - pattern = pattern.Replace("*", "%", StringComparison.InvariantCulture); - pattern = pattern.Replace("?", "_", StringComparison.InvariantCulture); - - if (pattern.Contains("%", StringComparison.InvariantCulture) || pattern.Contains("_", StringComparison.InvariantCulture)) - { - queryHelper.QueryWhereRestrictions += " AND System.FileName LIKE '" + pattern + "' "; - } - else - { - // if there are no wildcards we can use a contains which is much faster as it uses the index - queryHelper.QueryWhereRestrictions += " AND Contains(System.FileName, '" + pattern + "') "; - } - } - } - - public static void InitQueryHelper(out ISearchQueryHelper queryHelper, int maxCount) - { - // This uses the Microsoft.Search.Interop assembly - CSearchManager manager = new CSearchManager(); - - // SystemIndex catalog is the default catalog in Windows - ISearchCatalogManager catalogManager = manager.GetCatalog("SystemIndex"); - - // Get the ISearchQueryHelper which will help us to translate AQS --> SQL necessary to query the indexer - queryHelper = catalogManager.GetQueryHelper(); - - // Set the number of results we want. Don't set this property if all results are needed. - queryHelper.QueryMaxResults = maxCount; - - // Set list of columns we want to display, getting the path presently - queryHelper.QuerySelectColumns = "System.ItemUrl, System.FileName, System.FileAttributes"; - - // Set additional query restriction - queryHelper.QueryWhereRestrictions = "AND scope='file:'"; - - // To filter based on title for now - queryHelper.QueryContentProperties = "System.FileName"; - - // Set sorting order - queryHelper.QuerySorting = "System.DateModified DESC"; - } - - public IEnumerable Search(string keyword, string pattern = "*", int maxCount = 30) - { - lock (_lock) - { - ISearchQueryHelper queryHelper; - InitQueryHelper(out queryHelper, maxCount); - ModifyQueryHelper(ref queryHelper, pattern); - return ExecuteQuery(queryHelper, keyword); - } - } - } -} + } + + // convert file pattern if it is not '*'. Don't create restriction for '*' as it includes all files. + if (pattern != "*") + { + pattern = pattern.Replace("*", "%", StringComparison.InvariantCulture); + pattern = pattern.Replace("?", "_", StringComparison.InvariantCulture); + + if (pattern.Contains("%", StringComparison.InvariantCulture) || pattern.Contains("_", StringComparison.InvariantCulture)) + { + queryHelper.QueryWhereRestrictions += " AND System.FileName LIKE '" + pattern + "' "; + } + else + { + // if there are no wildcards we can use a contains which is much faster as it uses the index + queryHelper.QueryWhereRestrictions += " AND Contains(System.FileName, '" + pattern + "') "; + } + } + } + + public static void InitQueryHelper(out ISearchQueryHelper queryHelper, int maxCount) + { + // This uses the Microsoft.Search.Interop assembly + CSearchManager manager = new CSearchManager(); + + // SystemIndex catalog is the default catalog in Windows + ISearchCatalogManager catalogManager = manager.GetCatalog("SystemIndex"); + + // Get the ISearchQueryHelper which will help us to translate AQS --> SQL necessary to query the indexer + queryHelper = catalogManager.GetQueryHelper(); + + // Set the number of results we want. Don't set this property if all results are needed. + queryHelper.QueryMaxResults = maxCount; + + // Set list of columns we want to display, getting the path presently + queryHelper.QuerySelectColumns = "System.ItemUrl, System.FileName, System.FileAttributes"; + + // Set additional query restriction + queryHelper.QueryWhereRestrictions = "AND scope='file:'"; + + // To filter based on title for now + queryHelper.QueryContentProperties = "System.FileName"; + + // Set sorting order + queryHelper.QuerySorting = "System.DateModified DESC"; + } + + public IEnumerable Search(string keyword, string pattern = "*", int maxCount = 30) + { + lock (_lock) + { + ISearchQueryHelper queryHelper; + InitQueryHelper(out queryHelper, maxCount); + ModifyQueryHelper(ref queryHelper, pattern); + return ExecuteQuery(queryHelper, keyword); + } + } + } +}