2025-07-16 13:28:17 +00:00
using Flow.Launcher.Plugin.Explorer.Search.Everything.Exceptions ;
2022-03-25 21:19:00 +00:00
using System ;
using System.Collections.Generic ;
2022-08-16 22:45:36 +00:00
using System.Runtime.CompilerServices ;
2022-03-25 21:19:00 +00:00
using System.Text ;
using System.Threading ;
using System.Threading.Tasks ;
namespace Flow.Launcher.Plugin.Explorer.Search.Everything
{
public static class EverythingApi
{
private const int BufferSize = 4096 ;
2023-03-03 12:35:08 +00:00
private static readonly SemaphoreSlim _semaphore = new ( 1 , 1 ) ;
2023-01-26 07:19:42 +00:00
2022-03-25 21:19:00 +00:00
// cached buffer to remove redundant allocations.
2022-12-03 19:44:43 +00:00
private static readonly StringBuilder buffer = new ( BufferSize ) ;
2022-03-25 21:19:00 +00:00
public enum StateCode
{
OK ,
MemoryError ,
IPCError ,
RegisterClassExError ,
CreateWindowError ,
CreateThreadError ,
InvalidIndexError ,
InvalidCallError
}
2023-03-05 10:24:42 +00:00
const uint EVERYTHING_REQUEST_FULL_PATH_AND_FILE_NAME = 0x00000004 u ;
const uint EVERYTHING_REQUEST_RUN_COUNT = 0x00000400 u ;
2022-03-25 21:19:00 +00:00
/// <summary>
/// Checks whether the sort option is Fast Sort.
/// </summary>
2025-07-16 13:28:17 +00:00
public static bool IsFastSortOption ( EverythingSortOption sortOption )
2022-03-25 21:19:00 +00:00
{
var fastSortOptionEnabled = EverythingApiDllImport . Everything_IsFastSort ( sortOption ) ;
// If the Everything service is not running, then this call will incorrectly report
// the state as false. This checks for errors thrown by the api and up to the caller to handle.
CheckAndThrowExceptionOnError ( ) ;
return fastSortOptionEnabled ;
}
2022-11-25 19:25:12 +00:00
public static async ValueTask < bool > IsEverythingRunningAsync ( CancellationToken token = default )
{
2025-11-06 12:46:29 +00:00
try
{
await _semaphore . WaitAsync ( token ) ;
}
catch ( OperationCanceledException )
{
return false ;
}
2022-12-03 19:50:30 +00:00
2022-11-29 03:38:17 +00:00
try
{
2025-11-06 07:28:13 +00:00
_ = EverythingApiDllImport . Everything_GetMajorVersion ( ) ;
2022-11-29 03:38:17 +00:00
var result = EverythingApiDllImport . Everything_GetLastError ( ) ! = StateCode . IPCError ;
return result ;
}
finally
{
_semaphore . Release ( ) ;
}
2022-11-25 19:25:12 +00:00
}
2022-03-25 21:19:00 +00:00
/// <summary>
/// Searches the specified key word and reset the everything API afterwards
/// </summary>
2022-08-16 22:45:36 +00:00
/// <param name="option">Search Criteria</param>
2022-03-25 21:19:00 +00:00
/// <param name="token">when cancelled the current search will stop and exit (and would not reset)</param>
2022-08-16 22:45:36 +00:00
/// <returns>An IAsyncEnumerable that will enumerate all results searched by the specific query and option</returns>
public static async IAsyncEnumerable < SearchResult > SearchAsync ( EverythingSearchOption option ,
[EnumeratorCancellation] CancellationToken token )
2022-03-25 21:19:00 +00:00
{
2022-05-15 20:15:56 +00:00
if ( option . Offset < 0 )
throw new ArgumentOutOfRangeException ( nameof ( option . Offset ) , option . Offset , "Offset must be greater than or equal to 0" ) ;
2022-03-25 21:19:00 +00:00
2022-05-15 20:15:56 +00:00
if ( option . MaxCount < 0 )
throw new ArgumentOutOfRangeException ( nameof ( option . MaxCount ) , option . MaxCount , "MaxCount must be greater than or equal to 0" ) ;
2023-03-03 12:35:08 +00:00
2025-11-06 12:47:54 +00:00
try
{
await _semaphore . WaitAsync ( token ) ;
}
catch ( OperationCanceledException )
{
yield break ;
}
2023-01-26 07:19:42 +00:00
2022-08-16 22:45:36 +00:00
try
2022-03-25 21:19:00 +00:00
{
2022-12-03 19:44:43 +00:00
if ( token . IsCancellationRequested )
yield break ;
2022-11-25 19:25:12 +00:00
2022-05-15 20:15:56 +00:00
if ( option . Keyword . StartsWith ( "@" ) )
2022-03-25 21:19:00 +00:00
{
EverythingApiDllImport . Everything_SetRegex ( true ) ;
2022-05-15 20:15:56 +00:00
option . Keyword = option . Keyword [ 1. . ] ;
2022-03-25 21:19:00 +00:00
}
2022-11-25 19:25:12 +00:00
2022-09-22 00:18:20 +00:00
var builder = new StringBuilder ( ) ;
builder . Append ( option . Keyword ) ;
2022-03-25 21:19:00 +00:00
2022-09-24 19:23:59 +00:00
if ( ! string . IsNullOrWhiteSpace ( option . ParentPath ) )
2022-03-28 20:28:18 +00:00
{
2022-09-22 00:18:20 +00:00
builder . Append ( $" {(option.IsRecursive ? "" : " parent : ")}\"{option.ParentPath}\"" ) ;
2022-03-28 20:28:18 +00:00
}
2022-05-15 20:15:56 +00:00
if ( option . IsContentSearch )
{
2022-09-22 00:18:20 +00:00
builder . Append ( $" content:\" { option . ContentSearchKeyword } \ "" ) ;
2022-05-15 20:15:56 +00:00
}
2022-09-22 00:18:20 +00:00
EverythingApiDllImport . Everything_SetSearchW ( builder . ToString ( ) ) ;
2022-05-15 20:15:56 +00:00
EverythingApiDllImport . Everything_SetOffset ( option . Offset ) ;
EverythingApiDllImport . Everything_SetMax ( option . MaxCount ) ;
2022-03-25 21:19:00 +00:00
2022-05-15 20:15:56 +00:00
EverythingApiDllImport . Everything_SetSort ( option . SortOption ) ;
2023-01-26 06:55:25 +00:00
EverythingApiDllImport . Everything_SetMatchPath ( option . IsFullPathSearch ) ;
2023-03-15 18:53:14 +00:00
2025-07-16 13:28:17 +00:00
if ( option . SortOption = = EverythingSortOption . RUN_COUNT_DESCENDING )
2023-03-08 11:00:00 +00:00
{
EverythingApiDllImport . Everything_SetRequestFlags ( EVERYTHING_REQUEST_FULL_PATH_AND_FILE_NAME | EVERYTHING_REQUEST_RUN_COUNT ) ;
}
else
{
EverythingApiDllImport . Everything_SetRequestFlags ( EVERYTHING_REQUEST_FULL_PATH_AND_FILE_NAME ) ;
}
2022-03-25 21:19:00 +00:00
2022-08-16 22:45:36 +00:00
if ( token . IsCancellationRequested ) yield break ;
2022-03-25 21:19:00 +00:00
if ( ! EverythingApiDllImport . Everything_QueryW ( true ) )
{
CheckAndThrowExceptionOnError ( ) ;
2022-08-16 22:45:36 +00:00
yield break ;
2022-03-25 21:19:00 +00:00
}
2023-03-15 18:53:14 +00:00
for ( var idx = 0 ; idx < EverythingApiDllImport . Everything_GetNumResults ( ) ; + + idx )
2022-03-25 21:19:00 +00:00
{
if ( token . IsCancellationRequested )
{
2022-08-16 22:45:36 +00:00
yield break ;
2022-03-25 21:19:00 +00:00
}
EverythingApiDllImport . Everything_GetResultFullPathNameW ( idx , buffer , BufferSize ) ;
var result = new SearchResult
{
2023-03-14 07:21:11 +00:00
// todo the types are wrong. Everything expects uint everywhere, but we send int just above/below. how to fix? Is EverythingApiDllImport autogenerated or handmade?
2022-03-25 21:19:00 +00:00
FullPath = buffer . ToString ( ) ,
Type = EverythingApiDllImport . Everything_IsFolderResult ( idx ) ? ResultType . Folder :
EverythingApiDllImport . Everything_IsFileResult ( idx ) ? ResultType . File :
2023-03-05 10:24:42 +00:00
ResultType . Volume ,
2023-03-15 18:53:14 +00:00
Score = ( int ) EverythingApiDllImport . Everything_GetResultRunCount ( ( uint ) idx )
2022-03-25 21:19:00 +00:00
} ;
2022-08-16 22:45:36 +00:00
yield return result ;
2022-03-25 21:19:00 +00:00
}
2022-08-16 22:45:36 +00:00
}
finally
{
EverythingApiDllImport . Everything_Reset ( ) ;
_semaphore . Release ( ) ;
2022-03-25 21:19:00 +00:00
}
}
private static void CheckAndThrowExceptionOnError ( )
{
switch ( EverythingApiDllImport . Everything_GetLastError ( ) )
{
case StateCode . CreateThreadError :
throw new CreateThreadException ( ) ;
case StateCode . CreateWindowError :
throw new CreateWindowException ( ) ;
case StateCode . InvalidCallError :
throw new InvalidCallException ( ) ;
case StateCode . InvalidIndexError :
throw new InvalidIndexException ( ) ;
case StateCode . IPCError :
throw new IPCErrorException ( ) ;
case StateCode . MemoryError :
throw new MemoryErrorException ( ) ;
case StateCode . RegisterClassExError :
throw new RegisterClassExException ( ) ;
2022-08-16 22:45:36 +00:00
case StateCode . OK :
break ;
default :
throw new ArgumentOutOfRangeException ( ) ;
2022-03-25 21:19:00 +00:00
}
}
2023-03-03 12:36:36 +00:00
public static async Task IncrementRunCounterAsync ( string fileOrFolder )
{
2023-03-08 10:10:43 +00:00
await _semaphore . WaitAsync ( TimeSpan . FromSeconds ( 1 ) ) ;
2023-03-03 12:36:36 +00:00
try
{
2023-03-03 12:56:20 +00:00
_ = EverythingApiDllImport . Everything_IncRunCountFromFileName ( fileOrFolder ) ;
2023-03-03 12:36:36 +00:00
}
catch ( Exception )
{
/*ignored*/
}
finally { _semaphore . Release ( ) ; }
}
2022-03-25 21:19:00 +00:00
}
2022-09-10 15:45:41 +00:00
}