Skip to content

Commit 00c229d

Browse files
committed
add exception handling when Windows Search service not available
1 parent 61aaa01 commit 00c229d

File tree

4 files changed

+80
-59
lines changed

4 files changed

+80
-59
lines changed

Flow.Launcher.Test/Plugins/ExplorerTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryShould
116116
{
117117
// Given
118118
var queryConstructor = new QueryConstructor(new Settings());
119-
var baseQuery = queryConstructor.BaseQueryHelper;
119+
var baseQuery = queryConstructor.CreateBaseQuery();
120120

121121
// system running this test could have different locale than the hard-coded 1033 LCID en-US.
122122
var queryKeywordLocale = baseQuery.QueryKeywordLocale;

Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,26 @@
1-
using System;
1+
using System;
22
using System.Buffers;
33
using Microsoft.Search.Interop;
44

55
namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
66
{
77
public class QueryConstructor
88
{
9-
private Settings Settings { get; }
9+
private Settings settings { get; }
1010

1111
private const string SystemIndex = "SystemIndex";
1212

13-
public CSearchQueryHelper BaseQueryHelper { get; }
14-
1513
public QueryConstructor(Settings settings)
1614
{
17-
Settings = settings;
18-
BaseQueryHelper = CreateBaseQuery();
15+
this.settings = settings;
1916
}
2017

21-
22-
private CSearchQueryHelper CreateBaseQuery()
18+
public CSearchQueryHelper CreateBaseQuery()
2319
{
2420
var baseQuery = CreateQueryHelper();
2521

2622
// Set the number of results we want. Don't set this property if all results are needed.
27-
baseQuery.QueryMaxResults = Settings.MaxResult;
23+
baseQuery.QueryMaxResults = settings.MaxResult;
2824

2925
// Set list of columns we want to display, getting the path presently
3026
baseQuery.QuerySelectColumns = "System.FileName, System.ItemUrl, System.ItemType";
@@ -38,9 +34,10 @@ private CSearchQueryHelper CreateBaseQuery()
3834
return baseQuery;
3935
}
4036

41-
internal static CSearchQueryHelper CreateQueryHelper()
37+
internal CSearchQueryHelper CreateQueryHelper()
4238
{
4339
// This uses the Microsoft.Search.Interop assembly
40+
// Throws COMException if Windows Search service is not running/disabled, this needs to be caught
4441
var manager = new CSearchManager();
4542

4643
// SystemIndex catalog is the default catalog in Windows
@@ -67,7 +64,7 @@ public string Directory(ReadOnlySpan<char> path, ReadOnlySpan<char> searchString
6764
? RecursiveDirectoryConstraint(path)
6865
: TopLevelDirectoryConstraint(path);
6966

70-
var query = $"SELECT TOP {Settings.MaxResult} {BaseQueryHelper.QuerySelectColumns} FROM {SystemIndex} WHERE {scopeConstraint} {queryConstraint} ORDER BY {FileName}";
67+
var query = $"SELECT TOP {settings.MaxResult} {CreateBaseQuery().QuerySelectColumns} FROM {SystemIndex} WHERE {scopeConstraint} {queryConstraint} ORDER BY {FileName}";
7168

7269
return query;
7370
}
@@ -81,7 +78,7 @@ public string FilesAndFolders(ReadOnlySpan<char> userSearchString)
8178
userSearchString = "*";
8279

8380
// Generate SQL from constructed parameters, converting the userSearchString from AQS->WHERE clause
84-
return $"{BaseQueryHelper.GenerateSQLFromUserQuery(userSearchString.ToString())} AND {RestrictionsForAllFilesAndFoldersSearch} ORDER BY {FileName}";
81+
return $"{CreateBaseQuery().GenerateSQLFromUserQuery(userSearchString.ToString())} AND {RestrictionsForAllFilesAndFoldersSearch} ORDER BY {FileName}";
8582
}
8683

8784
///<summary>
@@ -101,7 +98,7 @@ public string FilesAndFolders(ReadOnlySpan<char> userSearchString)
10198
public string FileContent(ReadOnlySpan<char> userSearchString)
10299
{
103100
string query =
104-
$"SELECT TOP {Settings.MaxResult} {BaseQueryHelper.QuerySelectColumns} FROM {SystemIndex} WHERE {RestrictionsForFileContentSearch(userSearchString)} AND {RestrictionsForAllFilesAndFoldersSearch} ORDER BY {FileName}";
101+
$"SELECT TOP {settings.MaxResult} {CreateBaseQuery().QuerySelectColumns} FROM {SystemIndex} WHERE {RestrictionsForFileContentSearch(userSearchString)} AND {RestrictionsForAllFilesAndFoldersSearch} ORDER BY {FileName}";
105102

106103
return query;
107104
}

Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndex.cs

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using Flow.Launcher.Infrastructure.Logger;
1+
using Flow.Launcher.Infrastructure.Logger;
22
using Microsoft.Search.Interop;
33
using System;
44
using System.Collections.Generic;
@@ -86,32 +86,6 @@ internal static IAsyncEnumerable<SearchResult> WindowsIndexSearchAsync(
8686
{
8787
throw new SearchException("Windows Index", e.Message, e);
8888
}
89-
catch (COMException)
90-
{
91-
// Occurs because the Windows Indexing (WSearch) is turned off in services and unable to be used by Explorer plugin
92-
93-
if (!SearchManager.Settings.WarnWindowsSearchServiceOff)
94-
return AsyncEnumerable.Empty<SearchResult>();
95-
96-
var api = SearchManager.Context.API;
97-
98-
throw new EngineNotAvailableException(
99-
"Windows Index",
100-
api.GetTranslation("plugin_explorer_windowsSearchServiceFix"),
101-
api.GetTranslation("plugin_explorer_windowsSearchServiceNotRunning"),
102-
c =>
103-
{
104-
SearchManager.Settings.WarnWindowsSearchServiceOff = false;
105-
106-
// Clears the warning message so user is not mistaken that it has not worked
107-
api.ChangeQuery(string.Empty);
108-
109-
return ValueTask.FromResult(false);
110-
})
111-
{
112-
ErrorIcon = Constants.WindowsIndexErrorImagePath
113-
};
114-
}
11589
}
11690

11791
internal static bool PathIsIndexed(string path)
Lines changed: 68 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4+
using System.Runtime.InteropServices;
45
using System.Threading;
56
using System.Threading.Tasks;
7+
using Flow.Launcher.Plugin.Explorer.Exceptions;
68
using Flow.Launcher.Plugin.Explorer.Search.IProvider;
7-
using Microsoft.Search.Interop;
89

910
namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
1011
{
1112
public class WindowsIndexSearchManager : IIndexProvider, IContentIndexProvider, IPathIndexProvider
1213
{
1314
private Settings Settings { get; }
14-
private QueryConstructor QueryConstructor { get; }
1515

16-
private CSearchQueryHelper QueryHelper { get; }
16+
private QueryConstructor QueryConstructor { get; }
17+
1718
public WindowsIndexSearchManager(Settings settings)
1819
{
1920
Settings = settings;
2021
QueryConstructor = new QueryConstructor(Settings);
21-
QueryHelper = QueryConstructor.CreateQueryHelper();
2222
}
2323

2424
private IAsyncEnumerable<SearchResult> WindowsIndexFileContentSearchAsync(
@@ -28,20 +28,38 @@ private IAsyncEnumerable<SearchResult> WindowsIndexFileContentSearchAsync(
2828
if (querySearchString.IsEmpty)
2929
return AsyncEnumerable.Empty<SearchResult>();
3030

31-
return WindowsIndex.WindowsIndexSearchAsync(
32-
QueryHelper.ConnectionString,
33-
QueryConstructor.FileContent(querySearchString),
34-
token);
31+
try
32+
{
33+
return WindowsIndex.WindowsIndexSearchAsync(
34+
QueryConstructor.CreateQueryHelper().ConnectionString,
35+
QueryConstructor.FileContent(querySearchString),
36+
token);
37+
}
38+
catch (COMException)
39+
{
40+
// Occurs when the Windows Indexing (WSearch) is turned off in services and unable to be used by Explorer plugin
41+
// Thrown by QueryConstructor.CreateQueryHelper()
42+
return HandledEngineNotAvailableExceptionAsync();
43+
}
3544
}
3645

3746
private IAsyncEnumerable<SearchResult> WindowsIndexFilesAndFoldersSearchAsync(
3847
ReadOnlySpan<char> querySearchString,
3948
CancellationToken token = default)
4049
{
41-
return WindowsIndex.WindowsIndexSearchAsync(
42-
QueryHelper.ConnectionString,
43-
QueryConstructor.FilesAndFolders(querySearchString),
44-
token);
50+
try
51+
{
52+
return WindowsIndex.WindowsIndexSearchAsync(
53+
QueryConstructor.CreateQueryHelper().ConnectionString,
54+
QueryConstructor.FilesAndFolders(querySearchString),
55+
token);
56+
}
57+
catch (COMException)
58+
{
59+
// Occurs when the Windows Indexing (WSearch) is turned off in services and unable to be used by Explorer plugin
60+
// Thrown by QueryConstructor.CreateQueryHelper()
61+
return HandledEngineNotAvailableExceptionAsync();
62+
}
4563
}
4664

4765
private IAsyncEnumerable<SearchResult> WindowsIndexTopLevelFolderSearchAsync(
@@ -50,12 +68,19 @@ private IAsyncEnumerable<SearchResult> WindowsIndexTopLevelFolderSearchAsync(
5068
bool recursive,
5169
CancellationToken token)
5270
{
53-
var queryConstructor = new QueryConstructor(Settings);
54-
55-
return WindowsIndex.WindowsIndexSearchAsync(
56-
QueryConstructor.CreateQueryHelper().ConnectionString,
57-
queryConstructor.Directory(path, search, recursive),
58-
token);
71+
try
72+
{
73+
return WindowsIndex.WindowsIndexSearchAsync(
74+
QueryConstructor.CreateQueryHelper().ConnectionString,
75+
QueryConstructor.Directory(path, search, recursive),
76+
token);
77+
}
78+
catch (COMException)
79+
{
80+
// Occurs when the Windows Indexing (WSearch) is turned off in services and unable to be used by Explorer plugin
81+
// Thrown by QueryConstructor.CreateQueryHelper()
82+
return HandledEngineNotAvailableExceptionAsync();
83+
}
5984
}
6085
public IAsyncEnumerable<SearchResult> SearchAsync(string search, CancellationToken token)
6186
{
@@ -69,5 +94,30 @@ public IAsyncEnumerable<SearchResult> EnumerateAsync(string path, string search,
6994
{
7095
return WindowsIndexTopLevelFolderSearchAsync(search, path, recursive, token);
7196
}
97+
98+
private IAsyncEnumerable<SearchResult> HandledEngineNotAvailableExceptionAsync()
99+
{
100+
if (!SearchManager.Settings.WarnWindowsSearchServiceOff)
101+
return AsyncEnumerable.Empty<SearchResult>();
102+
103+
var api = SearchManager.Context.API;
104+
105+
throw new EngineNotAvailableException(
106+
"Windows Index",
107+
api.GetTranslation("plugin_explorer_windowsSearchServiceFix"),
108+
api.GetTranslation("plugin_explorer_windowsSearchServiceNotRunning"),
109+
c =>
110+
{
111+
SearchManager.Settings.WarnWindowsSearchServiceOff = false;
112+
113+
// Clears the warning message so user is not mistaken that it has not worked
114+
api.ChangeQuery(string.Empty);
115+
116+
return ValueTask.FromResult(false);
117+
})
118+
{
119+
ErrorIcon = Constants.WindowsIndexErrorImagePath
120+
};
121+
}
72122
}
73123
}

0 commit comments

Comments
 (0)