diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs index 5bfc68ea613..e81d70c5246 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Runtime.InteropServices; using System.Windows; using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Logger; @@ -9,6 +10,7 @@ using Flow.Launcher.Plugin.SharedCommands; using Windows.Win32; using Windows.Win32.Foundation; +using Windows.Win32.Security; using Windows.Win32.System.Shutdown; using Application = System.Windows.Application; using Control = System.Windows.Controls.Control; @@ -20,6 +22,10 @@ public class Main : IPlugin, ISettingProvider, IPluginI18n private PluginInitContext context; private Dictionary KeywordTitleMappings = new Dictionary(); + // SHTDN_REASON_MAJOR_OTHER indicates a generic shutdown reason that isn't categorized under hardware failure, software updates, or other predefined reasons. + // SHTDN_REASON_FLAG_PLANNED marks the shutdown as planned rather than an unexpected shutdown or failure + private const SHUTDOWN_REASON REASON = SHUTDOWN_REASON.SHTDN_REASON_MAJOR_OTHER | SHUTDOWN_REASON.SHTDN_REASON_FLAG_PLANNED; + public Control CreateSettingPanel() { var results = Commands(); @@ -100,6 +106,44 @@ public void Init(PluginInitContext context) }; } + private static unsafe bool EnableShutdownPrivilege() + { + try + { + if (!PInvoke.OpenProcessToken(Process.GetCurrentProcess().SafeHandle, TOKEN_ACCESS_MASK.TOKEN_ADJUST_PRIVILEGES | TOKEN_ACCESS_MASK.TOKEN_QUERY, out var tokenHandle)) + { + return false; + } + + if (!PInvoke.LookupPrivilegeValue(null, PInvoke.SE_SHUTDOWN_NAME, out var luid)) + { + return false; + } + + var privileges = new TOKEN_PRIVILEGES + { + PrivilegeCount = 1, + Privileges = new() { e0 = new LUID_AND_ATTRIBUTES { Luid = luid, Attributes = TOKEN_PRIVILEGES_ATTRIBUTES.SE_PRIVILEGE_ENABLED } } + }; + + if (!PInvoke.AdjustTokenPrivileges(tokenHandle, false, &privileges, 0, null, null)) + { + return false; + } + + if (Marshal.GetLastWin32Error() != (int)WIN32_ERROR.NO_ERROR) + { + return false; + } + + return true; + } + catch (Exception) + { + return false; + } + } + private List Commands() { var results = new List(); @@ -120,10 +164,12 @@ private List Commands() context.API.GetTranslation("flowlauncher_plugin_sys_dlgtext_shutdown_computer"), context.API.GetTranslation("flowlauncher_plugin_sys_shutdown_computer"), MessageBoxButton.YesNo, MessageBoxImage.Warning); + if (result == MessageBoxResult.Yes) - { - Process.Start("shutdown", "/s /t 0"); - } + if (EnableShutdownPrivilege()) + PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_SHUTDOWN | EXIT_WINDOWS_FLAGS.EWX_POWEROFF, REASON); + else + Process.Start("shutdown", "/s /t 0"); return true; } @@ -140,10 +186,12 @@ private List Commands() context.API.GetTranslation("flowlauncher_plugin_sys_dlgtext_restart_computer"), context.API.GetTranslation("flowlauncher_plugin_sys_restart_computer"), MessageBoxButton.YesNo, MessageBoxImage.Warning); + if (result == MessageBoxResult.Yes) - { - Process.Start("shutdown", "/r /t 0"); - } + if (EnableShutdownPrivilege()) + PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_REBOOT, REASON); + else + Process.Start("shutdown", "/r /t 0"); return true; } @@ -162,7 +210,10 @@ private List Commands() MessageBoxButton.YesNo, MessageBoxImage.Warning); if (result == MessageBoxResult.Yes) - Process.Start("shutdown", "/r /o /t 0"); + if (EnableShutdownPrivilege()) + PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_REBOOT | EXIT_WINDOWS_FLAGS.EWX_BOOTOPTIONS, REASON); + else + Process.Start("shutdown", "/r /o /t 0"); return true; } @@ -181,7 +232,7 @@ private List Commands() MessageBoxButton.YesNo, MessageBoxImage.Warning); if (result == MessageBoxResult.Yes) - PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_LOGOFF, 0); + PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_LOGOFF, REASON); return true; } @@ -204,7 +255,11 @@ private List Commands() SubTitle = context.API.GetTranslation("flowlauncher_plugin_sys_sleep"), Glyph = new GlyphInfo (FontFamily:"/Resources/#Segoe Fluent Icons", Glyph:"\xec46"), IcoPath = "Images\\sleep.png", - Action = c => PInvoke.SetSuspendState(false, false, false) + Action = c => + { + PInvoke.SetSuspendState(false, false, false); + return true; + } }, new Result { @@ -214,12 +269,7 @@ private List Commands() IcoPath = "Images\\hibernate.png", Action= c => { - var info = ShellCommand.SetProcessStartInfo("shutdown", arguments:"/h"); - info.WindowStyle = ProcessWindowStyle.Hidden; - info.UseShellExecute = true; - - ShellCommand.Execute(info); - + PInvoke.SetSuspendState(true, false, false); return true; } }, @@ -231,10 +281,7 @@ private List Commands() Glyph = new GlyphInfo (FontFamily:"/Resources/#Segoe Fluent Icons", Glyph:"\xe773"), Action = c => { - { - System.Diagnostics.Process.Start("control.exe", "srchadmin.dll"); - } - + Process.Start("control.exe", "srchadmin.dll"); return true; } }, @@ -272,10 +319,7 @@ private List Commands() CopyText = recycleBinFolder, Action = c => { - { - System.Diagnostics.Process.Start("explorer", recycleBinFolder); - } - + Process.Start("explorer", recycleBinFolder); return true; } }, diff --git a/Plugins/Flow.Launcher.Plugin.Sys/NativeMethods.txt b/Plugins/Flow.Launcher.Plugin.Sys/NativeMethods.txt index 8fcb6cae91e..4567e46a384 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/NativeMethods.txt +++ b/Plugins/Flow.Launcher.Plugin.Sys/NativeMethods.txt @@ -3,4 +3,10 @@ LockWorkStation SHEmptyRecycleBin S_OK E_UNEXPECTED -SetSuspendState \ No newline at end of file +SetSuspendState +OpenProcessToken +WIN32_ERROR +LookupPrivilegeValue +AdjustTokenPrivileges +TOKEN_PRIVILEGES +SE_SHUTDOWN_NAME \ No newline at end of file