Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -304,4 +304,5 @@ Output-Performance.txt

# vscode
.vscode
.history
.history
/.copilot
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ public static void Load(string directory)
[DllImport(Dll, CharSet = CharSet.Unicode)]
internal static extern IntPtr Everything3_ConnectW(string instanceName);

[DllImport(Dll)]
internal static extern uint Everything3_GetMajorVersion(IntPtr client);

[DllImport(Dll)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool Everything3_DestroyClient(IntPtr client);
Expand Down Expand Up @@ -129,7 +132,6 @@ public static void Load(string directory)
[DllImport(Dll)]
internal static extern uint Everything3_GetLastError();


[DllImport(Dll)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool Everything3_IsPropertyFastSort(IntPtr client, uint propertyId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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();
}
}

Expand All @@ -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
{
Expand All @@ -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
Expand All @@ -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);
Comment on lines +135 to +136

Copy link
Copy Markdown
Contributor Author

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.

}
finally
catch (IPCErrorException)
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
{
_ = Everything3ApiDllImport.Everything3_DestroyClient(client);
DestroyEverythingClient(_client);
throw;
}
}
}
Expand All @@ -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();
Expand All @@ -234,6 +257,8 @@ private static async IAsyncEnumerable<SearchResult> SearchWithEverything3Async(I

yield return result;
}

completed = true;
}
finally
{
Expand All @@ -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;

Expand All @@ -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();
Expand All @@ -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);
Expand All @@ -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
Expand All @@ -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(
Expand All @@ -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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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
Check if this issue is valid — if so, understand the root cause and fix it. At Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingApiV3.cs, line 368:

<comment>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.</comment>

<file context>
@@ -363,7 +365,7 @@ private bool EverythingClientConnected()
             }
 
-            return _client != IntPtr.Zero && Everything3ApiDllImport.Everything3_GetMajorVersion(_client) != 0;
+            return _client != IntPtr.Zero;
         }
 
</file context>
Suggested change
return _client != IntPtr.Zero;
return _client != IntPtr.Zero && Everything3ApiDllImport.Everything3_GetMajorVersion(_client) != 0;

}

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>
Expand Down Expand Up @@ -451,7 +494,7 @@ private static bool TryConvertSortOption(EverythingSortOption sortOption, out ui
}
}

private static void CheckAndThrowExceptionOnErrorFromEverything3()
private void CheckAndThrowExceptionOnErrorFromEverything3()
{
switch (Everything3ApiDllImport.Everything3_GetLastError())
{
Expand All @@ -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();
Comment thread
Copilot marked this conversation as resolved.
Comment thread
VictoriousRaptor marked this conversation as resolved.
Comment thread
VictoriousRaptor marked this conversation as resolved.
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,7 @@ public async IAsyncEnumerable<SearchResult> ContentSearchAsync(string plainSearc
IsRunCounterEnabled: Settings.EverythingEnableRunCount);

await foreach (var result in api.SearchAsync(option, token))
{
yield return result;
}
}

public async IAsyncEnumerable<SearchResult> EnumerateAsync(string path, string search, bool recursive, [EnumeratorCancellation] CancellationToken token)
Expand Down Expand Up @@ -119,7 +117,6 @@ public void InitializeApi(string sdkDirectory)

private async Task EnsureAvailableAsync(CancellationToken token)
{
var engineName = Enum.GetName(Settings.IndexSearchEngineOption.Everything)!;
try
{
await api.CheckAvailableAsync(token);
Expand All @@ -128,28 +125,42 @@ private async Task EnsureAvailableAsync(CancellationToken token)
{
// ignore, the search was cancelled
}
catch (Exceptions.IPCErrorException) when (api is LegacyEverythingApi)
catch (Exception ex) when (IsAvailabilityException(ex))
{
throw new EngineNotAvailableException(engineName,
throw WrapEngineNotAvailableException(ex);
}
}

internal static bool IsAvailabilityException(Exception exception) =>
exception is Exceptions.IPCErrorException ||
exception is DllNotFoundException ||
exception is EntryPointNotFoundException;

internal EngineNotAvailableException WrapEngineNotAvailableException(Exception exception)
{
var engineName = Enum.GetName(Settings.IndexSearchEngineOption.Everything)!;

if (exception is Exceptions.IPCErrorException && api is LegacyEverythingApi)
{
return new EngineNotAvailableException(engineName,
Localize.flowlauncher_plugin_everything_click_to_launch_or_install(),
Localize.flowlauncher_plugin_everything_is_not_running(),
Constants.EverythingErrorImagePath,
ClickToInstallEverythingAsync);
}
catch (Exceptions.IPCErrorException)

if (exception is Exceptions.IPCErrorException)
{
throw new EngineNotAvailableException(engineName,
return new EngineNotAvailableException(engineName,
Localize.flowlauncher_plugin_everything_15_resolution(),
Localize.flowlauncher_plugin_everything_15_unavailable(),
Constants.EverythingErrorImagePath);
}
catch (Exception ex) when (ex is DllNotFoundException || ex is EntryPointNotFoundException)
{
throw new EngineNotAvailableException(engineName,
Localize.flowlauncher_plugin_everything_architecture_check(),
Constants.GeneralSearchErrorImagePath,
Localize.flowlauncher_plugin_everything_sdk_issue());
}

return new EngineNotAvailableException(engineName,
Localize.flowlauncher_plugin_everything_architecture_check(),
Constants.GeneralSearchErrorImagePath,
Localize.flowlauncher_plugin_everything_sdk_issue());
}

private async ValueTask<bool> ClickToInstallEverythingAsync(ActionContext _)
Expand Down
Loading