22using System . Collections . Generic ;
33using System . Diagnostics ;
44using System . IO ;
5+ using System . Runtime . InteropServices ;
56using System . Windows ;
67using Flow . Launcher . Infrastructure ;
78using Flow . Launcher . Infrastructure . Logger ;
89using Flow . Launcher . Infrastructure . UserSettings ;
910using Flow . Launcher . Plugin . SharedCommands ;
1011using Windows . Win32 ;
1112using Windows . Win32 . Foundation ;
13+ using Windows . Win32 . Security ;
1214using Windows . Win32 . System . Shutdown ;
1315using Application = System . Windows . Application ;
1416using Control = System . Windows . Controls . Control ;
@@ -20,6 +22,10 @@ public class Main : IPlugin, ISettingProvider, IPluginI18n
2022 private PluginInitContext context ;
2123 private Dictionary < string , string > KeywordTitleMappings = new Dictionary < string , string > ( ) ;
2224
25+ // SHTDN_REASON_MAJOR_OTHER indicates a generic shutdown reason that isn't categorized under hardware failure, software updates, or other predefined reasons.
26+ // SHTDN_REASON_FLAG_PLANNED marks the shutdown as planned rather than an unexpected shutdown or failure
27+ private const SHUTDOWN_REASON REASON = SHUTDOWN_REASON . SHTDN_REASON_MAJOR_OTHER | SHUTDOWN_REASON . SHTDN_REASON_FLAG_PLANNED ;
28+
2329 public Control CreateSettingPanel ( )
2430 {
2531 var results = Commands ( ) ;
@@ -100,6 +106,44 @@ public void Init(PluginInitContext context)
100106 } ;
101107 }
102108
109+ private static unsafe bool EnableShutdownPrivilege ( )
110+ {
111+ try
112+ {
113+ if ( ! PInvoke . OpenProcessToken ( Process . GetCurrentProcess ( ) . SafeHandle , TOKEN_ACCESS_MASK . TOKEN_ADJUST_PRIVILEGES | TOKEN_ACCESS_MASK . TOKEN_QUERY , out var tokenHandle ) )
114+ {
115+ return false ;
116+ }
117+
118+ if ( ! PInvoke . LookupPrivilegeValue ( null , PInvoke . SE_SHUTDOWN_NAME , out var luid ) )
119+ {
120+ return false ;
121+ }
122+
123+ var privileges = new TOKEN_PRIVILEGES
124+ {
125+ PrivilegeCount = 1 ,
126+ Privileges = new ( ) { e0 = new LUID_AND_ATTRIBUTES { Luid = luid , Attributes = TOKEN_PRIVILEGES_ATTRIBUTES . SE_PRIVILEGE_ENABLED } }
127+ } ;
128+
129+ if ( ! PInvoke . AdjustTokenPrivileges ( tokenHandle , false , & privileges , 0 , null , null ) )
130+ {
131+ return false ;
132+ }
133+
134+ if ( Marshal . GetLastWin32Error ( ) != ( int ) WIN32_ERROR . NO_ERROR )
135+ {
136+ return false ;
137+ }
138+
139+ return true ;
140+ }
141+ catch ( Exception )
142+ {
143+ return false ;
144+ }
145+ }
146+
103147 private List < Result > Commands ( )
104148 {
105149 var results = new List < Result > ( ) ;
@@ -120,10 +164,12 @@ private List<Result> Commands()
120164 context . API . GetTranslation ( "flowlauncher_plugin_sys_dlgtext_shutdown_computer" ) ,
121165 context . API . GetTranslation ( "flowlauncher_plugin_sys_shutdown_computer" ) ,
122166 MessageBoxButton . YesNo , MessageBoxImage . Warning ) ;
167+
123168 if ( result == MessageBoxResult . Yes )
124- {
125- Process . Start ( "shutdown" , "/s /t 0" ) ;
126- }
169+ if ( EnableShutdownPrivilege ( ) )
170+ PInvoke . ExitWindowsEx ( EXIT_WINDOWS_FLAGS . EWX_SHUTDOWN | EXIT_WINDOWS_FLAGS . EWX_POWEROFF , REASON ) ;
171+ else
172+ Process . Start ( "shutdown" , "/s /t 0" ) ;
127173
128174 return true ;
129175 }
@@ -140,10 +186,12 @@ private List<Result> Commands()
140186 context . API . GetTranslation ( "flowlauncher_plugin_sys_dlgtext_restart_computer" ) ,
141187 context . API . GetTranslation ( "flowlauncher_plugin_sys_restart_computer" ) ,
142188 MessageBoxButton . YesNo , MessageBoxImage . Warning ) ;
189+
143190 if ( result == MessageBoxResult . Yes )
144- {
145- Process . Start ( "shutdown" , "/r /t 0" ) ;
146- }
191+ if ( EnableShutdownPrivilege ( ) )
192+ PInvoke . ExitWindowsEx ( EXIT_WINDOWS_FLAGS . EWX_REBOOT , REASON ) ;
193+ else
194+ Process . Start ( "shutdown" , "/r /t 0" ) ;
147195
148196 return true ;
149197 }
@@ -162,7 +210,10 @@ private List<Result> Commands()
162210 MessageBoxButton . YesNo , MessageBoxImage . Warning ) ;
163211
164212 if ( result == MessageBoxResult . Yes )
165- Process . Start ( "shutdown" , "/r /o /t 0" ) ;
213+ if ( EnableShutdownPrivilege ( ) )
214+ PInvoke . ExitWindowsEx ( EXIT_WINDOWS_FLAGS . EWX_REBOOT | EXIT_WINDOWS_FLAGS . EWX_BOOTOPTIONS , REASON ) ;
215+ else
216+ Process . Start ( "shutdown" , "/r /o /t 0" ) ;
166217
167218 return true ;
168219 }
@@ -181,7 +232,7 @@ private List<Result> Commands()
181232 MessageBoxButton . YesNo , MessageBoxImage . Warning ) ;
182233
183234 if ( result == MessageBoxResult . Yes )
184- PInvoke . ExitWindowsEx ( EXIT_WINDOWS_FLAGS . EWX_LOGOFF , 0 ) ;
235+ PInvoke . ExitWindowsEx ( EXIT_WINDOWS_FLAGS . EWX_LOGOFF , REASON ) ;
185236
186237 return true ;
187238 }
@@ -204,7 +255,11 @@ private List<Result> Commands()
204255 SubTitle = context . API . GetTranslation ( "flowlauncher_plugin_sys_sleep" ) ,
205256 Glyph = new GlyphInfo ( FontFamily : "/Resources/#Segoe Fluent Icons" , Glyph : "\xec46 " ) ,
206257 IcoPath = "Images\\ sleep.png" ,
207- Action = c => PInvoke . SetSuspendState ( false , false , false )
258+ Action = c =>
259+ {
260+ PInvoke . SetSuspendState ( false , false , false ) ;
261+ return true ;
262+ }
208263 } ,
209264 new Result
210265 {
@@ -214,12 +269,7 @@ private List<Result> Commands()
214269 IcoPath = "Images\\ hibernate.png" ,
215270 Action = c =>
216271 {
217- var info = ShellCommand . SetProcessStartInfo ( "shutdown" , arguments : "/h" ) ;
218- info . WindowStyle = ProcessWindowStyle . Hidden ;
219- info . UseShellExecute = true ;
220-
221- ShellCommand . Execute ( info ) ;
222-
272+ PInvoke . SetSuspendState ( true , false , false ) ;
223273 return true ;
224274 }
225275 } ,
@@ -231,10 +281,7 @@ private List<Result> Commands()
231281 Glyph = new GlyphInfo ( FontFamily : "/Resources/#Segoe Fluent Icons" , Glyph : "\xe773 " ) ,
232282 Action = c =>
233283 {
234- {
235- System . Diagnostics . Process . Start ( "control.exe" , "srchadmin.dll" ) ;
236- }
237-
284+ Process . Start ( "control.exe" , "srchadmin.dll" ) ;
238285 return true ;
239286 }
240287 } ,
@@ -272,10 +319,7 @@ private List<Result> Commands()
272319 CopyText = recycleBinFolder ,
273320 Action = c =>
274321 {
275- {
276- System . Diagnostics . Process . Start ( "explorer" , recycleBinFolder ) ;
277- }
278-
322+ Process . Start ( "explorer" , recycleBinFolder ) ;
279323 return true ;
280324 }
281325 } ,
0 commit comments