Skip to content

Commit f2c6487

Browse files
authored
Prevent the file watching loop from still being alive after we kill it (#1001)
2 parents 57d81b6 + 35b512b commit f2c6487

File tree

2 files changed

+132
-133
lines changed

2 files changed

+132
-133
lines changed

Files/Helpers/NativeDirectoryChangesHelper.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ public class NativeDirectoryChangesHelper
2020
[DllImport("api-ms-win-core-io-l1-1-1.dll")]
2121
public static extern bool CancelIo(IntPtr hFile);
2222

23+
[DllImport("api-ms-win-core-io-l1-1-1.dll")]
24+
public static extern bool CancelIoEx(IntPtr hFile, IntPtr lpOverlapped);
25+
2326
[DllImport("api-ms-win-core-synch-l1-2-0.dll")]
2427
public static extern uint WaitForMultipleObjectsEx(uint nCount, IntPtr[] lpHandles, bool bWaitAll, uint dwMilliseconds, bool bAlertable);
2528

Files/View Models/ItemViewModel.cs

Lines changed: 129 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
using Files.Common;
33
using Files.Enums;
44
using Files.Helpers;
5-
using Files.Interacts;
65
using Files.Views.Pages;
76
using Microsoft.Toolkit.Uwp.UI;
87
using System;
@@ -16,10 +15,10 @@
1615
using System.Threading;
1716
using System.Threading.Tasks;
1817
using Windows.ApplicationModel.Core;
18+
using Windows.Foundation;
1919
using Windows.Foundation.Collections;
2020
using Windows.Storage;
2121
using Windows.Storage.FileProperties;
22-
using Windows.Storage.Search;
2322
using Windows.UI.Core;
2423
using Windows.UI.Text;
2524
using Windows.UI.Xaml;
@@ -34,8 +33,10 @@ namespace Files.Filesystem
3433
{
3534
public class ItemViewModel : INotifyPropertyChanged, IDisposable
3635
{
37-
private volatile bool IsWatching = false;
36+
private volatile bool MustTryToWatchAgain = false;
37+
private static SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1);
3838
private IntPtr hWatchDir;
39+
private IAsyncAction aWatcherAction;
3940
public ReadOnlyObservableCollection<ListedItem> FilesAndFolders { get; }
4041
public ListedItem CurrentFolder { get; private set; }
4142
public CollectionViewSource viewSource;
@@ -364,11 +365,9 @@ private void WorkingDirectoryChanged()
364365

365366
public void CancelLoadAndClearFiles()
366367
{
367-
var x = CancelIo(hWatchDir);
368-
CloseHandle(hWatchDir);
369-
hWatchDir = new IntPtr(-1);
370-
Debug.WriteLine("\n\nWatcher task aborted...\n\n"); // Cancel directory item change watcher
371-
IsWatching = false;
368+
Debug.WriteLine("CancelLoadAndClearFiles");
369+
CloseWatcher();
370+
372371
App.CurrentInstance.NavigationToolbar.CanRefresh = true;
373372
if (IsLoadingItems == false) { return; }
374373

@@ -383,12 +382,6 @@ public void CancelLoadAndClearFiles()
383382
}
384383
}
385384

386-
public void CancelChangeWatcher()
387-
{
388-
var x = CancelIo(hWatchDir);
389-
Debug.WriteLine("\n\nWatcher task aborted...\n\n");
390-
}
391-
392385
public void OrderFiles()
393386
{
394387
if (_filesAndFolders.Count == 0)
@@ -632,10 +625,8 @@ public async void RapidAddItemsToCollectionAsync(string path)
632625
{
633626

634627
await EnumerateItemsFromStandardFolder(path);
635-
if (!IsWatching)
636-
{
637-
WatchForDirectoryChanges(path);
638-
}
628+
WatchForDirectoryChanges(path);
629+
639630
}
640631

641632
if (FilesAndFolders.Count == 0)
@@ -667,7 +658,22 @@ public async void RapidAddItemsToCollectionAsync(string path)
667658
}
668659
}
669660

670-
private static SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1);
661+
public void CloseWatcher()
662+
{
663+
if (aWatcherAction != null)
664+
{
665+
aWatcherAction?.Cancel();
666+
667+
if (aWatcherAction.Status != AsyncStatus.Started)
668+
{
669+
aWatcherAction = null; // Prevent duplicate execution of this block
670+
Debug.WriteLine("watcher canceled");
671+
CancelIoEx(hWatchDir, IntPtr.Zero);
672+
Debug.WriteLine("watcher handle closed");
673+
CloseHandle(hWatchDir);
674+
}
675+
}
676+
}
671677

672678
public async Task EnumerateItemsFromSpecialFolder(string path)
673679
{
@@ -851,124 +857,113 @@ public async Task EnumerateItemsFromStandardFolder(string path)
851857

852858
}
853859

