mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
add exception handling when Windows Search service not available
This commit is contained in:
parent
61aaa0129d
commit
00c229d82c
4 changed files with 80 additions and 59 deletions
|
|
@ -116,7 +116,7 @@ namespace Flow.Launcher.Test.Plugins
|
||||||
{
|
{
|
||||||
// Given
|
// Given
|
||||||
var queryConstructor = new QueryConstructor(new Settings());
|
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.
|
// system running this test could have different locale than the hard-coded 1033 LCID en-US.
|
||||||
var queryKeywordLocale = baseQuery.QueryKeywordLocale;
|
var queryKeywordLocale = baseQuery.QueryKeywordLocale;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using Microsoft.Search.Interop;
|
using Microsoft.Search.Interop;
|
||||||
|
|
||||||
|
|
@ -6,25 +6,21 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
||||||
{
|
{
|
||||||
public class QueryConstructor
|
public class QueryConstructor
|
||||||
{
|
{
|
||||||
private Settings Settings { get; }
|
private Settings settings { get; }
|
||||||
|
|
||||||
private const string SystemIndex = "SystemIndex";
|
private const string SystemIndex = "SystemIndex";
|
||||||
|
|
||||||
public CSearchQueryHelper BaseQueryHelper { get; }
|
|
||||||
|
|
||||||
public QueryConstructor(Settings settings)
|
public QueryConstructor(Settings settings)
|
||||||
{
|
{
|
||||||
Settings = settings;
|
this.settings = settings;
|
||||||
BaseQueryHelper = CreateBaseQuery();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CSearchQueryHelper CreateBaseQuery()
|
||||||
private CSearchQueryHelper CreateBaseQuery()
|
|
||||||
{
|
{
|
||||||
var baseQuery = CreateQueryHelper();
|
var baseQuery = CreateQueryHelper();
|
||||||
|
|
||||||
// Set the number of results we want. Don't set this property if all results are needed.
|
// 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
|
// Set list of columns we want to display, getting the path presently
|
||||||
baseQuery.QuerySelectColumns = "System.FileName, System.ItemUrl, System.ItemType";
|
baseQuery.QuerySelectColumns = "System.FileName, System.ItemUrl, System.ItemType";
|
||||||
|
|
@ -38,9 +34,10 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
||||||
return baseQuery;
|
return baseQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static CSearchQueryHelper CreateQueryHelper()
|
internal CSearchQueryHelper CreateQueryHelper()
|
||||||
{
|
{
|
||||||
// This uses the Microsoft.Search.Interop assembly
|
// 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();
|
var manager = new CSearchManager();
|
||||||
|
|
||||||
// SystemIndex catalog is the default catalog in Windows
|
// SystemIndex catalog is the default catalog in Windows
|
||||||
|
|
@ -67,7 +64,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
||||||
? RecursiveDirectoryConstraint(path)
|
? RecursiveDirectoryConstraint(path)
|
||||||
: TopLevelDirectoryConstraint(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;
|
return query;
|
||||||
}
|
}
|
||||||
|
|
@ -81,7 +78,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
||||||
userSearchString = "*";
|
userSearchString = "*";
|
||||||
|
|
||||||
// Generate SQL from constructed parameters, converting the userSearchString from AQS->WHERE clause
|
// 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}";
|
||||||
}
|
}
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
|
|
@ -101,7 +98,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
||||||
public string FileContent(ReadOnlySpan<char> userSearchString)
|
public string FileContent(ReadOnlySpan<char> userSearchString)
|
||||||
{
|
{
|
||||||
string query =
|
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;
|
return query;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Flow.Launcher.Infrastructure.Logger;
|
using Flow.Launcher.Infrastructure.Logger;
|
||||||
using Microsoft.Search.Interop;
|
using Microsoft.Search.Interop;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
@ -86,32 +86,6 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
||||||
{
|
{
|
||||||
throw new SearchException("Windows Index", e.Message, e);
|
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<SearchResult>();
|
|
||||||
|
|
||||||
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)
|
internal static bool PathIsIndexed(string path)
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,24 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Flow.Launcher.Plugin.Explorer.Exceptions;
|
||||||
using Flow.Launcher.Plugin.Explorer.Search.IProvider;
|
using Flow.Launcher.Plugin.Explorer.Search.IProvider;
|
||||||
using Microsoft.Search.Interop;
|
|
||||||
|
|
||||||
namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
||||||
{
|
{
|
||||||
public class WindowsIndexSearchManager : IIndexProvider, IContentIndexProvider, IPathIndexProvider
|
public class WindowsIndexSearchManager : IIndexProvider, IContentIndexProvider, IPathIndexProvider
|
||||||
{
|
{
|
||||||
private Settings Settings { get; }
|
private Settings Settings { get; }
|
||||||
private QueryConstructor QueryConstructor { get; }
|
|
||||||
|
|
||||||
private CSearchQueryHelper QueryHelper { get; }
|
private QueryConstructor QueryConstructor { get; }
|
||||||
|
|
||||||
public WindowsIndexSearchManager(Settings settings)
|
public WindowsIndexSearchManager(Settings settings)
|
||||||
{
|
{
|
||||||
Settings = settings;
|
Settings = settings;
|
||||||
QueryConstructor = new QueryConstructor(Settings);
|
QueryConstructor = new QueryConstructor(Settings);
|
||||||
QueryHelper = QueryConstructor.CreateQueryHelper();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IAsyncEnumerable<SearchResult> WindowsIndexFileContentSearchAsync(
|
private IAsyncEnumerable<SearchResult> WindowsIndexFileContentSearchAsync(
|
||||||
|
|
@ -28,20 +28,38 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
||||||
if (querySearchString.IsEmpty)
|
if (querySearchString.IsEmpty)
|
||||||
return AsyncEnumerable.Empty<SearchResult>();
|
return AsyncEnumerable.Empty<SearchResult>();
|
||||||
|
|
||||||
return WindowsIndex.WindowsIndexSearchAsync(
|
try
|
||||||
QueryHelper.ConnectionString,
|
{
|
||||||
QueryConstructor.FileContent(querySearchString),
|
return WindowsIndex.WindowsIndexSearchAsync(
|
||||||
token);
|
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<SearchResult> WindowsIndexFilesAndFoldersSearchAsync(
|
private IAsyncEnumerable<SearchResult> WindowsIndexFilesAndFoldersSearchAsync(
|
||||||
ReadOnlySpan<char> querySearchString,
|
ReadOnlySpan<char> querySearchString,
|
||||||
CancellationToken token = default)
|
CancellationToken token = default)
|
||||||
{
|
{
|
||||||
return WindowsIndex.WindowsIndexSearchAsync(
|
try
|
||||||
QueryHelper.ConnectionString,
|
{
|
||||||
QueryConstructor.FilesAndFolders(querySearchString),
|
return WindowsIndex.WindowsIndexSearchAsync(
|
||||||
token);
|
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<SearchResult> WindowsIndexTopLevelFolderSearchAsync(
|
private IAsyncEnumerable<SearchResult> WindowsIndexTopLevelFolderSearchAsync(
|
||||||
|
|
@ -50,12 +68,19 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
||||||
bool recursive,
|
bool recursive,
|
||||||
CancellationToken token)
|
CancellationToken token)
|
||||||
{
|
{
|
||||||
var queryConstructor = new QueryConstructor(Settings);
|
try
|
||||||
|
{
|
||||||
return WindowsIndex.WindowsIndexSearchAsync(
|
return WindowsIndex.WindowsIndexSearchAsync(
|
||||||
QueryConstructor.CreateQueryHelper().ConnectionString,
|
QueryConstructor.CreateQueryHelper().ConnectionString,
|
||||||
queryConstructor.Directory(path, search, recursive),
|
QueryConstructor.Directory(path, search, recursive),
|
||||||
token);
|
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<SearchResult> SearchAsync(string search, CancellationToken token)
|
public IAsyncEnumerable<SearchResult> SearchAsync(string search, CancellationToken token)
|
||||||
{
|
{
|
||||||
|
|
@ -69,5 +94,30 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
||||||
{
|
{
|
||||||
return WindowsIndexTopLevelFolderSearchAsync(search, path, recursive, token);
|
return WindowsIndexTopLevelFolderSearchAsync(search, path, recursive, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IAsyncEnumerable<SearchResult> HandledEngineNotAvailableExceptionAsync()
|
||||||
|
{
|
||||||
|
if (!SearchManager.Settings.WarnWindowsSearchServiceOff)
|
||||||
|
return AsyncEnumerable.Empty<SearchResult>();
|
||||||
|
|
||||||
|
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
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue