Skip to content

Commit

Permalink
Improve StorageAreaWatcher
Browse files Browse the repository at this point in the history
  • Loading branch information
zhuxb711 committed Jul 1, 2020
1 parent dcab65c commit 9665177
Show file tree
Hide file tree
Showing 19 changed files with 126 additions and 130 deletions.
42 changes: 22 additions & 20 deletions RX_Explorer/Class/FileSystemStorageItem.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.ComponentModel;
using System.IO;
using System.Threading.Tasks;
using Windows.ApplicationModel.Core;
using Windows.Storage;
Expand Down Expand Up @@ -166,31 +167,32 @@ public void SetAsRecycleItem(string OriginPath, DateTimeOffset CreateTime)
ModifiedTimeRaw = CreateTime;
}

/// <summary>
/// 调用此方法以重命名存储对象,并更新界面显示
/// </summary>
/// <param name="Name"></param>
/// <returns></returns>
public async Task RenameAsync(string Name)
{
_ = await GetStorageItem().ConfigureAwait(true);

if (StorageItem is StorageFile File)
public async Task Replace(string NewPath)
{
try
{
await File.RenameAsync(Name, NameCollisionOption.GenerateUniqueName);
StorageFile File = await StorageFile.GetFileFromPathAsync(NewPath);
StorageItem = File;
StorageType = StorageItemTypes.File;

SizeRaw = await File.GetSizeRawDataAsync().ConfigureAwait(true);
ModifiedTimeRaw = await File.GetModifiedTimeAsync().ConfigureAwait(true);
OnPropertyChanged(nameof(this.Name));
OnPropertyChanged(nameof(ModifiedTime));
OnPropertyChanged(nameof(DisplayType));
Thumbnail = await File.GetThumbnailBitmapAsync().ConfigureAwait(true) ?? new BitmapImage(new Uri("ms-appx:///Assets/DocIcon.png"));
}
else if (StorageItem is StorageFolder Folder)
catch (FileNotFoundException)
{
await Folder.RenameAsync(Name, NameCollisionOption.GenerateUniqueName);
StorageFolder Folder = await StorageFolder.GetFolderFromPathAsync(NewPath);
StorageItem = Folder;
StorageType = StorageItemTypes.Folder;

Thumbnail = new BitmapImage(new Uri("ms-appx:///Assets/FolderIcon.png"));
ModifiedTimeRaw = await Folder.GetModifiedTimeAsync().ConfigureAwait(true);
OnPropertyChanged(nameof(this.Name));
OnPropertyChanged(nameof(ModifiedTime));
OnPropertyChanged(nameof(DisplayType));
}

OnPropertyChanged(nameof(this.Name));
OnPropertyChanged(nameof(ModifiedTime));
OnPropertyChanged(nameof(DisplayType));
}

/// <summary>
Expand Down Expand Up @@ -357,7 +359,7 @@ public override int GetHashCode()
}
else
{
if(right is null)
if (right is null)
{
return false;
}
Expand All @@ -376,7 +378,7 @@ public override int GetHashCode()
}
else
{
if(right is null)
if (right is null)
{
return true;
}
Expand Down
38 changes: 9 additions & 29 deletions RX_Explorer/Class/StorageAreaWatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Windows.ApplicationModel.Core;
using Windows.Storage;
using Windows.UI.Core;
Expand All @@ -14,37 +12,25 @@ public sealed class StorageAreaWatcher
{
private readonly ObservableCollection<FileSystemStorageItem> CurrentCollection;

private CancellationTokenSource Cancellation;

private Task CurrentWatchTask;
private IntPtr WatchPtr = IntPtr.Zero;

public void SetCurrentLocation(StorageFolder Folder)
{
if (Folder != null)
{
Cancellation?.Cancel();

if (CurrentWatchTask != null)
if (WatchPtr != IntPtr.Zero)
{
SpinWait.SpinUntil(() => CurrentWatchTask.IsCompleted);
WIN_Native_API.StopDirectoryWatcher(WatchPtr);
}

Cancellation?.Dispose();
Cancellation = new CancellationTokenSource();

CurrentWatchTask = WIN_Native_API.CreateDirectoryWatcher(Folder.Path, Cancellation.Token, Added, Removed, Renamed);
WatchPtr = WIN_Native_API.CreateDirectoryWatcher(Folder.Path, Added, Removed, Renamed);
}
else
{
Cancellation?.Cancel();

if (CurrentWatchTask != null)
if (WatchPtr != IntPtr.Zero)
{
SpinWait.SpinUntil(() => CurrentWatchTask.IsCompleted);
WIN_Native_API.StopDirectoryWatcher(WatchPtr);
}

Cancellation?.Dispose();
Cancellation = null;
}
}

Expand All @@ -57,16 +43,10 @@ private void Renamed(string OldPath, string NewPath)
{
if (CurrentCollection.FirstOrDefault((Item) => Item.Path == OldPath) is FileSystemStorageItem Item)
{
int Index = CurrentCollection.IndexOf(Item);
if (WIN_Native_API.GetStorageItems(NewPath).FirstOrDefault() is FileSystemStorageItem NewItem)
CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
NewItem.LoadMoreProperty().ConfigureAwait(false).GetAwaiter().GetResult();
CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
CurrentCollection.Remove(Item);
CurrentCollection.Insert(Index, NewItem);
}).AsTask().ConfigureAwait(false).GetAwaiter().GetResult();
}
Item.Replace(NewPath).ConfigureAwait(false).GetAwaiter().GetResult();
}).AsTask().ConfigureAwait(false).GetAwaiter().GetResult();
}
}

