From 00c229d82c95bed164bc2fda73f281bb16d985e3 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 15 Dec 2022 22:03:54 +1100 Subject: [PATCH] add exception handling when Windows Search service not available --- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 2 +- .../Search/WindowsIndex/QueryConstructor.cs | 23 +++-- .../Search/WindowsIndex/WindowsIndex.cs | 28 +----- .../WindowsIndex/WindowsIndexSearchManager.cs | 86 +++++++++++++++---- 4 files changed, 80 insertions(+), 59 deletions(-) diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index a5e3ec3df..e0cc9b4c2 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -116,7 +116,7 @@ namespace Flow.Launcher.Test.Plugins { // Given var queryConstructor = new QueryConstructor(new Settings()); - var baseQuery = queryConstructor.BaseQueryHelper; + var baseQuery = queryConstructor.CreateBaseQuery(); // system running this test could have different locale than the hard-coded 1033 LCID en-US. var queryKeywordLocale = baseQuery.QueryKeywordLocale; diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs index 6501c1657..87eca91da 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Buffers; using Microsoft.Search.Interop; @@ -6,25 +6,21 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex { public class QueryConstructor { - private Settings Settings { get; } + private Settings settings { get; } private const string SystemIndex = "SystemIndex"; - public CSearchQueryHelper BaseQueryHelper { get; } - public QueryConstructor(Settings settings) { - Settings = settings; - BaseQueryHelper = CreateBaseQuery(); + this.settings = settings; } - - private CSearchQueryHelper CreateBaseQuery() + public CSearchQueryHelper CreateBaseQuery() { var baseQuery = CreateQueryHelper(); // Set the number of results we want. Don't set this property if all results are needed. - baseQuery.QueryMaxResults = Settings.MaxResult; + baseQuery.QueryMaxResults = settings.MaxResult; // Set list of columns we want to display, getting the path presently baseQuery.QuerySelectColumns = "System.FileName, System.ItemUrl, System.ItemType"; @@ -38,9 +34,10 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex return baseQuery; } - internal static CSearchQueryHelper CreateQueryHelper() + internal CSearchQueryHelper CreateQueryHelper() { // This uses the Microsoft.Search.Interop assembly + // Throws COMException if Windows Search service is not running/disabled, this needs to be caught var manager = new CSearchManager(); // SystemIndex catalog is the default catalog in Windows @@ -67,7 +64,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex ? RecursiveDirectoryConstraint(path) : TopLevelDirectoryConstraint(path); - var query = $"SELECT TOP {Settings.MaxResult} {BaseQueryHelper.QuerySelectColumns} FROM {SystemIndex} WHERE {scopeConstraint} {queryConstraint} ORDER BY {FileName}"; + var query = $"SELECT TOP {settings.MaxResult} {CreateBaseQuery().QuerySelectColumns} FROM {SystemIndex} WHERE {scopeConstraint} {queryConstraint} ORDER BY {FileName}"; return query; } @@ -81,7 +78,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex userSearchString = "*"; // Generate SQL from constructed parameters, converting the userSearchString from AQS->WHERE clause - return $"{BaseQueryHelper.GenerateSQLFromUserQuery(userSearchString.ToString())} AND {RestrictionsForAllFilesAndFoldersSearch} ORDER BY {FileName}"; + return $"{CreateBaseQuery().GenerateSQLFromUserQuery(userSearchString.ToString())} AND {RestrictionsForAllFilesAndFoldersSearch} ORDER BY {FileName}"; } /// @@ -101,7 +98,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex public string FileContent(ReadOnlySpan userSearchString) { string query = - $"SELECT TOP {Settings.MaxResult} {BaseQueryHelper.QuerySelectColumns} FROM {SystemIndex} WHERE {RestrictionsForFileContentSearch(userSearchString)} AND {RestrictionsForAllFilesAndFoldersSearch} ORDER BY {FileName}"; + $"SELECT TOP {settings.MaxResult} {CreateBaseQuery().QuerySelectColumns} FROM {SystemIndex} WHERE {RestrictionsForFileContentSearch(userSearchString)} AND {RestrictionsForAllFilesAndFoldersSearch} ORDER BY {FileName}"; return query; } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndex.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndex.cs index 4396951d6..2093508a0 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndex.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndex.cs @@ -1,4 +1,4 @@ -using Flow.Launcher.Infrastructure.Logger; +using Flow.Launcher.Infrastructure.Logger; using Microsoft.Search.Interop; using System; using System.Collections.Generic; @@ -86,32 +86,6 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex { throw new SearchException("Windows Index", e.Message, e); } - catch (COMException) - { - // Occurs because the Windows Indexing (WSearch) is turned off in services and unable to be used by Explorer plugin - - if (!SearchManager.Settings.WarnWindowsSearchServiceOff) - return AsyncEnumerable.Empty(); - - var api = SearchManager.Context.API; - - throw new EngineNotAvailableException( - "Windows Index", - api.GetTranslation("plugin_explorer_windowsSearchServiceFix"), - api.GetTranslation("plugin_explorer_windowsSearchServiceNotRunning"), - c => - { - SearchManager.Settings.WarnWindowsSearchServiceOff = false; - - // Clears the warning message so user is not mistaken that it has not worked - api.ChangeQuery(string.Empty); - - return ValueTask.FromResult(false); - }) - { - ErrorIcon = Constants.WindowsIndexErrorImagePath - }; - } } internal static bool PathIsIndexed(string path) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndexSearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndexSearchManager.cs index 91ba2a858..abb50849d 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndexSearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndexSearchManager.cs @@ -1,24 +1,24 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; +using Flow.Launcher.Plugin.Explorer.Exceptions; using Flow.Launcher.Plugin.Explorer.Search.IProvider; -using Microsoft.Search.Interop; namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex { public class WindowsIndexSearchManager : IIndexProvider, IContentIndexProvider, IPathIndexProvider { private Settings Settings { get; } - private QueryConstructor QueryConstructor { get; } - private CSearchQueryHelper QueryHelper { get; } + private QueryConstructor QueryConstructor { get; } + public WindowsIndexSearchManager(Settings settings) { Settings = settings; QueryConstructor = new QueryConstructor(Settings); - QueryHelper = QueryConstructor.CreateQueryHelper(); } private IAsyncEnumerable WindowsIndexFileContentSearchAsync( @@ -28,20 +28,38 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex if (querySearchString.IsEmpty) return AsyncEnumerable.Empty(); - return WindowsIndex.WindowsIndexSearchAsync( - QueryHelper.ConnectionString, - QueryConstructor.FileContent(querySearchString), - token); + try + { + return WindowsIndex.WindowsIndexSearchAsync( + QueryConstructor.CreateQueryHelper().ConnectionString, + QueryConstructor.FileContent(querySearchString), + token); + } + catch (COMException) + { + // Occurs when the Windows Indexing (WSearch) is turned off in services and unable to be used by Explorer plugin + // Thrown by QueryConstructor.CreateQueryHelper() + return HandledEngineNotAvailableExceptionAsync(); + } } private IAsyncEnumerable WindowsIndexFilesAndFoldersSearchAsync( ReadOnlySpan querySearchString, CancellationToken token = default) { - return WindowsIndex.WindowsIndexSearchAsync( - QueryHelper.ConnectionString, - QueryConstructor.FilesAndFolders(querySearchString), - token); + try + { + return WindowsIndex.WindowsIndexSearchAsync( + QueryConstructor.CreateQueryHelper().ConnectionString, + QueryConstructor.FilesAndFolders(querySearchString), + token); + } + catch (COMException) + { + // Occurs when the Windows Indexing (WSearch) is turned off in services and unable to be used by Explorer plugin + // Thrown by QueryConstructor.CreateQueryHelper() + return HandledEngineNotAvailableExceptionAsync(); + } } private IAsyncEnumerable WindowsIndexTopLevelFolderSearchAsync( @@ -50,12 +68,19 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex bool recursive, CancellationToken token) { - var queryConstructor = new QueryConstructor(Settings); - - return WindowsIndex.WindowsIndexSearchAsync( - QueryConstructor.CreateQueryHelper().ConnectionString, - queryConstructor.Directory(path, search, recursive), - token); + try + { + return WindowsIndex.WindowsIndexSearchAsync( + QueryConstructor.CreateQueryHelper().ConnectionString, + QueryConstructor.Directory(path, search, recursive), + token); + } + catch (COMException) + { + // Occurs when the Windows Indexing (WSearch) is turned off in services and unable to be used by Explorer plugin + // Thrown by QueryConstructor.CreateQueryHelper() + return HandledEngineNotAvailableExceptionAsync(); + } } public IAsyncEnumerable SearchAsync(string search, CancellationToken token) { @@ -69,5 +94,30 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex { return WindowsIndexTopLevelFolderSearchAsync(search, path, recursive, token); } + + private IAsyncEnumerable HandledEngineNotAvailableExceptionAsync() + { + if (!SearchManager.Settings.WarnWindowsSearchServiceOff) + return AsyncEnumerable.Empty(); + + var api = SearchManager.Context.API; + + throw new EngineNotAvailableException( + "Windows Index", + api.GetTranslation("plugin_explorer_windowsSearchServiceFix"), + api.GetTranslation("plugin_explorer_windowsSearchServiceNotRunning"), + c => + { + SearchManager.Settings.WarnWindowsSearchServiceOff = false; + + // Clears the warning message so user is not mistaken that it has not worked + api.ChangeQuery(string.Empty); + + return ValueTask.FromResult(false); + }) + { + ErrorIcon = Constants.WindowsIndexErrorImagePath + }; + } } }