mirror of
https://github.com/BytexGrid/NeatShift.git
synced 2026-05-06 13:50:58 +08:00
Admin on demand (#9)
This commit is contained in:
@@ -21,33 +21,6 @@ namespace NeatShift
|
||||
[SupportedOSPlatform("windows7.0")]
|
||||
protected override void OnStartup(StartupEventArgs e)
|
||||
{
|
||||
// Check if running as administrator
|
||||
bool isAdmin = IsRunningAsAdministrator();
|
||||
if (!isAdmin)
|
||||
{
|
||||
// Restart the application with admin rights
|
||||
try
|
||||
{
|
||||
var processInfo = new ProcessStartInfo
|
||||
{
|
||||
UseShellExecute = true,
|
||||
FileName = Process.GetCurrentProcess().MainModule?.FileName,
|
||||
Verb = "runas"
|
||||
};
|
||||
|
||||
Process.Start(processInfo);
|
||||
Current.Shutdown();
|
||||
return;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"Failed to restart with administrator privileges: {ex.Message}",
|
||||
"Error",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
var services = new ServiceCollection();
|
||||
|
||||
ConfigureServices(services);
|
||||
|
||||
82
NeatShift/NeatShift/Services/AdminManager.cs
Normal file
82
NeatShift/NeatShift/Services/AdminManager.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
using ModernWpf.Controls;
|
||||
using NeatShift.Views;
|
||||
|
||||
namespace NeatShift.Services
|
||||
{
|
||||
public static class AdminManager
|
||||
{
|
||||
private static bool _isAdminGranted = false;
|
||||
|
||||
public static bool IsAdminGranted
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_isAdminGranted) return true;
|
||||
|
||||
// Double check if we're actually running as admin
|
||||
var identity = WindowsIdentity.GetCurrent();
|
||||
var principal = new WindowsPrincipal(identity);
|
||||
_isAdminGranted = principal.IsInRole(WindowsBuiltInRole.Administrator);
|
||||
|
||||
return _isAdminGranted;
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<bool> EnsureAdmin(string reason, string actions)
|
||||
{
|
||||
if (IsAdminGranted) return true;
|
||||
|
||||
// Show our custom prompt first
|
||||
var dialog = new AdminPromptDialog(reason, actions);
|
||||
if (await dialog.ShowAsync() != ContentDialogResult.Primary)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
// Try to restart with admin rights
|
||||
var processInfo = new ProcessStartInfo
|
||||
{
|
||||
UseShellExecute = true,
|
||||
FileName = Process.GetCurrentProcess().MainModule?.FileName,
|
||||
Verb = "runas"
|
||||
};
|
||||
|
||||
Process.Start(processInfo);
|
||||
// Shutdown the current instance
|
||||
System.Windows.Application.Current.Shutdown();
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Messages
|
||||
{
|
||||
public static (string Reason, string Actions) SymbolicLink => (
|
||||
"Moving files with symbolic links requires administrator access to create special file system links.",
|
||||
"• Create symbolic links\n• Maintain file access for applications"
|
||||
);
|
||||
|
||||
public static (string Reason, string Actions) SystemRestore => (
|
||||
"Creating system restore points requires administrator access to modify system protection settings.",
|
||||
"• Create system restore points\n• Manage system protection"
|
||||
);
|
||||
|
||||
public static (string Reason, string Actions) ViewLinks => (
|
||||
"Viewing symbolic links requires administrator access to read special file system attributes.",
|
||||
"• Read symbolic link information\n• View file system attributes"
|
||||
);
|
||||
|
||||
public static (string Reason, string Actions) Backup => (
|
||||
"Managing backups requires administrator access to create system restore points and manage file system operations.",
|
||||
"• Create and manage system restore points\n• Access protected file locations for NeatSaves\n• Manage system protection settings"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,8 +134,12 @@ namespace NeatShift.ViewModels
|
||||
|
||||
if (dialog.ShowDialog() == DialogResult.OK && !string.IsNullOrEmpty(dialog.SelectedPath))
|
||||
{
|
||||
var linksDialog = new SymbolicLinksDialog(dialog.SelectedPath);
|
||||
await linksDialog.ShowAsync();
|
||||
var (reason, actions) = AdminManager.Messages.ViewLinks;
|
||||
if (await AdminManager.EnsureAdmin(reason, actions))
|
||||
{
|
||||
var linksDialog = new SymbolicLinksDialog(dialog.SelectedPath);
|
||||
await linksDialog.ShowAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,43 +152,51 @@ namespace NeatShift.ViewModels
|
||||
}
|
||||
|
||||
[RelayCommand(CanExecute = nameof(CanExecuteFileOperations))]
|
||||
private void AddFiles()
|
||||
private async Task AddFiles()
|
||||
{
|
||||
var dialog = new Microsoft.Win32.OpenFileDialog
|
||||
var (reason, actions) = AdminManager.Messages.SymbolicLink;
|
||||
if (await AdminManager.EnsureAdmin(reason, actions))
|
||||
{
|
||||
Multiselect = true,
|
||||
Title = "Select files to move"
|
||||
};
|
||||
|
||||
if (dialog.ShowDialog() == true)
|
||||
{
|
||||
foreach (string file in dialog.FileNames)
|
||||
var dialog = new Microsoft.Win32.OpenFileDialog
|
||||
{
|
||||
if (!string.IsNullOrEmpty(file))
|
||||
Multiselect = true,
|
||||
Title = "Select files to move"
|
||||
};
|
||||
|
||||
if (dialog.ShowDialog() == true)
|
||||
{
|
||||
foreach (string file in dialog.FileNames)
|
||||
{
|
||||
SourceItems.Add(new FileSystemItem(file));
|
||||
if (!string.IsNullOrEmpty(file))
|
||||
{
|
||||
SourceItems.Add(new FileSystemItem(file));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand(CanExecute = nameof(CanExecuteFileOperations))]
|
||||
private void AddFolder()
|
||||
private async Task AddFolder()
|
||||
{
|
||||
using var dialog = new CommonOpenFileDialog
|
||||
var (reason, actions) = AdminManager.Messages.SymbolicLink;
|
||||
if (await AdminManager.EnsureAdmin(reason, actions))
|
||||
{
|
||||
Title = "Select folders to move",
|
||||
IsFolderPicker = true,
|
||||
Multiselect = true
|
||||
};
|
||||
|
||||
if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
|
||||
{
|
||||
foreach (string folder in dialog.FileNames)
|
||||
using var dialog = new CommonOpenFileDialog
|
||||
{
|
||||
if (!string.IsNullOrEmpty(folder))
|
||||
Title = "Select folders to move",
|
||||
IsFolderPicker = true,
|
||||
Multiselect = true
|
||||
};
|
||||
|
||||
if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
|
||||
{
|
||||
foreach (string folder in dialog.FileNames)
|
||||
{
|
||||
SourceItems.Add(new FileSystemItem(folder));
|
||||
if (!string.IsNullOrEmpty(folder))
|
||||
{
|
||||
SourceItems.Add(new FileSystemItem(folder));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -272,7 +284,12 @@ namespace NeatShift.ViewModels
|
||||
return;
|
||||
}
|
||||
|
||||
await MoveFiles();
|
||||
// Request admin rights if needed
|
||||
var (reason, actions) = AdminManager.Messages.SymbolicLink;
|
||||
if (await AdminManager.EnsureAdmin(reason, actions))
|
||||
{
|
||||
await MoveFiles();
|
||||
}
|
||||
}
|
||||
|
||||
private bool CanExecuteFileOperations() => !IsOperationInProgress;
|
||||
@@ -389,8 +406,12 @@ namespace NeatShift.ViewModels
|
||||
[RelayCommand]
|
||||
private async Task ManageRestorePoints()
|
||||
{
|
||||
var dialog = new RestorePointDialog();
|
||||
await dialog.ShowAsync();
|
||||
var (reason, actions) = AdminManager.Messages.Backup;
|
||||
if (await AdminManager.EnsureAdmin(reason, actions))
|
||||
{
|
||||
var dialog = new RestorePointDialog();
|
||||
await dialog.ShowAsync();
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@@ -554,8 +575,12 @@ namespace NeatShift.ViewModels
|
||||
[RelayCommand]
|
||||
private async Task ManageNeatSaves()
|
||||
{
|
||||
var dialog = new NeatSavesManagementDialog(_neatSavesService);
|
||||
await dialog.ShowAsync();
|
||||
var (reason, actions) = AdminManager.Messages.Backup;
|
||||
if (await AdminManager.EnsureAdmin(reason, actions))
|
||||
{
|
||||
var dialog = new NeatSavesManagementDialog(_neatSavesService);
|
||||
await dialog.ShowAsync();
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
|
||||
25
NeatShift/NeatShift/Views/AdminPromptDialog.xaml
Normal file
25
NeatShift/NeatShift/Views/AdminPromptDialog.xaml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<ui:ContentDialog
|
||||
x:Class="NeatShift.Views.AdminPromptDialog"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:ui="http://schemas.modernwpf.com/2019"
|
||||
Title="Administrator Access Required"
|
||||
PrimaryButtonText="Continue"
|
||||
CloseButtonText="Cancel"
|
||||
DefaultButton="Primary">
|
||||
|
||||
<StackPanel Margin="0,10,0,0">
|
||||
<TextBlock x:Name="ReasonText"
|
||||
TextWrapping="Wrap"
|
||||
Margin="0,0,0,20"/>
|
||||
|
||||
<TextBlock Text="This action requires administrator privileges to:"
|
||||
FontWeight="SemiBold"
|
||||
Margin="0,0,0,10"/>
|
||||
|
||||
<TextBlock x:Name="ActionText"
|
||||
TextWrapping="Wrap"
|
||||
Margin="20,0,0,0"/>
|
||||
</StackPanel>
|
||||
</ui:ContentDialog>
|
||||
14
NeatShift/NeatShift/Views/AdminPromptDialog.xaml.cs
Normal file
14
NeatShift/NeatShift/Views/AdminPromptDialog.xaml.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using ModernWpf.Controls;
|
||||
|
||||
namespace NeatShift.Views
|
||||
{
|
||||
public partial class AdminPromptDialog : ContentDialog
|
||||
{
|
||||
public AdminPromptDialog(string reason, string actions)
|
||||
{
|
||||
InitializeComponent();
|
||||
ReasonText.Text = reason;
|
||||
ActionText.Text = actions;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||
<security>
|
||||
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
|
||||
Reference in New Issue
Block a user