Expand Down
166 changes: 90 additions & 76 deletions RX_Explorer/Class/WIN_Native_API.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Threading;
using System.Threading.Tasks;
using Windows.Storage;
using FileAttributes = System.IO.FileAttributes;
Expand Down Expand Up @@ -126,10 +125,16 @@ private enum StateChangeType
Rename_Action_NewName = 5
}

public static void StopDirectoryWatcher(IntPtr hDir)
{
CancelIoEx(hDir, IntPtr.Zero);
CloseHandle(hDir);
}

[System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2008:不要在未传递 TaskScheduler 的情况下创建任务", Justification = "<挂起>")]
public static Task CreateDirectoryWatcher(string Path, CancellationToken CancellationToken, Action<string> Added = null, Action<string> Removed = null, Action<string,string> Renamed = null)
public static IntPtr CreateDirectoryWatcher(string Path, Action<string> Added = null, Action<string> Removed = null, Action<string, string> Renamed = null, Action<string> Modified = null)
{
return Task.Factory.StartNew((Token) =>
try
{
IntPtr hDir = CreateFileFromApp(Path, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, IntPtr.Zero, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, IntPtr.Zero);

Expand All @@ -138,93 +143,102 @@ public static Task CreateDirectoryWatcher(string Path, CancellationToken Cancell
throw new Win32Exception(Marshal.GetLastWin32Error());
}

CancellationToken CancelToken = (CancellationToken)Token;
CancelToken.Register((handle) =>
Task.Factory.StartNew((Arguement) =>
{
CancelIoEx((IntPtr)handle, IntPtr.Zero);
CloseHandle((IntPtr)handle);
}, hDir);
ValueTuple<IntPtr, Action<string>, Action<string>, Action<string, string>, Action<string>> Package = (ValueTuple<IntPtr, Action<string>, Action<string>, Action<string, string>, Action<string>>)Arguement;

while (!CancelToken.IsCancellationRequested)
{
IntPtr BufferPointer = Marshal.AllocHGlobal(4096);

try
while (true)
{
if (ReadDirectoryChangesW(hDir, BufferPointer, 4096, false, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME, out _, IntPtr.Zero, IntPtr.Zero))
IntPtr BufferPointer = Marshal.AllocHGlobal(4096);

try
{
IntPtr CurrentPointer = BufferPointer;
int Offset = 0;
string OldPath = null;

do
if (ReadDirectoryChangesW(Package.Item1, BufferPointer, 4096, false, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME, out _, IntPtr.Zero, IntPtr.Zero))
{
CurrentPointer = (IntPtr)(Offset + CurrentPointer.ToInt64());

// Read file length (in bytes) at offset 8
int FileNameLength = Marshal.ReadInt32(CurrentPointer, 8);
// Read file name (fileLen/2 characters) from offset 12
string FileName = Marshal.PtrToStringUni((IntPtr)(12 + CurrentPointer.ToInt64()), FileNameLength / 2);
// Read action at offset 4
int ActionIndex = Marshal.ReadInt32(CurrentPointer, 4);
IntPtr CurrentPointer = BufferPointer;
int Offset = 0;
string OldPath = null;

if (ActionIndex < 1 || ActionIndex > 5)
do
{
ActionIndex = 0;
}
CurrentPointer = (IntPtr)(Offset + CurrentPointer.ToInt64());

switch ((StateChangeType)ActionIndex)
{
case StateChangeType.Unknown_Action:
{
break;
}

case StateChangeType.Added_Action:
{
Added?.Invoke(System.IO.Path.Combine(Path, FileName));
break;
}
case StateChangeType.Removed_Action:
{
Removed?.Invoke(System.IO.Path.Combine(Path, FileName));
break;
}
case StateChangeType.Modified_Action:
{
break;
}
case StateChangeType.Rename_Action_OldName:
{
OldPath = System.IO.Path.Combine(Path, FileName);
break;
}
case StateChangeType.Rename_Action_NewName:
{
Renamed?.Invoke(OldPath, System.IO.Path.Combine(Path, FileName));
break;
}
}
// Read file length (in bytes) at offset 8
int FileNameLength = Marshal.ReadInt32(CurrentPointer, 8);
// Read file name (fileLen/2 characters) from offset 12
string FileName = Marshal.PtrToStringUni((IntPtr)(12 + CurrentPointer.ToInt64()), FileNameLength / 2);
// Read action at offset 4
int ActionIndex = Marshal.ReadInt32(CurrentPointer, 4);

if (ActionIndex < 1 || ActionIndex > 5)
{
ActionIndex = 0;
}

switch ((StateChangeType)ActionIndex)
{
case StateChangeType.Unknown_Action:
{
break;
}

case StateChangeType.Added_Action:
{
Package.Item2?.Invoke(System.IO.Path.Combine(Path, FileName));
break;
}
case StateChangeType.Removed_Action:
{
Package.Item3?.Invoke(System.IO.Path.Combine(Path, FileName));
break;
}
case StateChangeType.Modified_Action:
{
Package.Item5?.Invoke(FileName);
break;
}
case StateChangeType.Rename_Action_OldName:
{
OldPath = System.IO.Path.Combine(Path, FileName);
break;
}
case StateChangeType.Rename_Action_NewName:
{
Package.Item4?.Invoke(OldPath, System.IO.Path.Combine(Path, FileName));
break;
}
}

// Read NextEntryOffset at offset 0 and move pointer to next structure if needed
Offset = Marshal.ReadInt32(CurrentPointer);
// Read NextEntryOffset at offset 0 and move pointer to next structure if needed
Offset = Marshal.ReadInt32(CurrentPointer);
}
while (Offset != 0);
}
else
{
break;
}
while (Offset != 0);
}
}
catch
{
break;
}
finally
{
if (BufferPointer != IntPtr.Zero)
catch
{
Marshal.FreeHGlobal(BufferPointer);
break;
}
finally
{
if (BufferPointer != IntPtr.Zero)
{
Marshal.FreeHGlobal(BufferPointer);
}
}
}
}
}, CancellationToken, TaskCreationOptions.LongRunning);
}, (hDir, Added, Removed, Renamed, Modified));

