diff --git a/src/Files.App/Data/Contexts/Window/IWindowContext.cs b/src/Files.App/Data/Contexts/Window/IWindowContext.cs
index e6271f95c25f..45d535b3b5b0 100644
--- a/src/Files.App/Data/Contexts/Window/IWindowContext.cs
+++ b/src/Files.App/Data/Contexts/Window/IWindowContext.cs
@@ -1,12 +1,16 @@
// Copyright (c) 2024 Files Community
// Licensed under the MIT License. See the LICENSE.
-using System.ComponentModel;
-
namespace Files.App.Data.Contexts
{
public interface IWindowContext : INotifyPropertyChanged
{
bool IsCompactOverlay { get; }
+
+ ///
+ bool IsRunningAsAdmin { get; }
+
+ ///
+ bool CanDragAndDrop { get; }
}
}
diff --git a/src/Files.App/Data/Contexts/Window/WindowContext.cs b/src/Files.App/Data/Contexts/Window/WindowContext.cs
index 58140f8bf533..ce4b80015c0c 100644
--- a/src/Files.App/Data/Contexts/Window/WindowContext.cs
+++ b/src/Files.App/Data/Contexts/Window/WindowContext.cs
@@ -1,18 +1,30 @@
// Copyright (c) 2024 Files Community
// Licensed under the MIT License. See the LICENSE.
-using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.UI.Windowing;
namespace Files.App.Data.Contexts
{
+ ///
internal sealed class WindowContext : ObservableObject, IWindowContext
{
+ private IWindowsSecurityService WindowsSecurityService = Ioc.Default.GetRequiredService();
+
private bool isCompactOverlay;
+ ///
public bool IsCompactOverlay => isCompactOverlay;
+ ///
+ public bool IsRunningAsAdmin { get; private set; }
+
+ ///
+ public bool CanDragAndDrop { get; private set; }
+
public WindowContext()
{
+ IsRunningAsAdmin = WindowsSecurityService.IsAppElevated();
+ CanDragAndDrop = WindowsSecurityService.CanDragAndDrop();
+
MainWindow.Instance.PresenterChanged += Window_PresenterChanged;
}
diff --git a/src/Files.App/Data/Contracts/IWindowsSecurityService.cs b/src/Files.App/Data/Contracts/IWindowsSecurityService.cs
new file mode 100644
index 000000000000..b1d29250d101
--- /dev/null
+++ b/src/Files.App/Data/Contracts/IWindowsSecurityService.cs
@@ -0,0 +1,40 @@
+// Copyright (c) 2024 Files Community
+// Licensed under the MIT License. See the LICENSE.
+
+namespace Files.App.Data.Contracts
+{
+ ///
+ /// Provides service for security APIs on Windows.
+ ///
+ public interface IWindowsSecurityService
+ {
+ ///
+ /// Gets a value that indicates whether the application is elevated.
+ ///
+ /// Returns true if the application is elevated; otherwise, false.
+ bool IsAppElevated();
+
+ ///
+ /// Gets a value that indicates whether the application can drag & drop.
+ ///
+ ///
+ /// Drag & drop onto an elevated app is not allowed (just crashes) due to UIPI.
+ ///
+ ///
+ /// For more info, visit:
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// Returns true if the application can drag & drop; otherwise, false.
+ bool CanDragAndDrop();
+
+ ///
+ /// Gets a value that indicates whether the application needs to be elevated for some operations.
+ ///
+ ///
+ /// True if the application needs to be elevated for some operations; otherwise, false.
+ bool IsElevationRequired(string path);
+ }
+}
diff --git a/src/Files.App/Helpers/Application/AppLifecycleHelper.cs b/src/Files.App/Helpers/Application/AppLifecycleHelper.cs
index d05d49735bd4..5c0daa8e4e56 100644
--- a/src/Files.App/Helpers/Application/AppLifecycleHelper.cs
+++ b/src/Files.App/Helpers/Application/AppLifecycleHelper.cs
@@ -166,6 +166,7 @@ public static IHost ConfigureHost()
// Services
.AddSingleton()
.AddSingleton()
+ .AddSingleton()
.AddSingleton()
.AddSingleton()
.AddSingleton()
diff --git a/src/Files.App/Helpers/Environment/ElevationHelpers.cs b/src/Files.App/Helpers/Environment/ElevationHelpers.cs
deleted file mode 100644
index 2770e09eb7ae..000000000000
--- a/src/Files.App/Helpers/Environment/ElevationHelpers.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (c) 2024 Files Community
-// Licensed under the MIT License. See the LICENSE.
-
-using System.Security.Principal;
-
-namespace Files.App.Helpers
-{
- public static class ElevationHelpers
- {
- public static bool IsElevationRequired(string filePath)
- {
- if (string.IsNullOrEmpty(filePath))
- return false;
-
- return Win32PInvoke.IsElevationRequired(filePath);
- }
-
- public static bool IsAppRunAsAdmin()
- {
- using WindowsIdentity identity = WindowsIdentity.GetCurrent();
- return new WindowsPrincipal(identity).IsInRole(new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null));
- }
- }
-}
diff --git a/src/Files.App/Services/Windows/WindowsSecurityService.cs b/src/Files.App/Services/Windows/WindowsSecurityService.cs
new file mode 100644
index 000000000000..1c05f8c7bfa5
--- /dev/null
+++ b/src/Files.App/Services/Windows/WindowsSecurityService.cs
@@ -0,0 +1,32 @@
+// Copyright (c) 2024 Files Community
+// Licensed under the MIT License. See the LICENSE.
+
+namespace Files.App.Services
+{
+ ///
+ public sealed class WindowsSecurityService : IWindowsSecurityService
+ {
+ ///
+ public unsafe bool IsAppElevated()
+ {
+ var identity = System.Security.Principal.WindowsIdentity.GetCurrent();
+ var principal = new System.Security.Principal.WindowsPrincipal(identity);
+ return principal.IsInRole(System.Security.Principal.WindowsBuiltInRole.Administrator);
+ }
+
+ ///
+ public unsafe bool CanDragAndDrop()
+ {
+ return !IsAppElevated();
+ }
+
+ ///
+ public bool IsElevationRequired(string path)
+ {
+ if (string.IsNullOrEmpty(path))
+ return false;
+
+ return Win32PInvoke.IsElevationRequired(path);
+ }
+ }
+}
diff --git a/src/Files.App/UserControls/TabBar/TabBar.xaml.cs b/src/Files.App/UserControls/TabBar/TabBar.xaml.cs
index 1151223e4505..4dc46487e888 100644
--- a/src/Files.App/UserControls/TabBar/TabBar.xaml.cs
+++ b/src/Files.App/UserControls/TabBar/TabBar.xaml.cs
@@ -18,6 +18,7 @@ public sealed partial class TabBar : BaseTabBar, INotifyPropertyChanged
private readonly ICommandManager Commands = Ioc.Default.GetRequiredService();
private readonly IAppearanceSettingsService AppearanceSettingsService = Ioc.Default.GetRequiredService();
+ private readonly IWindowContext WindowContext = Ioc.Default.GetRequiredService();
// Fields
@@ -44,12 +45,8 @@ public sealed partial class TabBar : BaseTabBar, INotifyPropertyChanged
public bool ShowTabActionsButton
=> AppearanceSettingsService.ShowTabActions;
- // Dragging makes the app crash when run as admin.
- // For more information:
- // - https://github.com/files-community/Files/issues/12390
- // - https://github.com/microsoft/terminal/issues/12017#issuecomment-1004129669
public bool AllowTabsDrag
- => !ElevationHelpers.IsAppRunAsAdmin();
+ => WindowContext.CanDragAndDrop;
public Rectangle DragArea
=> DragAreaRectangle;
@@ -172,7 +169,7 @@ private void TabView_TabStripDragOver(object sender, DragEventArgs e)
{
if (e.DataView.Properties.ContainsKey(TabPathIdentifier))
{
- HorizontalTabView.CanReorderTabs = true && !ElevationHelpers.IsAppRunAsAdmin();
+ HorizontalTabView.CanReorderTabs = WindowContext.CanDragAndDrop;
e.AcceptedOperation = DataPackageOperation.Move;
e.DragUIOverride.Caption = "TabStripDragAndDropUIOverrideCaption".GetLocalizedResource();
@@ -187,12 +184,12 @@ private void TabView_TabStripDragOver(object sender, DragEventArgs e)
private void TabView_DragLeave(object sender, DragEventArgs e)
{
- HorizontalTabView.CanReorderTabs = true && !ElevationHelpers.IsAppRunAsAdmin();
+ HorizontalTabView.CanReorderTabs = WindowContext.CanDragAndDrop;
}
private async void TabView_TabStripDrop(object sender, DragEventArgs e)
{
- HorizontalTabView.CanReorderTabs = true && !ElevationHelpers.IsAppRunAsAdmin();
+ HorizontalTabView.CanReorderTabs = WindowContext.CanDragAndDrop;
if (!(sender is TabView tabStrip))
return;
diff --git a/src/Files.App/ViewModels/ShellViewModel.cs b/src/Files.App/ViewModels/ShellViewModel.cs
index d491dae1aff4..dcae759b69ee 100644
--- a/src/Files.App/ViewModels/ShellViewModel.cs
+++ b/src/Files.App/ViewModels/ShellViewModel.cs
@@ -55,6 +55,7 @@ public sealed class ShellViewModel : ObservableObject, IDisposable
private readonly IFileTagsSettingsService fileTagsSettingsService = Ioc.Default.GetRequiredService();
private readonly ISizeProvider folderSizeProvider = Ioc.Default.GetRequiredService();
private readonly IStorageCacheService fileListCache = Ioc.Default.GetRequiredService();
+ private readonly IWindowsSecurityService WindowsSecurityService = Ioc.Default.GetRequiredService();
// Only used for Binding and ApplyFilesAndFoldersChangesAsync, don't manipulate on this!
public BulkConcurrentObservableCollection FilesAndFolders { get; }
@@ -1268,9 +1269,7 @@ private bool CheckElevationRights(ListedItem item)
if (item.SyncStatusUI.LoadSyncStatus)
return false;
- return item.IsShortcut
- ? ElevationHelpers.IsElevationRequired(((ShortcutItem)item).TargetPath)
- : ElevationHelpers.IsElevationRequired(item.ItemPath);
+ return WindowsSecurityService.IsElevationRequired(item.IsShortcut ? ((ShortcutItem)item).TargetPath : item.ItemPath);
}
public async Task LoadGitPropertiesAsync(GitItem gitItem)
diff --git a/src/Files.App/Views/Layouts/BaseLayoutPage.cs b/src/Files.App/Views/Layouts/BaseLayoutPage.cs
index 9ca1d77258b1..077581742188 100644
--- a/src/Files.App/Views/Layouts/BaseLayoutPage.cs
+++ b/src/Files.App/Views/Layouts/BaseLayoutPage.cs
@@ -39,6 +39,7 @@ public abstract class BaseLayoutPage : Page, IBaseLayoutPage, INotifyPropertyCha
protected IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetService()!;
protected ICommandManager Commands { get; } = Ioc.Default.GetRequiredService();
public InfoPaneViewModel InfoPaneViewModel { get; } = Ioc.Default.GetRequiredService();
+ protected readonly IWindowContext WindowContext = Ioc.Default.GetRequiredService();
// ViewModels
@@ -82,10 +83,8 @@ public CurrentInstanceViewModel? InstanceViewModel
public static AppModel AppModel
=> App.AppModel;
- // NOTE: Dragging makes the app crash when run as admin. (#12390)
- // For more information, visit https://github.com/microsoft/terminal/issues/12017#issuecomment-1004129669
public bool AllowItemDrag
- => !ElevationHelpers.IsAppRunAsAdmin();
+ => WindowContext.CanDragAndDrop;
public CommandBarFlyout ItemContextMenuFlyout { get; set; } = new()
{
diff --git a/src/Files.App/Views/MainPage.xaml.cs b/src/Files.App/Views/MainPage.xaml.cs
index 812b763361e7..6106a9ef5bb5 100644
--- a/src/Files.App/Views/MainPage.xaml.cs
+++ b/src/Files.App/Views/MainPage.xaml.cs
@@ -12,10 +12,7 @@
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Navigation;
-using Sentry;
-using System.Data;
using Windows.ApplicationModel;
-using Windows.ApplicationModel.DataTransfer;
using Windows.Foundation.Metadata;
using Windows.Graphics;
using Windows.Services.Store;
@@ -28,15 +25,10 @@ public sealed partial class MainPage : Page
{
private IGeneralSettingsService generalSettingsService { get; } = Ioc.Default.GetRequiredService();
public IUserSettingsService UserSettingsService { get; }
-
+ private readonly IWindowContext WindowContext = Ioc.Default.GetRequiredService();
public ICommandManager Commands { get; }
-
- public IWindowContext WindowContext { get; }
-
public SidebarViewModel SidebarAdaptiveViewModel { get; }
-
public MainPageViewModel ViewModel { get; }
-
public StatusCenterViewModel OngoingTasksViewModel { get; }
public static AppModel AppModel
@@ -44,8 +36,6 @@ public static AppModel AppModel
private bool keyReleased = true;
- private bool isAppRunningAsAdmin => ElevationHelpers.IsAppRunAsAdmin();
-
private DispatcherQueueTimer _updateDateDisplayTimer;
public MainPage()
@@ -55,7 +45,6 @@ public MainPage()
// Dependency Injection
UserSettingsService = Ioc.Default.GetRequiredService();
Commands = Ioc.Default.GetRequiredService();
- WindowContext = Ioc.Default.GetRequiredService();
SidebarAdaptiveViewModel = Ioc.Default.GetRequiredService();
SidebarAdaptiveViewModel.PaneFlyout = (MenuFlyout)Resources["SidebarContextMenu"];
ViewModel = Ioc.Default.GetRequiredService();
@@ -307,7 +296,7 @@ private void Page_Loaded(object sender, RoutedEventArgs e)
if
(
AppLifecycleHelper.AppEnvironment is not AppEnvironment.Dev &&
- isAppRunningAsAdmin &&
+ WindowContext.IsRunningAsAdmin &&
UserSettingsService.ApplicationSettingsService.ShowRunningAsAdminPrompt
)
{
diff --git a/src/Files.App/Views/Settings/TagsPage.xaml.cs b/src/Files.App/Views/Settings/TagsPage.xaml.cs
index 55d9c49af679..cf6176ddf5ab 100644
--- a/src/Files.App/Views/Settings/TagsPage.xaml.cs
+++ b/src/Files.App/Views/Settings/TagsPage.xaml.cs
@@ -12,6 +12,8 @@ namespace Files.App.Views.Settings
{
public sealed partial class TagsPage : Page
{
+ private readonly IWindowContext WindowContext = Ioc.Default.GetRequiredService();
+
private string oldTagName = string.Empty;
// Will be null unless the user has edited any tag
@@ -19,9 +21,8 @@ public sealed partial class TagsPage : Page
private FlyoutBase? deleteItemFlyout;
- // See issue #12390 on Github. Dragging makes the app crash when run as admin.
- // Further reading: https://github.com/microsoft/terminal/issues/12017#issuecomment-1004129669
- public bool AllowItemsDrag => !ElevationHelpers.IsAppRunAsAdmin();
+ public bool AllowItemsDrag
+ => WindowContext.CanDragAndDrop;
public TagsPage()
{