diff --git a/src/Directory.Build.props b/src/Directory.Build.props index cc17ede..ef0aa8d 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -6,7 +6,7 @@ x64 - 0.2.8 + 0.2.9 X-Filer X-Filer diff --git a/src/Plugins/XFiler.Base/XFiler.Base.csproj b/src/Plugins/XFiler.Base/XFiler.Base.csproj index 825e67e..7e86106 100644 --- a/src/Plugins/XFiler.Base/XFiler.Base.csproj +++ b/src/Plugins/XFiler.Base/XFiler.Base.csproj @@ -1,7 +1,7 @@  - + all diff --git a/src/SDK/XFiler.SDK/Behaviors/RegistryContextMenuBehavior.cs b/src/SDK/XFiler.SDK/Behaviors/RegistryContextMenuBehavior.cs index 29edd8b..64e2466 100644 --- a/src/SDK/XFiler.SDK/Behaviors/RegistryContextMenuBehavior.cs +++ b/src/SDK/XFiler.SDK/Behaviors/RegistryContextMenuBehavior.cs @@ -39,16 +39,7 @@ public static bool GetRootItem(DependencyObject element) public static readonly DependencyProperty SelectedItemsContainerProperty = DependencyProperty.Register( nameof(SelectedItemsContainer), typeof(ObjectReference), typeof(RegistryContextMenuBehavior), new PropertyMetadata(default(ObjectReference))); - - //private static void PropsChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) - //{ - // if (d is RegistryContextMenuBehavior behavior) - // { - // if (behavior.NativeContextMenuLoader != null && behavior.FileInfoModel != null) - // behavior.LoadNativeContextMenuItems(behavior.NativeContextMenuLoader, behavior.FileInfoModel); - // } - //} - + #endregion #region Public Properties diff --git a/src/SDK/XFiler.SDK/Helpers/NativeContextMenuLoaderReference.cs b/src/SDK/XFiler.SDK/Helpers/NativeContextMenuLoaderReference.cs index 701e764..4ce7162 100644 --- a/src/SDK/XFiler.SDK/Helpers/NativeContextMenuLoaderReference.cs +++ b/src/SDK/XFiler.SDK/Helpers/NativeContextMenuLoaderReference.cs @@ -17,7 +17,12 @@ public INativeContextMenuLoader NativeContextMenuLoader set => SetValue(NativeContextMenuLoaderProperty, value); } + public IReadOnlyList AddItems => NativeContextMenuLoader.AddItems; + public ICommand InvokeCommand => NativeContextMenuLoader.InvokeCommand; + public ICommand InvokeAddNewItemsCommand => NativeContextMenuLoader.InvokeAddNewItemsCommand; + + public void Init() => NativeContextMenuLoader?.Init(); public IReadOnlyList CreateMenuItems(IEnumerable selectedItems) { diff --git a/src/SDK/XFiler.SDK/Services/ContextMenu/IAddNewContextMenuModel.cs b/src/SDK/XFiler.SDK/Services/ContextMenu/IAddNewContextMenuModel.cs new file mode 100644 index 0000000..17f1754 --- /dev/null +++ b/src/SDK/XFiler.SDK/Services/ContextMenu/IAddNewContextMenuModel.cs @@ -0,0 +1,12 @@ +using System.Windows.Controls; + +namespace XFiler.SDK; + +public interface IAddNewContextMenuModel +{ + string Extension { get; } + string Name { get; } + byte[]? Data { get; } + string? Template { get; } + Image? Icon { get; } +} \ No newline at end of file diff --git a/src/SDK/XFiler.SDK/Services/IRegistryContextMenuLoader.cs b/src/SDK/XFiler.SDK/Services/ContextMenu/INativeContextMenuLoader.cs similarity index 57% rename from src/SDK/XFiler.SDK/Services/IRegistryContextMenuLoader.cs rename to src/SDK/XFiler.SDK/Services/ContextMenu/INativeContextMenuLoader.cs index 2c171f6..85af453 100644 --- a/src/SDK/XFiler.SDK/Services/IRegistryContextMenuLoader.cs +++ b/src/SDK/XFiler.SDK/Services/ContextMenu/INativeContextMenuLoader.cs @@ -1,21 +1,15 @@ using System.Collections.Generic; using System.Windows.Input; -using System.Windows.Media; namespace XFiler.SDK; public interface INativeContextMenuLoader { + IReadOnlyList AddItems { get; } ICommand InvokeCommand { get; } + ICommand InvokeAddNewItemsCommand { get; } - IReadOnlyList CreateMenuItems(IEnumerable selectedItems); -} - -public interface IRegistryContextMenuModel -{ - string Name { get; } + void Init(); - ImageSource? Icon { get; } - - IReadOnlyList? Children { get; } + IReadOnlyList CreateMenuItems(IEnumerable selectedItems); } \ No newline at end of file diff --git a/src/SDK/XFiler.SDK/Services/ContextMenu/IRegistryContextMenuModel.cs b/src/SDK/XFiler.SDK/Services/ContextMenu/IRegistryContextMenuModel.cs new file mode 100644 index 0000000..476551e --- /dev/null +++ b/src/SDK/XFiler.SDK/Services/ContextMenu/IRegistryContextMenuModel.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using System.Windows.Media; + +namespace XFiler.SDK; + +public interface IRegistryContextMenuModel +{ + string Name { get; } + + ImageSource? Icon { get; } + + IReadOnlyList? Children { get; } +} \ No newline at end of file diff --git a/src/SDK/XFiler.SDK/Services/IDriveDetector.cs b/src/SDK/XFiler.SDK/Services/IDriveDetector.cs index 582436c..259c71d 100644 --- a/src/SDK/XFiler.SDK/Services/IDriveDetector.cs +++ b/src/SDK/XFiler.SDK/Services/IDriveDetector.cs @@ -1,15 +1,16 @@ using System; -namespace XFiler.SDK +namespace XFiler.SDK; + +public interface IDriveDetector +{ + event Action DriveChanged; + + void Init(); +} + +public enum EventType { - public interface IDriveDetector - { - event Action DriveChanged; - } - - public enum EventType - { - Added = 2, - Removed = 3 - } + Added = 2, + Removed = 3 } \ No newline at end of file diff --git a/src/SDK/XFiler.SDK/Services/IFileOperations.cs b/src/SDK/XFiler.SDK/Services/IFileOperations.cs index 272bd25..9a47466 100644 --- a/src/SDK/XFiler.SDK/Services/IFileOperations.cs +++ b/src/SDK/XFiler.SDK/Services/IFileOperations.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.IO; +using System.Threading.Tasks; namespace XFiler.SDK; @@ -19,4 +20,8 @@ void Delete(IReadOnlyList items, DirectoryInfo targetDirectory, void CreateFolder(string targetFolder, string name = "Новая папка"); void CreateEmptyTextFile(string targetFolder, string name = "Новый текстовый документ"); -} \ No newline at end of file + + Task CreateFile(string targetFolder, string name, string extension); + + Task CreateFileFromTemplate(string targetFolder, string name, string extension, string template); +} \ No newline at end of file diff --git a/src/SDK/XFiler.SDK/Services/ILaunchAtStartupService.cs b/src/SDK/XFiler.SDK/Services/ILaunchAtStartupService.cs index 83fecd6..7b3ff2f 100644 --- a/src/SDK/XFiler.SDK/Services/ILaunchAtStartupService.cs +++ b/src/SDK/XFiler.SDK/Services/ILaunchAtStartupService.cs @@ -1,7 +1,6 @@ -namespace XFiler.SDK +namespace XFiler.SDK; + +public interface ILaunchAtStartupService { - public interface ILaunchAtStartupService - { - void Init(); - } + void Init(); } \ No newline at end of file diff --git a/src/SDK/XFiler.SDK/Services/IRestartService.cs b/src/SDK/XFiler.SDK/Services/IRestartService.cs index af844aa..5c3f634 100644 --- a/src/SDK/XFiler.SDK/Services/IRestartService.cs +++ b/src/SDK/XFiler.SDK/Services/IRestartService.cs @@ -1,9 +1,8 @@ -namespace XFiler.SDK +namespace XFiler.SDK; + +public interface IRestartService { - public interface IRestartService - { - void RestartApplication(); + void RestartApplication(); - const string RestartKey = "/restart"; - } + const string RestartKey = "/restart"; } \ No newline at end of file diff --git a/src/SDK/XFiler.SDK/Services/IStorage.cs b/src/SDK/XFiler.SDK/Services/IStorage.cs index 8c8795e..e4dc8a7 100644 --- a/src/SDK/XFiler.SDK/Services/IStorage.cs +++ b/src/SDK/XFiler.SDK/Services/IStorage.cs @@ -13,10 +13,6 @@ public interface IStorage string DbDirectory { get; } string Bookmarks { get; } - - string ContextMenuFolder { get; } - - string ContextMenuTxtFile { get; } - - string ContextMenuTxtFile2 { get; } + + string ExtensionsDirectory { get; } } \ No newline at end of file diff --git a/src/SDK/XFiler.SDK/XFiler.SDK.csproj b/src/SDK/XFiler.SDK/XFiler.SDK.csproj index a1ba8a4..4612ed2 100644 --- a/src/SDK/XFiler.SDK/XFiler.SDK.csproj +++ b/src/SDK/XFiler.SDK/XFiler.SDK.csproj @@ -1,9 +1,9 @@  - + - + all diff --git a/src/Themes/XFiler.GoogleChromeStyle/XFiler.GoogleChromeStyle.csproj b/src/Themes/XFiler.GoogleChromeStyle/XFiler.GoogleChromeStyle.csproj index cb2232a..016c34a 100644 --- a/src/Themes/XFiler.GoogleChromeStyle/XFiler.GoogleChromeStyle.csproj +++ b/src/Themes/XFiler.GoogleChromeStyle/XFiler.GoogleChromeStyle.csproj @@ -1,9 +1,9 @@  - - - + + + diff --git a/src/XFiler/App.xaml.cs b/src/XFiler/App.xaml.cs index c19b3e5..ddcf6d9 100644 --- a/src/XFiler/App.xaml.cs +++ b/src/XFiler/App.xaml.cs @@ -1,8 +1,7 @@ -using System.IO; -using System.Text; -using Autofac; +using Autofac; using Hardcodet.Wpf.TaskbarNotification; using SingleInstanceHelper; +using System.Text; using XFiler.TrayIcon; namespace XFiler; @@ -35,9 +34,9 @@ protected override void OnStartup(StartupEventArgs e) Host.Resolve().Init(); Host.Resolve().Init(); Host.Resolve().Init(); - - Host.Resolve().DriveChanged += OnDriveChanged; - + Host.Resolve().Init(); + Host.Resolve().Init(); + LoadNotifyIconResourceDictionary(); _trayIcon = FindResource("TrayIcon") as TaskbarIcon @@ -99,20 +98,7 @@ private void LoadNotifyIconResourceDictionary() is ResourceDictionary resourceDict) resources.Add(resourceDict); } - - private void OnDriveChanged(EventType type, string driveName) - { - if (type == EventType.Added) - { - DirectoryInfo info = new(driveName); - - var tab = Host.Resolve>().Invoke().CreateExplorerTab(info); - - if (tab != null) - Host.Resolve().OpenTabInNewWindow(tab); - } - } - + private void ShowArgs(IEnumerable args) { StringBuilder sb = new(); diff --git a/src/XFiler/Behaviors/AddNewContextMenuBehavior.cs b/src/XFiler/Behaviors/AddNewContextMenuBehavior.cs new file mode 100644 index 0000000..d8320f8 --- /dev/null +++ b/src/XFiler/Behaviors/AddNewContextMenuBehavior.cs @@ -0,0 +1,116 @@ +using System.Windows.Controls; +using Microsoft.Xaml.Behaviors; + +namespace XFiler; + +internal sealed class AddNewContextMenuBehavior : Behavior +{ + #region Attached Properties + + public static readonly DependencyProperty RootItemProperty = DependencyProperty.RegisterAttached( + "RootItem", typeof(bool), typeof(AddNewContextMenuBehavior), + new PropertyMetadata(false)); + + public static void SetRootItem(DependencyObject element, bool value) + => element.SetValue(RootItemProperty, value); + + public static bool GetRootItem(DependencyObject element) + => (bool) element.GetValue(RootItemProperty); + + #endregion + + #region Dependency Properties + + public static readonly DependencyProperty NativeContextMenuLoaderProperty = DependencyProperty.Register( + nameof(NativeContextMenuLoader), typeof(INativeContextMenuLoader), + typeof(AddNewContextMenuBehavior), + new PropertyMetadata(default(INativeContextMenuLoader))); + + public static readonly DependencyProperty FileInfoModelProperty = DependencyProperty.Register( + nameof(FileInfoModel), typeof(IFileSystemModel), + typeof(AddNewContextMenuBehavior), + new PropertyMetadata(default(IFileSystemModel))); + + #endregion + + #region Public Properties + + public INativeContextMenuLoader? NativeContextMenuLoader + { + get => (INativeContextMenuLoader) GetValue(NativeContextMenuLoaderProperty); + set => SetValue(NativeContextMenuLoaderProperty, value); + } + + public IFileSystemModel? FileInfoModel + { + get => (IFileSystemModel) GetValue(FileInfoModelProperty); + set => SetValue(FileInfoModelProperty, value); + } + + #endregion + + protected override void OnAttached() + { + base.OnAttached(); + + AssociatedObject.Loaded += AssociatedObjectOnLoaded; + } + + private void AssociatedObjectOnLoaded(object sender, RoutedEventArgs e) + { + AssociatedObject.Loaded -= AssociatedObjectOnLoaded; + + if (NativeContextMenuLoader != null && FileInfoModel != null) + LoadNativeContextMenuItems(NativeContextMenuLoader, FileInfoModel); + } + + #region Private Methods + + private void LoadNativeContextMenuItems(INativeContextMenuLoader loader, IFileSystemModel parentInfo) + { + ClearOldItems(); + + var rootItem = AssociatedObject.Items.OfType() + .FirstOrDefault(GetRootItem); + + var index = AssociatedObject.Items.Count; + + if (rootItem != null) + index = AssociatedObject.Items.IndexOf(rootItem) + 1; + + var models = loader.AddItems; + + foreach (var model in models) + AssociatedObject.Items.Insert(index++, CreateRegistryMenuItem(loader, model, parentInfo)); + } + + private void ClearOldItems() + { + if (AssociatedObject.Items is { } items) + { + var oldItems = items.OfType() + .Where(i => i.DataContext is IAddNewContextMenuModel) + .ToList(); + + foreach (var oldItem in oldItems) + AssociatedObject.Items.Remove(oldItem); + } + } + + private static MenuItem CreateRegistryMenuItem(INativeContextMenuLoader loader, IAddNewContextMenuModel model, + IFileSystemModel parentModel) + { + MenuItem item = new() + { + Header = model.Name, + DataContext = model, + Command = loader.InvokeAddNewItemsCommand, + CommandParameter = new Tuple(model, parentModel), + Icon = model.Icon, + }; + + return item; + } + + #endregion +} \ No newline at end of file diff --git a/src/XFiler/Pages/Explorer/Views/ExplorerPage.xaml b/src/XFiler/Pages/Explorer/Views/ExplorerPage.xaml index 96766c0..7d6885c 100644 --- a/src/XFiler/Pages/Explorer/Views/ExplorerPage.xaml +++ b/src/XFiler/Pages/Explorer/Views/ExplorerPage.xaml @@ -102,7 +102,12 @@ - + + + + @@ -111,6 +116,9 @@ + + + diff --git a/src/XFiler/Services/DetectDrives/DriveDetector.cs b/src/XFiler/Services/DetectDrives/DriveDetector.cs index 1103f30..89f0d44 100644 --- a/src/XFiler/Services/DetectDrives/DriveDetector.cs +++ b/src/XFiler/Services/DetectDrives/DriveDetector.cs @@ -1,16 +1,37 @@ -using System.Management; +using System.IO; +using System.Management; namespace XFiler; internal class DriveDetector : IDriveDetector { + #region Private Fields + + private readonly Func _tabFactory; + private readonly IWindowFactory _windowFactory; private const string Query = "SELECT * FROM Win32_VolumeChangeEvent WHERE EventType = 2 or EventType = 3"; + #endregion + + #region Events + public event Action? DriveChanged; - public DriveDetector() => Init(); + #endregion + + #region Constructor + + public DriveDetector(Func tabFactory, IWindowFactory windowFactory) + { + _tabFactory = tabFactory; + _windowFactory = windowFactory; + } + + #endregion - private void Init() + #region Public Methods + + public void Init() { ManagementEventWatcher watcher = new() { @@ -20,8 +41,14 @@ private void Init() watcher.EventArrived += WatcherOnEventArrived; watcher.Start(); + + DriveChanged += OnDriveChanged; } + #endregion + + #region Private Methods + private void WatcherOnEventArrived(object sender, EventArrivedEventArgs e) { var driveName = e.NewEvent.Properties["DriveName"].Value.ToString(); @@ -36,4 +63,19 @@ private void RaiseDriveChanged(string driveName, EventType eventType) { Application.Current.Dispatcher.Invoke(() => { DriveChanged?.Invoke(eventType, driveName); }); } + + private void OnDriveChanged(EventType type, string driveName) + { + if (type == EventType.Added) + { + DirectoryInfo info = new(driveName); + + var tab = _tabFactory.Invoke().CreateExplorerTab(info); + + if (tab != null) + _windowFactory.OpenTabInNewWindow(tab); + } + } + + #endregion } \ No newline at end of file diff --git a/src/XFiler/Services/Files/FileOperations.cs b/src/XFiler/Services/Files/FileOperations.cs index 1b1f4b7..a2babd9 100644 --- a/src/XFiler/Services/Files/FileOperations.cs +++ b/src/XFiler/Services/Files/FileOperations.cs @@ -1,6 +1,5 @@ using System.IO; using Windows.FileOperations; -using Windows.FileOperations.FileOperation; using RecycleOption = Windows.FileOperations.RecycleOption; using UICancelOption = Windows.FileOperations.UICancelOption; using UIOption = Windows.FileOperations.UIOption; @@ -85,6 +84,42 @@ public void CreateEmptyTextFile(string targetFolder, string name = "Новый }); } + public async Task CreateFile(string targetFolder, string name, string extension) + { + return await Task.Run(() => + { + var filePath = Path.Combine(targetFolder, $"{name}{extension}"); + + var index = 2; + + while (File.Exists(filePath)) + filePath = Path.Combine(targetFolder, $"{name} ({index++}){extension}"); + + TryAction(() => File.Create(filePath).Dispose()); + + return filePath; + }); + } + + public async Task CreateFileFromTemplate(string targetFolder, string name, string extension, + string template) + { + return await Task.Run(() => + { + var filePath = Path.Combine(targetFolder, $"{name}{extension}"); + + var index = 2; + + while (File.Exists(filePath)) + filePath = Path.Combine(targetFolder, $"{name} ({index++}){extension}"); + + TryAction(() => + File.Copy(template, filePath)); + + return filePath; + }); + } + private static void TryAction(Action action) { try diff --git a/src/XFiler/Services/RegistryContextMenu/AddNewContextMenuModel.cs b/src/XFiler/Services/RegistryContextMenu/AddNewContextMenuModel.cs new file mode 100644 index 0000000..7341fd1 --- /dev/null +++ b/src/XFiler/Services/RegistryContextMenu/AddNewContextMenuModel.cs @@ -0,0 +1,32 @@ +using System.IO; +using System.Windows.Controls; +using Windows.ImageOperations; +using Windows.Storage.FileProperties; + +namespace XFiler; + +public class AddNewContextMenuModel : IAddNewContextMenuModel +{ + public string Extension { get; } + public string Name { get; } + public byte[]? Data { get; } + public string? Template { get; } + public Image? Icon { get; } + + public AddNewContextMenuModel(string extension, string displayType, byte[]? data, + string? template, StorageItemThumbnail? thumbnail) + { + Extension = extension; + Name = displayType; + Data = data; + Template = template; + + if (thumbnail != null) + { + Icon = new Image + { + Source = ImageSystem.FromStream(thumbnail.AsStream()), + }; + } + } +} \ No newline at end of file diff --git a/src/XFiler/Services/RegistryContextMenu/WindowsApiContextMenuLoader.cs b/src/XFiler/Services/RegistryContextMenu/WindowsApiContextMenuLoader.cs index f4031e8..dc07829 100644 --- a/src/XFiler/Services/RegistryContextMenu/WindowsApiContextMenuLoader.cs +++ b/src/XFiler/Services/RegistryContextMenu/WindowsApiContextMenuLoader.cs @@ -1,20 +1,54 @@ -using System.Text; +using Microsoft.Win32; +using System.IO; +using System.Security; +using System.Text; using System.Windows.Input; using Vanara.PInvoke; +using Windows.Storage; +using Windows.Storage.FileProperties; namespace XFiler; internal class WindowsApiContextMenuLoader : INativeContextMenuLoader { + #region Private Fields + + private readonly IStorage _storage; + private readonly IFileOperations _fileOperations; + private readonly ILogger _logger; private ContextMenu? _contextMenu; + #endregion + + #region Public Properties + + public IReadOnlyList AddItems { get; private set; } = null!; + + #endregion + + #region Commands + public ICommand InvokeCommand { get; } + public ICommand InvokeAddNewItemsCommand { get; } + + #endregion + + #region Constructor - public WindowsApiContextMenuLoader() + public WindowsApiContextMenuLoader(IStorage storage, IFileOperations fileOperations, + ILogger logger) { + _storage = storage; + _fileOperations = fileOperations; + _logger = logger; InvokeCommand = new DelegateCommand(OnInvoke); + InvokeAddNewItemsCommand = new DelegateCommand(CreateNewFile); } + #endregion + + #region Public Methods + public IReadOnlyList CreateMenuItems(IEnumerable selectedItems) { _contextMenu?.Dispose(); @@ -28,14 +62,56 @@ public IReadOnlyList CreateMenuItems(IEnumerable(); } - + + public async void Init() + { + AddItems = await GetAddNewContextMenuItems(); + } + + #endregion + + #region Private Methods + private void OnInvoke(object obj) { if (obj is NativeContextMenuItem item) _contextMenu?.InvokeItem(item.ContextMenuItem.Id); } - private static Func FilterMenuItems(bool showOpenMenu) + private async void CreateNewFile(object item) + { + if (item is not Tuple tuple) + return; + + var (model, parent) = tuple; + + var parentDirectory = parent.Info.FullName; + + var endFileName = $"{model.Name}{model.Extension}"; + + try + { + endFileName = model.Template == null + ? await _fileOperations.CreateFile(parentDirectory, model.Name, model.Extension) + : await _fileOperations.CreateFileFromTemplate(parentDirectory, model.Name, model.Extension, + model.Template); + } + catch (Exception e) + { + _logger.Error(e, "WindowsApiContextMenuLoader.CreateNewFile"); + } + finally + { + if (model.Data != null) + { + await using var fileStream = File.Open(endFileName, FileMode.Append); + await fileStream.WriteAsync(model.Data, 0, model.Data.Length); + await fileStream.FlushAsync(); + } + } + } + + private static Func FilterMenuItems(bool showOpenMenu) { var knownItems = new List() { @@ -49,7 +125,7 @@ private static Func FilterMenuItems(bool showOpenMenu) ExtractStringFromDll("shell32.dll", 34593), // Add to collection }; - bool FilterMenuItemsImpl(string menuItem) + bool FilterMenuItemsImpl(string? menuItem) { return !string.IsNullOrEmpty(menuItem) && (knownItems.Contains(menuItem) || @@ -59,7 +135,7 @@ bool FilterMenuItemsImpl(string menuItem) return FilterMenuItemsImpl; } - public static string ExtractStringFromDll(string file, int number) + private static string ExtractStringFromDll(string file, int number) { var lib = Kernel32.LoadLibrary(file); @@ -71,4 +147,111 @@ public static string ExtractStringFromDll(string file, int number) return result.ToString(); } + + private async Task> GetAddNewContextMenuItems() + { + var newMenuItems = new List(); + + var keys = Registry.ClassesRoot.GetSubKeyNames() + .Where(x => x.StartsWith(".")) + .Where(x => !new[] {".library-ms", ".url", ".lnk"}.Contains(x)); + + foreach (var keyName in keys) + { + using var key = OpenSubKeySafe(Registry.ClassesRoot, keyName); + + if (key != null) + { + var ret = await GetShellNewRegistryEntries(key, key); + + if (ret != null) + newMenuItems.Add(ret); + } + } + + return newMenuItems; + } + + private async Task GetShellNewRegistryEntries(RegistryKey current, RegistryKey root) + { + foreach (var keyName in current.GetSubKeyNames()) + { + using var key = OpenSubKeySafe(current, keyName); + + if (key == null) + continue; + + if (keyName == "ShellNew") + return await ParseShellNewRegistryEntry(key, root); + + var ret = await GetShellNewRegistryEntries(key, root); + + if (ret != null) + return ret; + } + + return null; + } + + private async Task ParseShellNewRegistryEntry(RegistryKey key, RegistryKey root) + { + if (!key.GetValueNames().Contains("NullFile") && + !key.GetValueNames().Contains("ItemName") && + !key.GetValueNames().Contains("FileName")) + { + return null; + } + + var extension = root.Name[(root.Name.LastIndexOf('\\') + 1)..]; + + var fileName = (string?) key.GetValue("FileName"); + + if (!string.IsNullOrEmpty(fileName) && Path.GetExtension(fileName) != extension) + return null; + + var dataObj = key.GetValue("Data"); + + byte[]? data = null; + + if (dataObj != null) + { + data = key.GetValueKind("Data") switch + { + RegistryValueKind.Binary => (byte[]) dataObj, + RegistryValueKind.String => Encoding.UTF8.GetBytes((string) dataObj), + _ => null + }; + } + + var sampleFile = new FileInfo(Path.Combine(_storage.ExtensionsDirectory, "file" + extension)); + + if (!sampleFile.Exists) + sampleFile.Create(); + + var s = await StorageFile.GetFileFromPathAsync(Path.Combine(_storage.ExtensionsDirectory, "file" + extension)); + + var displayType = s != null ? s.DisplayType.Replace("\"", "'") : $"file {extension}"; + + var thumbnail = s != null + ? await s.GetThumbnailAsync(mode: ThumbnailMode.ListView, 24, ThumbnailOptions.UseCurrentScale) + : null; + + var entry = new AddNewContextMenuModel(extension, displayType, data, fileName, thumbnail); + + return entry; + } + + private static RegistryKey? OpenSubKeySafe(RegistryKey root, string keyName) + { + try + { + return root.OpenSubKey(keyName); + } + catch (SecurityException) + { + return null; + } + } + + #endregion } \ No newline at end of file diff --git a/src/XFiler/Services/Storage/Storage.cs b/src/XFiler/Services/Storage/Storage.cs index da8b962..b7e2bd9 100644 --- a/src/XFiler/Services/Storage/Storage.cs +++ b/src/XFiler/Services/Storage/Storage.cs @@ -18,11 +18,7 @@ internal sealed class Storage : IStorage public string Bookmarks { get; } - public string ContextMenuFolder { get; } - - public string ContextMenuTxtFile { get; } - - public string ContextMenuTxtFile2 { get; } + public string ExtensionsDirectory { get; } #endregion @@ -48,14 +44,8 @@ private Storage(string directory) Bookmarks = Path.Combine(BaseDirectory, "bookmarks.json"); - ContextMenuFolder = Path.Combine(BaseDirectory, "ContextMenu"); - Directory.CreateDirectory(ContextMenuFolder); - - ContextMenuTxtFile = Path.Combine(ContextMenuFolder, "cm.txt"); - ContextMenuTxtFile2 = Path.Combine(ContextMenuFolder, "cm2.txt"); - - File.WriteAllText(ContextMenuTxtFile, "Egg"); - File.WriteAllText(ContextMenuTxtFile2, "Egg2"); + ExtensionsDirectory = Path.Combine(BaseDirectory, "Extensions"); + Directory.CreateDirectory(ExtensionsDirectory); } public Storage() : this("X-Filer") diff --git a/src/XFiler/XFiler.csproj b/src/XFiler/XFiler.csproj index 16b0bd5..fb72052 100644 --- a/src/XFiler/XFiler.csproj +++ b/src/XFiler/XFiler.csproj @@ -3,6 +3,8 @@ WinExe files.ico + net6.0-windows10.0.19041.0 + 10.0.17763.0 @@ -23,8 +25,8 @@ - - + +