-
-
Notifications
You must be signed in to change notification settings - Fork 652
Improve Everything client validation and error handling #4641
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -304,4 +304,5 @@ Output-Performance.txt | |
|
|
||
| # vscode | ||
| .vscode | ||
| .history | ||
| .history | ||
| /.copilot | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -12,10 +12,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything | |||||
| public class EverythingApiV3 : IEverythingApi | ||||||
| { | ||||||
| private const int BufferSize = 4096; | ||||||
| private static readonly SemaphoreSlim _semaphore = new(1, 1); | ||||||
| private static readonly StringBuilder _buffer = new(BufferSize); | ||||||
| private readonly SemaphoreSlim _semaphore = new(1, 1); | ||||||
| private readonly StringBuilder _buffer = new(BufferSize); | ||||||
|
|
||||||
| private readonly string _instanceName; | ||||||
| private IntPtr _client; | ||||||
|
|
||||||
| public EverythingApiV3(string instanceName) | ||||||
| { | ||||||
|
|
@@ -44,11 +45,12 @@ public EverythingApiV3(string instanceName) | |||||
| const uint EVERYTHING3_ERROR_PROPERTY_NOT_FOUND = 0xE0000007; | ||||||
| const uint EVERYTHING3_OK = 0; | ||||||
|
|
||||||
| private static void LogIfEverything3CallFailed(string callName, bool succeeded) | ||||||
| private void CheckEverything3Call(string callName, bool succeeded) | ||||||
| { | ||||||
| if (!succeeded) | ||||||
| { | ||||||
| Main.Context?.API?.LogDebug(nameof(EverythingApiV3), $"{callName} failed"); | ||||||
| CheckAndThrowExceptionOnErrorFromEverything3(); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
|
|
@@ -71,9 +73,13 @@ public async Task CheckAvailableAsync(CancellationToken token = default) | |||||
|
|
||||||
| try | ||||||
| { | ||||||
| if (!TryConnectEverything3(out var client)) | ||||||
| token.ThrowIfCancellationRequested(); | ||||||
|
|
||||||
| if (!EverythingClientConnected()) | ||||||
| { | ||||||
| _client = IntPtr.Zero; | ||||||
| throw new IPCErrorException(); | ||||||
| _ = Everything3ApiDllImport.Everything3_DestroyClient(client); | ||||||
| } | ||||||
| } | ||||||
| finally | ||||||
| { | ||||||
|
|
@@ -100,10 +106,10 @@ private async IAsyncEnumerable<SearchResult> SearchCoreAsync(EverythingSearchOpt | |||||
| if (token.IsCancellationRequested) | ||||||
| yield break; | ||||||
|
|
||||||
| if (!TryConnectEverything3(out var client)) | ||||||
| if (!EverythingClientConnected()) | ||||||
| throw new IPCErrorException(); | ||||||
|
|
||||||
| await foreach (var result in SearchWithEverything3Async(client, preparedOption, query, token)) | ||||||
| await foreach (var result in SearchWithEverything3Async(preparedOption, query, token)) | ||||||
| yield return result; | ||||||
| } | ||||||
| finally | ||||||
|
|
@@ -122,15 +128,17 @@ public async Task IncrementRunCounterAsync(string fileOrFolder) | |||||
| } | ||||||
| try | ||||||
| { | ||||||
| if (TryConnectEverything3(out var client)) | ||||||
| if (EverythingClientConnected()) | ||||||
| { | ||||||
| try | ||||||
| { | ||||||
| Everything3ApiDllImport.Everything3_IncRunCountFromFilenameW(client, fileOrFolder); | ||||||
| var incremented = Everything3ApiDllImport.Everything3_IncRunCountFromFilenameW(_client, fileOrFolder) != 0; | ||||||
| CheckEverything3Call(nameof(Everything3ApiDllImport.Everything3_IncRunCountFromFilenameW), incremented); | ||||||
| } | ||||||
| finally | ||||||
| catch (IPCErrorException) | ||||||
|
cubic-dev-ai[bot] marked this conversation as resolved.
|
||||||
| { | ||||||
| _ = Everything3ApiDllImport.Everything3_DestroyClient(client); | ||||||
| DestroyEverythingClient(_client); | ||||||
| throw; | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
|
|
@@ -146,75 +154,90 @@ public async Task IncrementRunCounterAsync(string fileOrFolder) | |||||
|
|
||||||
| public bool IsFastSortOption(EverythingSortOption sortOption) | ||||||
| { | ||||||
| if (!TryConnectEverything3(out var client)) | ||||||
| throw new IPCErrorException(); | ||||||
| if (!_semaphore.Wait(TimeSpan.FromSeconds(1))) | ||||||
| return false; | ||||||
|
Comment on lines
+157
to
+158
|
||||||
|
|
||||||
| try | ||||||
| { | ||||||
| if (!EverythingClientConnected()) | ||||||
| throw new IPCErrorException(); | ||||||
|
|
||||||
| if (TryConvertSortOption(sortOption, out var propertyId, out _)) | ||||||
| { | ||||||
| var isFastSort = Everything3ApiDllImport.Everything3_IsPropertyFastSort(client, propertyId); | ||||||
| var isFastSort = Everything3ApiDllImport.Everything3_IsPropertyFastSort(_client, propertyId); | ||||||
| CheckAndThrowExceptionOnErrorFromEverything3(); | ||||||
| return isFastSort; | ||||||
| } | ||||||
| } | ||||||
| catch (IPCErrorException) | ||||||
| { | ||||||
| DestroyEverythingClient(_client); | ||||||
| throw; | ||||||
| } | ||||||
| finally | ||||||
| { | ||||||
| _ = Everything3ApiDllImport.Everything3_DestroyClient(client); | ||||||
| _semaphore.Release(); | ||||||
| } | ||||||
|
|
||||||
| return false; | ||||||
| } | ||||||
|
|
||||||
| private static async IAsyncEnumerable<SearchResult> SearchWithEverything3Async(IntPtr client, | ||||||
| private async IAsyncEnumerable<SearchResult> SearchWithEverything3Async( | ||||||
| EverythingSearchOption option, | ||||||
| EverythingHelper.PreparedQuery query, | ||||||
| [EnumeratorCancellation] CancellationToken token) | ||||||
| { | ||||||
| IntPtr searchState = IntPtr.Zero; | ||||||
| IntPtr resultList = IntPtr.Zero; | ||||||
| var completed = false; | ||||||
| var includeRunCount = option.IsRunCounterEnabled || option.SortOption == EverythingSortOption.RUN_COUNT_DESCENDING || option.SortOption == EverythingSortOption.RUN_COUNT_ASCENDING; | ||||||
|
|
||||||
| try | ||||||
| { | ||||||
| if (token.IsCancellationRequested) | ||||||
| yield break; | ||||||
| searchState = Everything3ApiDllImport.Everything3_CreateSearchState(); | ||||||
| if (searchState == IntPtr.Zero) | ||||||
| { | ||||||
| CheckAndThrowExceptionOnErrorFromEverything3(); | ||||||
| yield break; | ||||||
| } | ||||||
|
|
||||||
| LogIfEverything3CallFailed(nameof(Everything3ApiDllImport.Everything3_SetSearchRegex), | ||||||
| CheckEverything3Call(nameof(Everything3ApiDllImport.Everything3_SetSearchRegex), | ||||||
| Everything3ApiDllImport.Everything3_SetSearchRegex(searchState, option.UseRegex)); | ||||||
| LogIfEverything3CallFailed(nameof(Everything3ApiDllImport.Everything3_SetSearchMatchPath), | ||||||
| CheckEverything3Call(nameof(Everything3ApiDllImport.Everything3_SetSearchMatchPath), | ||||||
| Everything3ApiDllImport.Everything3_SetSearchMatchPath(searchState, option.IsFullPathSearch)); | ||||||
| LogIfEverything3CallFailed(nameof(Everything3ApiDllImport.Everything3_SetSearchTextW), | ||||||
| CheckEverything3Call(nameof(Everything3ApiDllImport.Everything3_SetSearchTextW), | ||||||
| Everything3ApiDllImport.Everything3_SetSearchTextW(searchState, query.SearchText)); | ||||||
| LogIfEverything3CallFailed(nameof(Everything3ApiDllImport.Everything3_SetSearchHideResultOmissions), | ||||||
| CheckEverything3Call(nameof(Everything3ApiDllImport.Everything3_SetSearchHideResultOmissions), | ||||||
| Everything3ApiDllImport.Everything3_SetSearchHideResultOmissions(searchState, true)); | ||||||
| LogIfEverything3CallFailed(nameof(Everything3ApiDllImport.Everything3_SetSearchViewportOffset), | ||||||
| CheckEverything3Call(nameof(Everything3ApiDllImport.Everything3_SetSearchViewportOffset), | ||||||
| Everything3ApiDllImport.Everything3_SetSearchViewportOffset(searchState, (nuint)option.Offset)); | ||||||
| LogIfEverything3CallFailed(nameof(Everything3ApiDllImport.Everything3_SetSearchViewportCount), | ||||||
| CheckEverything3Call(nameof(Everything3ApiDllImport.Everything3_SetSearchViewportCount), | ||||||
| Everything3ApiDllImport.Everything3_SetSearchViewportCount(searchState, (nuint)option.MaxCount)); | ||||||
|
|
||||||
| if (TryConvertSortOption(option.SortOption, out var sortPropertyId, out var ascending)) | ||||||
| { | ||||||
| if (!Everything3ApiDllImport.Everything3_AddSearchSort(searchState, sortPropertyId, ascending)) | ||||||
| { | ||||||
| CheckAndThrowExceptionOnErrorFromEverything3(); | ||||||
| yield break; | ||||||
| CheckEverything3Call(nameof(Everything3ApiDllImport.Everything3_AddSearchSort), false); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| _ = Everything3ApiDllImport.Everything3_ClearSearchPropertyRequests(searchState); | ||||||
| _ = Everything3ApiDllImport.Everything3_AddSearchPropertyRequestHighlighted(searchState, EVERYTHING3_PROPERTY_ID_NAME); | ||||||
| _ = Everything3ApiDllImport.Everything3_AddSearchPropertyRequest(searchState, EVERYTHING3_PROPERTY_ID_PATH_AND_NAME); | ||||||
| CheckEverything3Call(nameof(Everything3ApiDllImport.Everything3_ClearSearchPropertyRequests), | ||||||
| Everything3ApiDllImport.Everything3_ClearSearchPropertyRequests(searchState)); | ||||||
| CheckEverything3Call(nameof(Everything3ApiDllImport.Everything3_AddSearchPropertyRequestHighlighted), | ||||||
| Everything3ApiDllImport.Everything3_AddSearchPropertyRequestHighlighted(searchState, EVERYTHING3_PROPERTY_ID_NAME)); | ||||||
| CheckEverything3Call(nameof(Everything3ApiDllImport.Everything3_AddSearchPropertyRequest), | ||||||
| Everything3ApiDllImport.Everything3_AddSearchPropertyRequest(searchState, EVERYTHING3_PROPERTY_ID_PATH_AND_NAME)); | ||||||
| if (includeRunCount) | ||||||
| _ = Everything3ApiDllImport.Everything3_AddSearchPropertyRequest(searchState, EVERYTHING3_PROPERTY_ID_RUN_COUNT); | ||||||
| { | ||||||
| CheckEverything3Call(nameof(Everything3ApiDllImport.Everything3_AddSearchPropertyRequest), | ||||||
| Everything3ApiDllImport.Everything3_AddSearchPropertyRequest(searchState, EVERYTHING3_PROPERTY_ID_RUN_COUNT)); | ||||||
| } | ||||||
| if (token.IsCancellationRequested) | ||||||
| yield break; | ||||||
|
|
||||||
| resultList = Everything3ApiDllImport.Everything3_Search(client, searchState); | ||||||
| resultList = Everything3ApiDllImport.Everything3_Search(_client, searchState); | ||||||
| if (resultList == IntPtr.Zero) | ||||||
| { | ||||||
| CheckAndThrowExceptionOnErrorFromEverything3(); | ||||||
|
|
@@ -234,6 +257,8 @@ private static async IAsyncEnumerable<SearchResult> SearchWithEverything3Async(I | |||||
|
|
||||||
| yield return result; | ||||||
| } | ||||||
|
|
||||||
| completed = true; | ||||||
| } | ||||||
| finally | ||||||
| { | ||||||
|
|
@@ -243,13 +268,14 @@ private static async IAsyncEnumerable<SearchResult> SearchWithEverything3Async(I | |||||
| if (searchState != IntPtr.Zero) | ||||||
| _ = Everything3ApiDllImport.Everything3_DestroySearchState(searchState); | ||||||
|
|
||||||
| _ = Everything3ApiDllImport.Everything3_DestroyClient(client); | ||||||
| if (!completed) | ||||||
| DestroyEverythingClient(_client); | ||||||
| } | ||||||
|
|
||||||
| await Task.CompletedTask; | ||||||
| } | ||||||
|
|
||||||
| private static bool TryCreateSearchResult(IntPtr resultList, nuint resultIndex, bool includeRunCount, out SearchResult result) | ||||||
| private bool TryCreateSearchResult(IntPtr resultList, nuint resultIndex, bool includeRunCount, out SearchResult result) | ||||||
| { | ||||||
| result = default; | ||||||
|
|
||||||
|
|
@@ -267,7 +293,7 @@ private static bool TryCreateSearchResult(IntPtr resultList, nuint resultIndex, | |||||
| return true; | ||||||
| } | ||||||
|
|
||||||
| private static int GetResultScore(IntPtr resultList, nuint resultIndex) | ||||||
| private int GetResultScore(IntPtr resultList, nuint resultIndex) | ||||||
| { | ||||||
| var runCount = Everything3ApiDllImport.Everything3_GetResultRunCount(resultList, resultIndex); | ||||||
| var lastError = Everything3ApiDllImport.Everything3_GetLastError(); | ||||||
|
|
@@ -288,7 +314,7 @@ private static int GetResultScore(IntPtr resultList, nuint resultIndex) | |||||
| return (int)runCount; | ||||||
| } | ||||||
|
|
||||||
| private static bool TryGetResultFullPath(IntPtr resultList, nuint resultIndex, out string fullPath) | ||||||
| private bool TryGetResultFullPath(IntPtr resultList, nuint resultIndex, out string fullPath) | ||||||
| { | ||||||
| _buffer.Clear(); | ||||||
| var fullPathLength = Everything3ApiDllImport.Everything3_GetResultFullPathNameW(resultList, resultIndex, _buffer, BufferSize); | ||||||
|
|
@@ -303,7 +329,7 @@ private static bool TryGetResultFullPath(IntPtr resultList, nuint resultIndex, o | |||||
| return !string.IsNullOrEmpty(fullPath); | ||||||
| } | ||||||
|
|
||||||
| private static ResultType GetResultType(IntPtr resultList, nuint resultIndex) | ||||||
| private ResultType GetResultType(IntPtr resultList, nuint resultIndex) | ||||||
| { | ||||||
| return Everything3ApiDllImport.Everything3_IsFolderResult(resultList, resultIndex) | ||||||
| ? ResultType.Folder | ||||||
|
|
@@ -312,7 +338,7 @@ private static ResultType GetResultType(IntPtr resultList, nuint resultIndex) | |||||
| : ResultType.File; | ||||||
| } | ||||||
|
|
||||||
| private static List<int> GetHighlightData(IntPtr resultList, nuint resultIndex) | ||||||
| private List<int> GetHighlightData(IntPtr resultList, nuint resultIndex) | ||||||
| { | ||||||
| _buffer.Clear(); | ||||||
| var highlightedFileNameLength = Everything3ApiDllImport.Everything3_GetResultPropertyTextHighlightedW( | ||||||
|
|
@@ -327,10 +353,27 @@ private static List<int> GetHighlightData(IntPtr resultList, nuint resultIndex) | |||||
| : []; | ||||||
| } | ||||||
|
|
||||||
| private bool TryConnectEverything3(out IntPtr client) | ||||||
| private bool EverythingClientConnected() | ||||||
| { | ||||||
| if (_client == IntPtr.Zero) | ||||||
| _client = Everything3ApiDllImport.Everything3_ConnectW(_instanceName); | ||||||
|
|
||||||
| if (_client == IntPtr.Zero || Everything3ApiDllImport.Everything3_GetMajorVersion(_client) == 0) | ||||||
| { | ||||||
| DestroyEverythingClient(_client); | ||||||
| _client = Everything3ApiDllImport.Everything3_ConnectW(_instanceName); | ||||||
| } | ||||||
|
|
||||||
| return _client != IntPtr.Zero; | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: When the first connection yields a handle whose major version is zero, this reconnects but accepts any nonzero second handle. A still-invalid handle makes availability checks succeed and sends subsequent native calls through it; re-run the version check on the reconnected client. Prompt for AI agents
Suggested change
|
||||||
| } | ||||||
|
|
||||||
| private void DestroyEverythingClient(IntPtr client) | ||||||
| { | ||||||
| client = Everything3ApiDllImport.Everything3_ConnectW(_instanceName); | ||||||
| return client != IntPtr.Zero; | ||||||
| if (client == IntPtr.Zero || client != _client) | ||||||
| return; | ||||||
|
|
||||||
| _ = Everything3ApiDllImport.Everything3_DestroyClient(_client); | ||||||
| _client = IntPtr.Zero; | ||||||
| } | ||||||
|
|
||||||
| /// <summary> | ||||||
|
|
@@ -451,7 +494,7 @@ private static bool TryConvertSortOption(EverythingSortOption sortOption, out ui | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| private static void CheckAndThrowExceptionOnErrorFromEverything3() | ||||||
| private void CheckAndThrowExceptionOnErrorFromEverything3() | ||||||
| { | ||||||
| switch (Everything3ApiDllImport.Everything3_GetLastError()) | ||||||
| { | ||||||
|
|
@@ -464,6 +507,10 @@ private static void CheckAndThrowExceptionOnErrorFromEverything3() | |||||
| throw new InvalidCallException(); | ||||||
| case EVERYTHING3_ERROR_PROPERTY_NOT_FOUND: | ||||||
| throw new ArgumentException("EVERYTHING3_ERROR_PROPERTY_NOT_FOUND"); | ||||||
| case EVERYTHING3_OK: | ||||||
| return; | ||||||
| default: | ||||||
| throw new InvalidCallException(); | ||||||
|
Copilot marked this conversation as resolved.
VictoriousRaptor marked this conversation as resolved.
VictoriousRaptor marked this conversation as resolved.
|
||||||
| } | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ignore. If uncatched will break result action.