return hDir;
}
catch
{
return IntPtr.Zero;
}
}

public static bool CheckContainsAnyItem(string Path, ItemFilters Filter)
Expand Down
Binary file modified RX_Explorer/FullTrust/FullTrustProcess.exe
Binary file not shown.
Binary file modified RX_Explorer/FullTrust/Vanara.Core.dll
Binary file not shown.
Binary file modified RX_Explorer/FullTrust/Vanara.PInvoke.ComCtl32.dll
Binary file not shown.
Binary file modified RX_Explorer/FullTrust/Vanara.PInvoke.Cryptography.dll
Binary file not shown.
Binary file modified RX_Explorer/FullTrust/Vanara.PInvoke.Gdi32.dll
Binary file not shown.
Binary file modified RX_Explorer/FullTrust/Vanara.PInvoke.Kernel32.dll
Binary file not shown.
Binary file modified RX_Explorer/FullTrust/Vanara.PInvoke.Ole.dll
Binary file not shown.
Binary file modified RX_Explorer/FullTrust/Vanara.PInvoke.SearchApi.dll
Binary file not shown.
Binary file modified RX_Explorer/FullTrust/Vanara.PInvoke.Security.dll
Binary file not shown.
Binary file modified RX_Explorer/FullTrust/Vanara.PInvoke.Shared.dll
Binary file not shown.
Binary file modified RX_Explorer/FullTrust/Vanara.PInvoke.Shell32.dll
Binary file not shown.
Binary file modified RX_Explorer/FullTrust/Vanara.PInvoke.ShlwApi.dll
Binary file not shown.
Binary file modified RX_Explorer/FullTrust/Vanara.PInvoke.User32.dll
Binary file not shown.
Binary file modified RX_Explorer/FullTrust/Vanara.Windows.Shell.dll
Binary file not shown.
8 changes: 4 additions & 4 deletions RX_Explorer/View/FilePresenter.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -823,7 +823,7 @@ public async void Rename_Click(object sender, RoutedEventArgs e)

