Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 67 additions & 23 deletions Plugins/Flow.Launcher.Plugin.Sys/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
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;
using Flow.Launcher.Infrastructure.UserSettings;
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;
Expand All @@ -20,6 +22,10 @@ public class Main : IPlugin, ISettingProvider, IPluginI18n
private PluginInitContext context;
private Dictionary<string, string> KeywordTitleMappings = new Dictionary<string, string>();

// 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();
Expand Down Expand Up @@ -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<Result> Commands()
{
var results = new List<Result>();
Expand All @@ -120,10 +164,12 @@ private List<Result> 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;
}
Expand All @@ -140,10 +186,12 @@ private List<Result> 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;
}
Expand All @@ -162,7 +210,10 @@ private List<Result> 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;
}
Expand All @@ -181,7 +232,7 @@ private List<Result> 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;
}
Expand All @@ -204,7 +255,11 @@ private List<Result> 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
{
Expand All @@ -214,12 +269,7 @@ private List<Result> 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;
}
},
Expand All @@ -231,10 +281,7 @@ private List<Result> 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;
}
},
Expand Down Expand Up @@ -272,10 +319,7 @@ private List<Result> Commands()
CopyText = recycleBinFolder,
Action = c =>
{
{
System.Diagnostics.Process.Start("explorer", recycleBinFolder);
}

Process.Start("explorer", recycleBinFolder);
return true;
}
},
Expand Down
8 changes: 7 additions & 1 deletion Plugins/Flow.Launcher.Plugin.Sys/NativeMethods.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,10 @@ LockWorkStation
SHEmptyRecycleBin
S_OK
E_UNEXPECTED
SetSuspendState
SetSuspendState
OpenProcessToken
WIN32_ERROR
LookupPrivilegeValue
AdjustTokenPrivileges
TOKEN_PRIVILEGES
SE_SHUTDOWN_NAME
Loading