11// Copyright (c) Files Community
22// Licensed under the MIT License.
33
4+ using Files . Shared . Helpers ;
45using Microsoft . Extensions . Logging ;
56using System . IO ;
67using System . Text . RegularExpressions ;
@@ -66,7 +67,8 @@ public string AQSQuery
6667 }
6768 else
6869 {
69- return $ "System.FileName:\" { QueryWithWildcard } \" ";
70+ var escaped = QueryWithWildcard . Replace ( "\" " , "\\ \" " ) ;
71+ return QueryWithWildcard . Contains ( ' ' ) ? $ "System.FileName:\" { escaped } \" " : $ "System.FileName:{ QueryWithWildcard } ";
7072 }
7173 }
7274 }
@@ -377,7 +379,7 @@ private async Task AddItemsAsync(string folder, IList<ListedItem> results, Cance
377379 hiddenOnlyFromWin32 = ( results . Count != 0 ) ;
378380 }
379381
380- if ( ! IsAQSQuery && ( ! hiddenOnlyFromWin32 || UserSettingsService . FoldersSettingsService . ShowHiddenItems ) )
382+ if ( ! IsAQSQuery )
381383 {
382384 await SearchWithWin32Async ( folder , hiddenOnlyFromWin32 , UsedMaxItemCount - ( uint ) results . Count , results , token ) ;
383385 }
@@ -390,11 +392,13 @@ private async Task SearchWithWin32Async(string folder, bool hiddenOnly, uint max
390392 ( IntPtr hFile , WIN32_FIND_DATA findData ) = await Task . Run ( ( ) =>
391393 {
392394 int additionalFlags = Win32PInvoke . FIND_FIRST_EX_LARGE_FETCH ;
393- IntPtr hFileTsk = Win32PInvoke . FindFirstFileExFromApp ( $ "{ folder } \\ { QueryWithWildcard } ", Win32PInvoke . FINDEX_INFO_LEVELS . FindExInfoBasic ,
395+ IntPtr hFileTsk = Win32PInvoke . FindFirstFileExFromApp ( $ "{ folder } \\ * { QueryWithWildcard } ", Win32PInvoke . FINDEX_INFO_LEVELS . FindExInfoBasic ,
394396 out WIN32_FIND_DATA findDataTsk , Win32PInvoke . FINDEX_SEARCH_OPS . FindExSearchNameMatch , IntPtr . Zero , additionalFlags ) ;
395397 return ( hFileTsk , findDataTsk ) ;
396398 } ) . WithTimeoutAsync ( TimeSpan . FromSeconds ( 5 ) ) ;
397399
400+ var pendingShortcuts = new List < ( string Path , WIN32_FIND_DATA FindData ) > ( ) ;
401+
398402 if ( hFile != IntPtr . Zero && hFile . ToInt64 ( ) != - 1 )
399403 {
400404 await Task . Run ( ( ) =>
@@ -411,18 +415,26 @@ await Task.Run(() =>
411415 var isSystem = ( ( FileAttributes ) findData . dwFileAttributes & FileAttributes . System ) == FileAttributes . System ;
412416 var isHidden = ( ( FileAttributes ) findData . dwFileAttributes & FileAttributes . Hidden ) == FileAttributes . Hidden ;
413417 var startWithDot = findData . cFileName . StartsWith ( '.' ) ;
418+ var isShortcut = FileExtensionHelpers . IsShortcutOrUrlFile ( findData . cFileName ) ;
414419
415420 bool shouldBeListed = ( hiddenOnly ?
416- isHidden && ( ! isSystem || ! UserSettingsService . FoldersSettingsService . ShowProtectedSystemFiles ) :
421+ ( ! isHidden && isShortcut ) || ( isHidden && UserSettingsService . FoldersSettingsService . ShowHiddenItems && ( ! isSystem || UserSettingsService . FoldersSettingsService . ShowProtectedSystemFiles ) ) :
417422 ! isHidden || ( UserSettingsService . FoldersSettingsService . ShowHiddenItems && ( ! isSystem || UserSettingsService . FoldersSettingsService . ShowProtectedSystemFiles ) ) ) &&
418423 ( ! startWithDot || UserSettingsService . FoldersSettingsService . ShowDotFiles ) ;
419424
420425 if ( shouldBeListed )
421426 {
422- var item = GetListedItemAsync ( itemPath , findData ) ;
423- if ( item is not null )
427+ if ( isShortcut )
428+ {
429+ pendingShortcuts . Add ( ( itemPath , findData ) ) ;
430+ }
431+ else
424432 {
425- results . Add ( item ) ;
433+ var item = GetListedItemAsync ( itemPath , findData ) ;
434+ if ( item is not null )
435+ {
436+ results . Add ( item ) ;
437+ }
426438 }
427439 }
428440
@@ -442,6 +454,121 @@ await Task.Run(() =>
442454 Win32PInvoke . FindClose ( hFile ) ;
443455 } , token ) ;
444456 }
457+
458+ foreach ( var ( itemPath , itemFindData ) in pendingShortcuts )
459+ {
460+ if ( results . Count >= maxItemCount || token . IsCancellationRequested )
461+ break ;
462+
463+ var isUrl = FileExtensionHelpers . IsWebLinkFile ( itemFindData . cFileName ) ;
464+ var shortcutFindData = itemFindData ;
465+ var isHidden = ( ( FileAttributes ) shortcutFindData . dwFileAttributes & FileAttributes . Hidden ) == FileAttributes . Hidden ;
466+ Win32PInvoke . FileTimeToSystemTime ( ref shortcutFindData . ftLastWriteTime , out Win32PInvoke . SYSTEMTIME modifiedTime ) ;
467+ Win32PInvoke . FileTimeToSystemTime ( ref shortcutFindData . ftCreationTime , out Win32PInvoke . SYSTEMTIME createdTime ) ;
468+ var fileSize = Win32FindDataExtensions . GetSize ( shortcutFindData ) ;
469+ var itemFileExtension = shortcutFindData . cFileName . Contains ( '.' , StringComparison . Ordinal ) ? Path . GetExtension ( itemPath ) : null ;
470+
471+ var shortcutItem = new ShortcutItem ( null )
472+ {
473+ PrimaryItemAttribute = StorageItemTypes . File ,
474+ FileExtension = itemFileExtension ,
475+ IsHiddenItem = isHidden ,
476+ Opacity = isHidden ? Constants . UI . DimItemOpacity : 1 ,
477+ FileImage = null ,
478+ LoadFileIcon = false ,
479+ NeedsPlaceholderGlyph = false ,
480+ ItemNameRaw = shortcutFindData . cFileName ,
481+ ItemDateModifiedReal = modifiedTime . ToDateTime ( ) ,
482+ ItemDateCreatedReal = createdTime . ToDateTime ( ) ,
483+ ItemType = isUrl ? Strings . ShortcutWebLinkFileType . GetLocalizedResource ( ) : Strings . Shortcut . GetLocalizedResource ( ) ,
484+ ItemPath = itemPath ,
485+ FileSize = fileSize . ToSizeString ( ) ,
486+ FileSizeBytes = fileSize ,
487+ IsUrl = isUrl ,
488+ } ;
489+
490+ if ( results . Any ( r => string . Equals ( r . ItemPath , itemPath , StringComparison . OrdinalIgnoreCase ) ) )
491+ continue ;
492+
493+ if ( MaxItemCount == 0 )
494+ {
495+ _ = FileOperationsHelpers . ParseLinkAsync ( itemPath ) . ContinueWith ( ( t ) =>
496+ {
497+ if ( t . IsCompletedSuccessfully && t . Result is not null )
498+ {
499+ _ = FilesystemTasks . Wrap ( ( ) => MainWindow . Instance . DispatcherQueue . EnqueueOrInvokeAsync ( ( ) =>
500+ {
501+ shortcutItem . TargetPath = t . Result . TargetPath ;
502+ shortcutItem . Arguments = t . Result . Arguments ;
503+ shortcutItem . WorkingDirectory = t . Result . WorkingDirectory ;
504+ shortcutItem . RunAsAdmin = t . Result . RunAsAdmin ;
505+ shortcutItem . ShowWindowCommand = t . Result . ShowWindowCommand ;
506+ shortcutItem . PrimaryItemAttribute = t . Result . IsFolder ? StorageItemTypes . Folder : StorageItemTypes . File ;
507+ } ) ) ;
508+ }
509+ } ) ;
510+ }
511+ else
512+ {
513+ var iconResult = await FileThumbnailHelper . GetIconAsync (
514+ itemPath ,
515+ Constants . ShellIconSizes . Small ,
516+ false ,
517+ IconOptions . ReturnIconOnly | IconOptions . UseCurrentScale ) ;
518+ if ( iconResult is not null )
519+ shortcutItem . FileImage = await iconResult . ToBitmapAsync ( ) ;
520+ else
521+ shortcutItem . NeedsPlaceholderGlyph = true ;
522+ }
523+
524+ results . Add ( shortcutItem ) ;
525+
526+ if ( results . Count == 32 || results . Count % 300 == 0 )
527+ {
528+ SearchTick ? . Invoke ( this , EventArgs . Empty ) ;
529+ }
530+ }
531+
532+ ( IntPtr hSubDir , WIN32_FIND_DATA subDirData ) = await Task . Run ( ( ) =>
533+ {
534+ int additionalFlags = Win32PInvoke . FIND_FIRST_EX_LARGE_FETCH ;
535+ IntPtr hSubDirTsk = Win32PInvoke . FindFirstFileExFromApp ( $ "{ folder } \\ *", Win32PInvoke . FINDEX_INFO_LEVELS . FindExInfoBasic ,
536+ out WIN32_FIND_DATA subDirDataTsk , Win32PInvoke . FINDEX_SEARCH_OPS . FindExSearchNameMatch , IntPtr . Zero , additionalFlags ) ;
537+ return ( hSubDirTsk , subDirDataTsk ) ;
538+ } ) . WithTimeoutAsync ( TimeSpan . FromSeconds ( 5 ) ) ;
539+
540+ if ( hSubDir != IntPtr . Zero && hSubDir . ToInt64 ( ) != - 1 )
541+ {
542+ var subDirectories = new List < string > ( ) ;
543+
544+ await Task . Run ( ( ) =>
545+ {
546+ var hasNextDir = false ;
547+ do
548+ {
549+ var isDirectory = ( ( FileAttributes ) subDirData . dwFileAttributes & FileAttributes . Directory ) == FileAttributes . Directory ;
550+ if ( isDirectory && subDirData . cFileName != "." && subDirData . cFileName != ".." )
551+ {
552+ subDirectories . Add ( Path . Combine ( folder , subDirData . cFileName ) ) ;
553+ }
554+
555+ if ( token . IsCancellationRequested )
556+ break ;
557+
558+ hasNextDir = Win32PInvoke . FindNextFile ( hSubDir , out subDirData ) ;
559+ } while ( hasNextDir ) ;
560+
561+ Win32PInvoke . FindClose ( hSubDir ) ;
562+ } , token ) ;
563+
564+ foreach ( var subDir in subDirectories )
565+ {
566+ if ( results . Count >= maxItemCount || token . IsCancellationRequested )
567+ break ;
568+
569+ await SearchWithWin32Async ( subDir , hiddenOnly , maxItemCount - ( uint ) results . Count , results , token ) ;
570+ }
571+ }
445572 }
446573
447574 private ListedItem GetListedItemAsync ( string itemPath , WIN32_FIND_DATA findData )
@@ -596,6 +723,47 @@ private async Task<ListedItem> GetListedItemAsync(IStorageItem item)
596723 ItemOriginalPath = binFile . OriginalPath
597724 } ;
598725 }
726+ else if ( FileExtensionHelpers . IsShortcutOrUrlFile ( file . Path ) )
727+ {
728+ var isUrl = FileExtensionHelpers . IsWebLinkFile ( file . Path ) ;
729+ var shortcutItem = new ShortcutItem ( null )
730+ {
731+ PrimaryItemAttribute = StorageItemTypes . File ,
732+ FileExtension = itemFileExtension ,
733+ IsHiddenItem = false ,
734+ Opacity = 1 ,
735+ FileImage = null ,
736+ LoadFileIcon = false ,
737+ NeedsPlaceholderGlyph = false ,
738+ ItemNameRaw = file . Name ,
739+ ItemDateModifiedReal = props . DateModified ,
740+ ItemDateCreatedReal = file . DateCreated ,
741+ ItemType = isUrl ? Strings . ShortcutWebLinkFileType . GetLocalizedResource ( ) : Strings . Shortcut . GetLocalizedResource ( ) ,
742+ ItemPath = file . Path ,
743+ FileSize = itemSize ,
744+ FileSizeBytes = ( long ) props . Size ,
745+ IsUrl = isUrl ,
746+ } ;
747+ if ( MaxItemCount == 0 )
748+ {
749+ _ = FileOperationsHelpers . ParseLinkAsync ( file . Path ) . ContinueWith ( ( t ) =>
750+ {
751+ if ( t . IsCompletedSuccessfully && t . Result is not null )
752+ {
753+ _ = FilesystemTasks . Wrap ( ( ) => MainWindow . Instance . DispatcherQueue . EnqueueOrInvokeAsync ( ( ) =>
754+ {
755+ shortcutItem . TargetPath = t . Result . TargetPath ;
756+ shortcutItem . Arguments = t . Result . Arguments ;
757+ shortcutItem . WorkingDirectory = t . Result . WorkingDirectory ;
758+ shortcutItem . RunAsAdmin = t . Result . RunAsAdmin ;
759+ shortcutItem . ShowWindowCommand = t . Result . ShowWindowCommand ;
760+ shortcutItem . PrimaryItemAttribute = t . Result . IsFolder ? StorageItemTypes . Folder : StorageItemTypes . File ;
761+ } ) ) ;
762+ }
763+ } ) ;
764+ }
765+ listedItem = shortcutItem ;
766+ }
599767 else
600768 {
601769 listedItem = new ListedItem ( null )
0 commit comments