854-
private async void WatchForDirectoryChanges(string path)
860+
private void WatchForDirectoryChanges(string path)
855861
{
856-
var uiThreadScheduler = TaskScheduler.FromCurrentSynchronizationContext();
857-
Task thrItem = null;
858-
IsWatching = true;
862+
Debug.WriteLine("WatchForDirectoryChanges: {0}", path);
859863
hWatchDir = CreateFileFromApp(path, 1, 1 | 2 | 4,
860864
IntPtr.Zero, 3, (uint)File_Attributes.BackupSemantics | (uint)File_Attributes.Overlapped, IntPtr.Zero);
861865

862866
byte[] buff = new byte[4096];
863867

864-
await Windows.System.Threading.ThreadPool.RunAsync((x) =>
865-
{
866-
buff = new byte[4096];
867-
int notifyFilters = FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_FILE_NAME;
868-
869-
OVERLAPPED overlapped = new OVERLAPPED();
870-
overlapped.hEvent = CreateEvent(IntPtr.Zero, false, false, null);
871-
const uint INFINITE = 0xFFFFFFFF;
872-
873-
while (hWatchDir.ToInt64() != -1)
874-
{
875-
876-
unsafe
877-
{
878-
fixed (byte* pBuff = buff)
879-
{
880-
ref var notifyInformation = ref Unsafe.As<byte, FILE_NOTIFY_INFORMATION>(ref buff[0]);
881-
try
882-
{
883-
NativeDirectoryChangesHelper.ReadDirectoryChangesW(hWatchDir, pBuff,
884-
4096, false,
885-
notifyFilters, null,
886-
ref overlapped, null);
887-
}
888-
catch (Exception)
889-
{
890-
return;
891-
}
892-
893-
var rc = WaitForSingleObjectEx(overlapped.hEvent, INFINITE, true);
894-
895-
const uint FILE_ACTION_ADDED = 0x00000001;
896-
const uint FILE_ACTION_REMOVED = 0x00000002;
897-
const uint FILE_ACTION_MODIFIED = 0x00000003;
898-
const uint FILE_ACTION_RENAMED_OLD_NAME = 0x00000004;
899-
const uint FILE_ACTION_RENAMED_NEW_NAME = 0x00000005;
900-
901-
uint offset = 0;
902-
ref var notifyInfo = ref Unsafe.As<byte, FILE_NOTIFY_INFORMATION>(ref buff[offset]);
903-
do
904-
{
905-
notifyInfo = ref Unsafe.As<byte, FILE_NOTIFY_INFORMATION>(ref buff[offset]);
906-
string FileName = null;
907-
unsafe
908-
{
909-
fixed (char* name = notifyInfo.FileName)
910-
{
911-
FileName = Path.Combine(path, new string(name, 0, (int)notifyInfo.FileNameLength / 2));
912-
}
913-
}
914-
915-
uint action = notifyInfo.Action;
916-
//await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
917-
// () =>
918-
// {
919-
switch (action)
920-
{
921-
case FILE_ACTION_ADDED:
922-
if (!FilesAndFolders.Select(x => x.ItemPath).Contains(FileName))
923-
{
924-
AddFileOrFolder(FileName);
925-
Debug.WriteLine("File " + FileName + " added to working directory.");
926-
}
927-
break;
928-
case FILE_ACTION_REMOVED:
929-
if (FilesAndFolders.Select(x => x.ItemPath).Contains(FileName))
930-
{
931-
RemoveFileOrFolder(FilesAndFolders.First(x => x.ItemPath.Equals(FileName)));
932-
Debug.WriteLine("File " + FileName + " removed from working directory.");
933-
}
934-
break;
935-
case FILE_ACTION_MODIFIED:
936-
Debug.WriteLine("File " + FileName + " had attributes modified in the working directory.");
937-
break;
938-
case FILE_ACTION_RENAMED_OLD_NAME:
939-
if (FilesAndFolders.Select(x => x.ItemPath).Contains(FileName))
940-
{
941-
RemoveFileOrFolder(FilesAndFolders.First(x => x.ItemPath.Equals(FileName)));
942-
Debug.WriteLine("File " + FileName + " will be renamed in the working directory.");
943-
}
944-
break;
945-
case FILE_ACTION_RENAMED_NEW_NAME:
946-
if (!FilesAndFolders.Select(x => x.ItemPath).Contains(FileName))
947-
{
948-
AddFileOrFolder(FileName);
949-
Debug.WriteLine("File " + FileName + " was renamed in the working directory.");
950-
}
951-
break;
952-
default:
953-
Debug.WriteLine("File " + FileName + " performed an action in the working directory.");
954-
break;
955-
}
956-
//});
957-
offset += notifyInfo.NextEntryOffset;
958-
959-
} while (notifyInfo.NextEntryOffset != 0);
960-
961-
//ResetEvent(overlapped.hEvent);
962-
Debug.WriteLine("\n\nTask running...\n\n");
963-
}
964-
}
965-
}
966-
});
967-
968-
Debug.WriteLine("\n\nTask exiting...\n\n");
969-
970-
IsWatching = false;
971-
868+
aWatcherAction = Windows.System.Threading.ThreadPool.RunAsync((x) =>
869+
{
870+
var rand = Guid.NewGuid();
871+
buff = new byte[4096];
872+
int notifyFilters = FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_FILE_NAME;
873+
874+
OVERLAPPED overlapped = new OVERLAPPED();
875+
overlapped.hEvent = CreateEvent(IntPtr.Zero, false, false, null);
876+
const uint INFINITE = 0xFFFFFFFF;
877+
878+
while (x.Status != AsyncStatus.Canceled)
879+
{
880+
unsafe
881+
{
882+
fixed (byte* pBuff = buff)
883+
{
884+
ref var notifyInformation = ref Unsafe.As<byte, FILE_NOTIFY_INFORMATION>(ref buff[0]);
885+
if (x.Status != AsyncStatus.Canceled)
886+
{
887+
NativeDirectoryChangesHelper.ReadDirectoryChangesW(hWatchDir, pBuff,
888+
4096, false,
889+
notifyFilters, null,
890+
ref overlapped, null);
891+
}
892+
else
893+
{
894+
break;
895+
}
896+
897+
Debug.WriteLine("waiting: {0}", rand);
898+
if (x.Status == AsyncStatus.Canceled) { break; }
899+
var rc = WaitForSingleObjectEx(overlapped.hEvent, INFINITE, true);
900+
Debug.WriteLine("wait done: {0}", rand);
901+
902+
const uint FILE_ACTION_ADDED = 0x00000001;
903+
const uint FILE_ACTION_REMOVED = 0x00000002;
904+
const uint FILE_ACTION_MODIFIED = 0x00000003;
905+
const uint FILE_ACTION_RENAMED_OLD_NAME = 0x00000004;
906+
const uint FILE_ACTION_RENAMED_NEW_NAME = 0x00000005;
907+
908+
uint offset = 0;
909+
ref var notifyInfo = ref Unsafe.As<byte, FILE_NOTIFY_INFORMATION>(ref buff[offset]);
910+
if (x.Status == AsyncStatus.Canceled) { break; }
911+
912+
do
913+
{
914+
notifyInfo = ref Unsafe.As<byte, FILE_NOTIFY_INFORMATION>(ref buff[offset]);
915+
string FileName = null;
916+
unsafe
917+
{
918+
fixed (char* name = notifyInfo.FileName)
919+
{
920+
FileName = Path.Combine(path, new string(name, 0, (int)notifyInfo.FileNameLength / 2));
921+
}
922+
}
923+
924+
uint action = notifyInfo.Action;
925+
926+
Debug.WriteLine("action: {0}", action);
927+
switch (action)
928+
{
929+
case FILE_ACTION_ADDED:
930+
AddFileOrFolder(FileName);
931+
Debug.WriteLine("File " + FileName + " added to working directory.");
932+
break;
933+
case FILE_ACTION_REMOVED:
934+
RemoveFileOrFolder(FilesAndFolders.ToList().First(x => x.ItemPath.Equals(FileName)));
935+
Debug.WriteLine("File " + FileName + " removed from working directory.");
936+
break;
937+
case FILE_ACTION_MODIFIED:
938+
Debug.WriteLine("File " + FileName + " had attributes modified in the working directory.");
939+
break;
940+
case FILE_ACTION_RENAMED_OLD_NAME:
941+
RemoveFileOrFolder(FilesAndFolders.ToList().First(x => x.ItemPath.Equals(FileName)));
942+
Debug.WriteLine("File " + FileName + " will be renamed in the working directory.");
943+
break;
944+
case FILE_ACTION_RENAMED_NEW_NAME:
945+
AddFileOrFolder(FileName);
946+
Debug.WriteLine("File " + FileName + " was renamed in the working directory.");
947+
break;
948+
default:
949+
Debug.WriteLine("File " + FileName + " performed an action in the working directory.");
950+
break;
951+
}
952+
953+
offset += notifyInfo.NextEntryOffset;
954+
955+
} while (notifyInfo.NextEntryOffset != 0 && x.Status != AsyncStatus.Canceled);
956+
957+
//ResetEvent(overlapped.hEvent);
958+
Debug.WriteLine("Task running...");
959+
}
960+
}
961+
}
962+
CloseHandle(overlapped.hEvent);
963+
Debug.WriteLine("aWatcherAction done: {0}", rand);
964+
});
965+
966+
Debug.WriteLine("Task exiting...");
972967
}
973968

974969
public void AddFileOrFolder(ListedItem item)
@@ -1166,7 +1161,7 @@ private async Task AddFolder(StorageFolder folder)
11661161
IsLoadingItems = false;
11671162
return;
11681163
}
1169-
1164+
11701165
_filesAndFolders.Add(new ListedItem(folder.FolderRelativeId)
11711166
{
11721167
//FolderTooltipText = tooltipString,
@@ -1290,6 +1285,7 @@ public void Dispose()
12901285
{
12911286
_addFilesCTS?.Dispose();
12921287
_semaphoreCTS?.Dispose();
1288+
CloseWatcher();
12931289
}
12941290
}
1295-
}
1291+
}

0 commit comments

Comments
 (0)