1- using System . Collections . Concurrent ;
1+ // Copyright (c) 2024 Files Community
2+ // Licensed under the MIT License. See the LICENSE.
3+
4+ using System . Collections . Concurrent ;
25using System . IO ;
3- using Vanara . PInvoke ;
4- using static Vanara . PInvoke . Kernel32 ;
6+ using Windows . Win32 ;
7+ using Windows . Win32 . Storage . FileSystem ;
58
69namespace Files . App . Utils . Storage . Operations
710{
@@ -23,6 +26,14 @@ public FileSizeCalculator(params string[] paths)
2326 public async Task ComputeSizeAsync ( CancellationToken cancellationToken = default )
2427 {
2528 await Parallel . ForEachAsync ( _paths , cancellationToken , async ( path , token ) => await Task . Factory . StartNew ( ( ) =>
29+ {
30+ ComputeSizeRecursively ( path , token ) ;
31+ } ,
32+ token ,
33+ TaskCreationOptions . LongRunning ,
34+ TaskScheduler . Default ) ) ;
35+
36+ unsafe void ComputeSizeRecursively ( string path , CancellationToken token )
2637 {
2738 var queue = new Queue < string > ( ) ;
2839 if ( ! Win32Helper . HasFileAttribute ( path , FileAttributes . Directory ) )
@@ -35,76 +46,78 @@ await Parallel.ForEachAsync(_paths, cancellationToken, async (path, token) => aw
3546
3647 while ( queue . TryDequeue ( out var directory ) )
3748 {
38- using var hFile = FindFirstFileEx (
39- directory + "\\ *.*" ,
40- FINDEX_INFO_LEVELS . FindExInfoBasic ,
41- out WIN32_FIND_DATA findData ,
42- FINDEX_SEARCH_OPS . FindExSearchNameMatch ,
43- IntPtr . Zero ,
44- FIND_FIRST . FIND_FIRST_EX_LARGE_FETCH ) ;
45-
46- if ( ! hFile . IsInvalid )
49+ WIN32_FIND_DATAW * findData = default ;
50+
51+ fixed ( char * pszFilePath = directory + "\\ *.*" )
4752 {
48- do
53+ var hFile = PInvoke . FindFirstFileEx (
54+ pszFilePath ,
55+ FINDEX_INFO_LEVELS . FindExInfoBasic ,
56+ findData ,
57+ FINDEX_SEARCH_OPS . FindExSearchNameMatch ,
58+ null ,
59+ FIND_FIRST_EX_FLAGS . FIND_FIRST_EX_LARGE_FETCH ) ;
60+
61+ if ( ! hFile . IsNull )
4962 {
50- if ( ( findData . dwFileAttributes & FileAttributes . ReparsePoint ) == FileAttributes . ReparsePoint )
51- // Skip symbolic links and junctions
52- continue ;
63+ do
64+ {
65+ FILE_FLAGS_AND_ATTRIBUTES attributes = ( FILE_FLAGS_AND_ATTRIBUTES ) findData -> dwFileAttributes ;
5366
54- var itemPath = Path . Combine ( directory , findData . cFileName ) ;
67+ if ( attributes . HasFlag ( FILE_FLAGS_AND_ATTRIBUTES . FILE_ATTRIBUTE_REPARSE_POINT ) )
68+ // Skip symbolic links and junctions
69+ continue ;
5570
56- if ( ( findData . dwFileAttributes & FileAttributes . Directory ) != FileAttributes . Directory )
57- {
58- ComputeFileSize ( itemPath ) ;
59- }
60- else if ( findData . cFileName != "." && findData . cFileName != ".." )
61- {
62- queue . Enqueue ( itemPath ) ;
63- }
71+ var itemPath = Path . Combine ( directory , findData ->cFileName . ToString ( ) ) ;
72+
73+ if ( attributes . HasFlag ( FILE_FLAGS_AND_ATTRIBUTES . FILE_ATTRIBUTE_DIRECTORY ) )
74+ {
75+ ComputeFileSize ( itemPath ) ;
76+ }
77+ else if ( findData ->cFileName . ToString ( ) is string fileName &&
78+ fileName . Equals ( "." , StringComparison . OrdinalIgnoreCase ) &&
79+ fileName . Equals ( ".." , StringComparison . OrdinalIgnoreCase ) )
80+ {
81+ queue . Enqueue ( itemPath ) ;
82+ }
6483
65- if ( token . IsCancellationRequested )
66- break ;
84+ if ( token . IsCancellationRequested )
85+ break ;
86+ }
87+ while ( PInvoke . FindNextFile ( hFile , findData ) ) ;
6788 }
68- while ( FindNextFile ( hFile , out findData ) ) ;
89+
90+ PInvoke . CloseHandle ( hFile ) ;
6991 }
7092 }
7193 }
72- } , token , TaskCreationOptions . LongRunning , TaskScheduler . Default ) ) ;
94+ }
7395 }
7496
7597 private long ComputeFileSize ( string path )
7698 {
7799 if ( _computedFiles . TryGetValue ( path , out var size ) )
78- {
79100 return size ;
80- }
81101
82- using var hFile = CreateFile (
102+ using var hFile = PInvoke . CreateFile (
83103 path ,
84- Kernel32 . FileAccess . FILE_READ_ATTRIBUTES ,
85- FileShare . Read ,
104+ ( uint ) FILE_ACCESS_RIGHTS . FILE_READ_ATTRIBUTES ,
105+ FILE_SHARE_MODE . FILE_SHARE_READ ,
86106 null ,
87- FileMode . Open ,
107+ FILE_CREATION_DISPOSITION . OPEN_EXISTING ,
88108 0 ,
89109 null ) ;
90110
91- if ( ! hFile . IsInvalid )
92- {
93- if ( GetFileSizeEx ( hFile , out size ) && _computedFiles . TryAdd ( path , size ) )
94- {
95- Interlocked . Add ( ref _size , size ) ;
96- }
97- }
111+ if ( ! hFile . IsInvalid && PInvoke . GetFileSizeEx ( hFile , out size ) && _computedFiles . TryAdd ( path , size ) )
112+ Interlocked . Add ( ref _size , size ) ;
98113
99114 return size ;
100115 }
101116
102117 public void ForceComputeFileSize ( string path )
103118 {
104119 if ( ! Win32Helper . HasFileAttribute ( path , FileAttributes . Directory ) )
105- {
106120 ComputeFileSize ( path ) ;
107- }
108121 }
109122
110123 public bool TryGetComputedFileSize ( string path , out long size )
0 commit comments