try
{
await RenameItem.RenameAsync(dialog.DesireName).ConfigureAwait(true);
await (await RenameItem.GetStorageItem().ConfigureAwait(true)).RenameAsync(dialog.DesireName);
}
catch (UnauthorizedAccessException)
{
Expand Down Expand Up @@ -885,7 +885,7 @@ public async void Rename_Click(object sender, RoutedEventArgs e)
{
if (SettingControl.IsDetachTreeViewAndPresenter)
{
await RenameItem.RenameAsync(dialog.DesireName).ConfigureAwait(true);
await (await RenameItem.GetStorageItem().ConfigureAwait(true)).RenameAsync(dialog.DesireName);
}
else
{
Expand All @@ -895,7 +895,7 @@ public async void Rename_Click(object sender, RoutedEventArgs e)
TreeViewNode TargetNode = FileControlInstance.CurrentNode.Children.Where((Fold) => (Fold.Content as StorageFolder).Name == RenameItem.Name).FirstOrDefault();
int index = FileControlInstance.CurrentNode.Children.IndexOf(TargetNode);

await RenameItem.RenameAsync(dialog.DesireName).ConfigureAwait(true);
await (await RenameItem.GetStorageItem().ConfigureAwait(true)).RenameAsync(dialog.DesireName);

if (TargetNode.HasUnrealizedChildren)
{
Expand Down Expand Up @@ -937,7 +937,7 @@ public async void Rename_Click(object sender, RoutedEventArgs e)
}
else
{
await RenameItem.RenameAsync(dialog.DesireName).ConfigureAwait(true);
await (await RenameItem.GetStorageItem().ConfigureAwait(true)).RenameAsync(dialog.DesireName);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion RX_Explorer/View/SecureArea.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -733,7 +733,7 @@ private async void RenameFile_Click(object sender, RoutedEventArgs e)
return;
}

await RenameItem.RenameAsync(dialog.DesireName).ConfigureAwait(true);
await (await RenameItem.GetStorageItem().ConfigureAwait(true)).RenameAsync(dialog.DesireName);
}
}
}
Expand Down

0 comments on commit 9665177

Please sign in to comment.