From 1562c88ea71a0f56033553016682a51bc019d590 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 26 Jan 2025 20:07:40 +0800 Subject: [PATCH 01/16] Improve context menu item action response --- Flow.Launcher/ViewModel/MainViewModel.cs | 4 ++++ Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs | 1 + Plugins/Flow.Launcher.Plugin.Program/Main.cs | 2 ++ 3 files changed, 7 insertions(+) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 55bc8d1b3..b12e97e0b 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1273,6 +1273,8 @@ namespace Flow.Launcher.ViewModel { _topMostRecord.Remove(result); App.API.ShowMsg(InternationalizationManager.Instance.GetTranslation("success")); + App.API.BackToQueryResults(); + App.API.ReQuery(); return false; } }; @@ -1289,6 +1291,8 @@ namespace Flow.Launcher.ViewModel { _topMostRecord.AddOrUpdate(result); App.API.ShowMsg(InternationalizationManager.Instance.GetTranslation("success")); + App.API.BackToQueryResults(); + App.API.ReQuery(); return false; } }; diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs index feccc74c8..3f3b7cb58 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs @@ -242,6 +242,7 @@ namespace Flow.Launcher.Plugin.Explorer var name = "Plugin: Folder"; var message = $"File not found: {e.Message}"; Context.API.ShowMsgError(name, message); + return false; } return true; diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index 00b97e114..6ba7047f2 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -264,6 +264,8 @@ namespace Flow.Launcher.Plugin.Program Context.API.GetTranslation("flowlauncher_plugin_program_disable_dlgtitle_success"), Context.API.GetTranslation( "flowlauncher_plugin_program_disable_dlgtitle_success_message")); + Context.API.BackToQueryResults(); + Context.API.ReQuery(); return false; }, IcoPath = "Images/disable.png", From ae5186dacfad5fd354867b1ec254edaa913e0a52 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 26 Jan 2025 20:21:30 +0800 Subject: [PATCH 02/16] Remove context menu cache --- Flow.Launcher/ViewModel/MainViewModel.cs | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index b12e97e0b..2d232ebdd 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -34,8 +34,6 @@ namespace Flow.Launcher.ViewModel private bool _isQueryRunning; private Query _lastQuery; - private Result lastContextMenuResult = new Result(); - private List lastContextMenuResults = new List(); private string _queryTextBeforeLeaveResults; private readonly FlowLauncherJsonStorage _historyItemsStorage; @@ -986,19 +984,10 @@ namespace Flow.Launcher.ViewModel if (selected != null) // SelectedItem returns null if selection is empty. { List results; - if (selected == lastContextMenuResult) - { - results = lastContextMenuResults; - } - else - { + results = PluginManager.GetContextMenusForPlugin(selected); - lastContextMenuResults = results; - lastContextMenuResult = selected; results.Add(ContextMenuTopMost(selected)); results.Add(ContextMenuPluginInfo(selected.PluginID)); - } - if (!string.IsNullOrEmpty(query)) { @@ -1381,8 +1370,6 @@ namespace Flow.Launcher.ViewModel lastHistoryIndex = 1; // Trick for no delay MainWindowOpacity = 0; - lastContextMenuResult = new Result(); - lastContextMenuResults = new List(); if (ExternalPreviewVisible) CloseExternalPreview(); From 00de8611e1996d724c48672b5712c4db8f071b63 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 26 Jan 2025 20:37:41 +0800 Subject: [PATCH 03/16] Remove useless obsolete constructor --- Flow.Launcher.Plugin/Query.cs | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/Flow.Launcher.Plugin/Query.cs b/Flow.Launcher.Plugin/Query.cs index b41675a1a..e182491c2 100644 --- a/Flow.Launcher.Plugin/Query.cs +++ b/Flow.Launcher.Plugin/Query.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text.Json.Serialization; +using System.Text.Json.Serialization; namespace Flow.Launcher.Plugin { @@ -9,15 +6,6 @@ namespace Flow.Launcher.Plugin { public Query() { } - [Obsolete("Use the default Query constructor.")] - public Query(string rawQuery, string search, string[] terms, string[] searchTerms, string actionKeyword = "") - { - Search = search; - RawQuery = rawQuery; - SearchTerms = searchTerms; - ActionKeyword = actionKeyword; - } - /// /// Raw query, this includes action keyword if it has /// We didn't recommend use this property directly. You should always use Search property. From 95c8475c436b687822ddee2b342da5e4945225b3 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 26 Jan 2025 20:45:58 +0800 Subject: [PATCH 04/16] Fix null exception when result is from context menu & Add documents --- Flow.Launcher/Storage/TopMostRecord.cs | 2 ++ Flow.Launcher/Storage/UserSelectedRecord.cs | 2 ++ Flow.Launcher/ViewModel/MainViewModel.cs | 14 +++++++++----- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Flow.Launcher/Storage/TopMostRecord.cs b/Flow.Launcher/Storage/TopMostRecord.cs index cbd0b88fc..e76c57493 100644 --- a/Flow.Launcher/Storage/TopMostRecord.cs +++ b/Flow.Launcher/Storage/TopMostRecord.cs @@ -12,6 +12,8 @@ namespace Flow.Launcher.Storage internal bool IsTopMost(Result result) { + // origin query is null when user select the context menu item directly of one item from query list + // in this case, we do not need to check if the result is top most if (records.IsEmpty || result.OriginQuery == null || !records.TryGetValue(result.OriginQuery.RawQuery, out var value)) { diff --git a/Flow.Launcher/Storage/UserSelectedRecord.cs b/Flow.Launcher/Storage/UserSelectedRecord.cs index d6405005d..4f62d2b08 100644 --- a/Flow.Launcher/Storage/UserSelectedRecord.cs +++ b/Flow.Launcher/Storage/UserSelectedRecord.cs @@ -51,6 +51,8 @@ namespace Flow.Launcher.Storage private static int GenerateQueryAndResultHashCode(Query query, Result result) { + // query is null when user select the context menu item directly of one item from query list + // so we only need to consider the result if (query == null) { return GenerateResultHashCode(result); diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 2d232ebdd..5c3251bfc 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -396,11 +396,15 @@ namespace Flow.Launcher.ViewModel }) .ConfigureAwait(false); - if (SelectedIsFromQueryResults()) { _userSelectedRecord.Add(result); - _history.Add(result.OriginQuery.RawQuery); + // origin query is null when user select the context menu item directly of one item from query list + // so we don't want to add it to history + if (result.OriginQuery != null) + { + _history.Add(result.OriginQuery.RawQuery); + } lastHistoryIndex = 1; } @@ -985,9 +989,9 @@ namespace Flow.Launcher.ViewModel { List results; - results = PluginManager.GetContextMenusForPlugin(selected); - results.Add(ContextMenuTopMost(selected)); - results.Add(ContextMenuPluginInfo(selected.PluginID)); + results = PluginManager.GetContextMenusForPlugin(selected); + results.Add(ContextMenuTopMost(selected)); + results.Add(ContextMenuPluginInfo(selected.PluginID)); if (!string.IsNullOrEmpty(query)) { From 80f54ba0de3b55c6ef1cac360078dddd9a1e07a4 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 26 Jan 2025 20:51:12 +0800 Subject: [PATCH 05/16] Fix more possible origin query null exception & Remove useless load function --- Flow.Launcher/Storage/TopMostRecord.cs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/Flow.Launcher/Storage/TopMostRecord.cs b/Flow.Launcher/Storage/TopMostRecord.cs index e76c57493..a2af6fe92 100644 --- a/Flow.Launcher/Storage/TopMostRecord.cs +++ b/Flow.Launcher/Storage/TopMostRecord.cs @@ -26,11 +26,25 @@ namespace Flow.Launcher.Storage internal void Remove(Result result) { + // origin query is null when user select the context menu item directly of one item from query list + // in this case, we do not need to remove the record + if (result.OriginQuery == null) + { + return; + } + records.Remove(result.OriginQuery.RawQuery, out _); } internal void AddOrUpdate(Result result) { + // origin query is null when user select the context menu item directly of one item from query list + // in this case, we do not need to add or update the record + if (result.OriginQuery == null) + { + return; + } + var record = new Record { PluginID = result.PluginID, @@ -39,11 +53,6 @@ namespace Flow.Launcher.Storage }; records.AddOrUpdate(result.OriginQuery.RawQuery, record, (key, oldValue) => record); } - - public void Load(Dictionary dictionary) - { - records = new ConcurrentDictionary(dictionary); - } } public class Record From 1a39af35e20730667960f7af5422ae877de0d679 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 7 Feb 2025 20:23:53 +0800 Subject: [PATCH 06/16] Fix main window resize boarder thickness issue --- Flow.Launcher.Core/Resource/Theme.cs | 35 +++++++++++++++++++++++++++- Flow.Launcher/MainWindow.xaml | 2 +- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index 1d8409306..2c2feeae1 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -11,6 +11,7 @@ using System.Windows.Media.Effects; using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.UserSettings; +using System.Windows.Shell; namespace Flow.Launcher.Core.Resource { @@ -306,12 +307,15 @@ namespace Flow.Launcher.Core.Resource var marginSetter = windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == Border.MarginProperty) as Setter; if (marginSetter == null) { + var margin = new Thickness(ShadowExtraMargin, 12, ShadowExtraMargin, ShadowExtraMargin); marginSetter = new Setter() { Property = Border.MarginProperty, - Value = new Thickness(ShadowExtraMargin, 12, ShadowExtraMargin, ShadowExtraMargin), + Value = margin, }; windowBorderStyle.Setters.Add(marginSetter); + + SetResizeBoarderThickness(margin); } else { @@ -322,6 +326,8 @@ namespace Flow.Launcher.Core.Resource baseMargin.Right + ShadowExtraMargin, baseMargin.Bottom + ShadowExtraMargin); marginSetter.Value = newMargin; + + SetResizeBoarderThickness(newMargin); } windowBorderStyle.Setters.Add(effectSetter); @@ -352,9 +358,36 @@ namespace Flow.Launcher.Core.Resource marginSetter.Value = newMargin; } + SetResizeBoarderThickness(null); + UpdateResourceDictionary(dict); } + // because adding drop shadow effect will change the margin of the window, + // we need to update the window chrome thickness to correct set the resize border + private static void SetResizeBoarderThickness(Thickness? effectMargin) + { + var window = Application.Current.MainWindow; + if (WindowChrome.GetWindowChrome(window) is WindowChrome windowChrome) + { + Thickness thickness; + if (effectMargin == null) + { + thickness = SystemParameters.WindowResizeBorderThickness; + } + else + { + thickness = new Thickness( + effectMargin.Value.Left + SystemParameters.WindowResizeBorderThickness.Left, + effectMargin.Value.Top + SystemParameters.WindowResizeBorderThickness.Top, + effectMargin.Value.Right + SystemParameters.WindowResizeBorderThickness.Right, + effectMargin.Value.Bottom + SystemParameters.WindowResizeBorderThickness.Bottom); + } + + windowChrome.ResizeBorderThickness = thickness; + } + } + public record ThemeData(string FileNameWithoutExtension, string Name, bool? IsDark = null, bool? HasBlur = null); } } diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index f5fd729d4..4cc887c41 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -37,7 +37,7 @@ mc:Ignorable="d"> - + From cc570274ffb4ad678d4fba75914c4c40e515d53a Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 16 Feb 2025 22:47:10 +0800 Subject: [PATCH 07/16] Fix get thumbnail exception --- Flow.Launcher.Infrastructure/Image/ThumbnailReader.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Image/ThumbnailReader.cs b/Flow.Launcher.Infrastructure/Image/ThumbnailReader.cs index 2fb8cf363..a8d1d78ed 100644 --- a/Flow.Launcher.Infrastructure/Image/ThumbnailReader.cs +++ b/Flow.Launcher.Infrastructure/Image/ThumbnailReader.cs @@ -31,8 +31,6 @@ namespace Flow.Launcher.Infrastructure.Image private static readonly Guid GUID_IShellItem = typeof(IShellItem).GUID; - private static readonly HRESULT S_ExtractionFailed = (HRESULT)0x8004B200; - public static BitmapSource GetThumbnail(string fileName, int width, int height, ThumbnailOptions options) { HBITMAP hBitmap = GetHBitmap(Path.GetFullPath(fileName), width, height, options); @@ -79,7 +77,12 @@ namespace Flow.Launcher.Infrastructure.Image { imageFactory.GetImage(size, (SIIGBF)options, &hBitmap); } - catch (COMException ex) when (ex.HResult == S_ExtractionFailed && options == ThumbnailOptions.ThumbnailOnly) + catch (COMException) + { + // Fallback to IconOnly if ThumbnailOnly fails + imageFactory.GetImage(size, (SIIGBF)ThumbnailOptions.IconOnly, &hBitmap); + } + catch (FileNotFoundException) { // Fallback to IconOnly if ThumbnailOnly fails imageFactory.GetImage(size, (SIIGBF)ThumbnailOptions.IconOnly, &hBitmap); From 38b285067d1874ce1952db25a2529502a53cbe4f Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 16 Feb 2025 22:50:43 +0800 Subject: [PATCH 08/16] Improve documents --- Flow.Launcher.Infrastructure/Image/ThumbnailReader.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Image/ThumbnailReader.cs b/Flow.Launcher.Infrastructure/Image/ThumbnailReader.cs index a8d1d78ed..bd34bdd2a 100644 --- a/Flow.Launcher.Infrastructure/Image/ThumbnailReader.cs +++ b/Flow.Launcher.Infrastructure/Image/ThumbnailReader.cs @@ -79,12 +79,12 @@ namespace Flow.Launcher.Infrastructure.Image } catch (COMException) { - // Fallback to IconOnly if ThumbnailOnly fails + // Fallback to IconOnly for COM exceptions imageFactory.GetImage(size, (SIIGBF)ThumbnailOptions.IconOnly, &hBitmap); } catch (FileNotFoundException) { - // Fallback to IconOnly if ThumbnailOnly fails + // Fallback to IconOnly if files cannot be found imageFactory.GetImage(size, (SIIGBF)ThumbnailOptions.IconOnly, &hBitmap); } } From 71f1acd9ec148ea82055644b7498e2c956b6f623 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 17 Feb 2025 19:07:01 +0800 Subject: [PATCH 09/16] Revert com expcetion & Add thumbnail only check --- Flow.Launcher.Infrastructure/Image/ThumbnailReader.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Image/ThumbnailReader.cs b/Flow.Launcher.Infrastructure/Image/ThumbnailReader.cs index bd34bdd2a..b98ea50fe 100644 --- a/Flow.Launcher.Infrastructure/Image/ThumbnailReader.cs +++ b/Flow.Launcher.Infrastructure/Image/ThumbnailReader.cs @@ -31,6 +31,8 @@ namespace Flow.Launcher.Infrastructure.Image private static readonly Guid GUID_IShellItem = typeof(IShellItem).GUID; + private static readonly HRESULT S_ExtractionFailed = (HRESULT)0x8004B200; + public static BitmapSource GetThumbnail(string fileName, int width, int height, ThumbnailOptions options) { HBITMAP hBitmap = GetHBitmap(Path.GetFullPath(fileName), width, height, options); @@ -77,12 +79,12 @@ namespace Flow.Launcher.Infrastructure.Image { imageFactory.GetImage(size, (SIIGBF)options, &hBitmap); } - catch (COMException) + catch (COMException ex) when (ex.HResult == S_ExtractionFailed && options == ThumbnailOptions.ThumbnailOnly) { - // Fallback to IconOnly for COM exceptions + // Fallback to IconOnly if ThumbnailOnly fails imageFactory.GetImage(size, (SIIGBF)ThumbnailOptions.IconOnly, &hBitmap); } - catch (FileNotFoundException) + catch (FileNotFoundException) when (options == ThumbnailOptions.ThumbnailOnly) { // Fallback to IconOnly if files cannot be found imageFactory.GetImage(size, (SIIGBF)ThumbnailOptions.IconOnly, &hBitmap); From 827b6edb38f58c5d7f94a19b0c690a1b9c1ce3f4 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 17 Feb 2025 23:12:46 +0800 Subject: [PATCH 10/16] Improve code quality --- Plugins/Flow.Launcher.Plugin.Sys/Main.cs | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs index 5bfc68ea6..374caa511 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs @@ -120,10 +120,9 @@ namespace Flow.Launcher.Plugin.Sys 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"); - } return true; } @@ -140,10 +139,9 @@ namespace Flow.Launcher.Plugin.Sys 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"); - } return true; } @@ -204,7 +202,11 @@ namespace Flow.Launcher.Plugin.Sys 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 { @@ -231,10 +233,7 @@ namespace Flow.Launcher.Plugin.Sys 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 +271,7 @@ namespace Flow.Launcher.Plugin.Sys CopyText = recycleBinFolder, Action = c => { - { - System.Diagnostics.Process.Start("explorer", recycleBinFolder); - } - + Process.Start("explorer", recycleBinFolder); return true; } }, From ffa303825ef2a1523751f4aedc6a309726796b24 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 17 Feb 2025 23:22:06 +0800 Subject: [PATCH 11/16] Replace process commands with PInvoke --- Plugins/Flow.Launcher.Plugin.Sys/Main.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs index 374caa511..16c1b7f91 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs @@ -122,7 +122,7 @@ namespace Flow.Launcher.Plugin.Sys MessageBoxButton.YesNo, MessageBoxImage.Warning); if (result == MessageBoxResult.Yes) - Process.Start("shutdown", "/s /t 0"); + PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_SHUTDOWN | EXIT_WINDOWS_FLAGS.EWX_POWEROFF, SHUTDOWN_REASON.SHTDN_REASON_NONE); return true; } @@ -141,7 +141,7 @@ namespace Flow.Launcher.Plugin.Sys MessageBoxButton.YesNo, MessageBoxImage.Warning); if (result == MessageBoxResult.Yes) - Process.Start("shutdown", "/r /t 0"); + PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_REBOOT, SHUTDOWN_REASON.SHTDN_REASON_NONE); return true; } @@ -160,7 +160,7 @@ namespace Flow.Launcher.Plugin.Sys MessageBoxButton.YesNo, MessageBoxImage.Warning); if (result == MessageBoxResult.Yes) - Process.Start("shutdown", "/r /o /t 0"); + PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_REBOOT | EXIT_WINDOWS_FLAGS.EWX_BOOTOPTIONS, SHUTDOWN_REASON.SHTDN_REASON_NONE); return true; } @@ -179,7 +179,7 @@ namespace Flow.Launcher.Plugin.Sys MessageBoxButton.YesNo, MessageBoxImage.Warning); if (result == MessageBoxResult.Yes) - PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_LOGOFF, 0); + PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_LOGOFF, SHUTDOWN_REASON.SHTDN_REASON_NONE); return true; } From e93699b37daaa1bedefd5e442413ea79dbe3204e Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 17 Feb 2025 23:37:20 +0800 Subject: [PATCH 12/16] Add shutdown reason --- Plugins/Flow.Launcher.Plugin.Sys/Main.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs index 16c1b7f91..3199f50a2 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs @@ -121,8 +121,12 @@ namespace Flow.Launcher.Plugin.Sys context.API.GetTranslation("flowlauncher_plugin_sys_shutdown_computer"), MessageBoxButton.YesNo, MessageBoxImage.Warning); + // 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 if (result == MessageBoxResult.Yes) - PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_SHUTDOWN | EXIT_WINDOWS_FLAGS.EWX_POWEROFF, SHUTDOWN_REASON.SHTDN_REASON_NONE); + PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_SHUTDOWN | EXIT_WINDOWS_FLAGS.EWX_POWEROFF, + SHUTDOWN_REASON.SHTDN_REASON_MAJOR_OTHER | SHUTDOWN_REASON.SHTDN_REASON_FLAG_PLANNED); return true; } @@ -141,7 +145,8 @@ namespace Flow.Launcher.Plugin.Sys MessageBoxButton.YesNo, MessageBoxImage.Warning); if (result == MessageBoxResult.Yes) - PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_REBOOT, SHUTDOWN_REASON.SHTDN_REASON_NONE); + PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_REBOOT, + SHUTDOWN_REASON.SHTDN_REASON_MAJOR_OTHER | SHUTDOWN_REASON.SHTDN_REASON_FLAG_PLANNED); return true; } @@ -160,7 +165,8 @@ namespace Flow.Launcher.Plugin.Sys MessageBoxButton.YesNo, MessageBoxImage.Warning); if (result == MessageBoxResult.Yes) - PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_REBOOT | EXIT_WINDOWS_FLAGS.EWX_BOOTOPTIONS, SHUTDOWN_REASON.SHTDN_REASON_NONE); + PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_REBOOT | EXIT_WINDOWS_FLAGS.EWX_BOOTOPTIONS, + SHUTDOWN_REASON.SHTDN_REASON_MAJOR_OTHER | SHUTDOWN_REASON.SHTDN_REASON_FLAG_PLANNED); return true; } @@ -179,7 +185,8 @@ namespace Flow.Launcher.Plugin.Sys MessageBoxButton.YesNo, MessageBoxImage.Warning); if (result == MessageBoxResult.Yes) - PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_LOGOFF, SHUTDOWN_REASON.SHTDN_REASON_NONE); + PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_LOGOFF, + SHUTDOWN_REASON.SHTDN_REASON_MAJOR_OTHER | SHUTDOWN_REASON.SHTDN_REASON_FLAG_PLANNED); return true; } From 18093148429abfaa94db92607984a53d97cde8cf Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 18 Feb 2025 00:08:15 +0800 Subject: [PATCH 13/16] Enable shutdown privilege before calling PInvoke for shutdown and start --- Plugins/Flow.Launcher.Plugin.Sys/Main.cs | 63 +++++++++++++++++-- .../NativeMethods.txt | 7 ++- 2 files changed, 63 insertions(+), 7 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs index 3199f50a2..7d3f66746 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.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; @@ -20,6 +22,8 @@ namespace Flow.Launcher.Plugin.Sys private PluginInitContext context; private Dictionary KeywordTitleMappings = new Dictionary(); + private const string SE_SHUTDOWN_NAME = "SeShutdownPrivilege"; + public Control CreateSettingPanel() { var results = Commands(); @@ -100,6 +104,44 @@ namespace Flow.Launcher.Plugin.Sys }; } + 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, 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(); @@ -125,8 +167,11 @@ namespace Flow.Launcher.Plugin.Sys // software updates, or other predefined reasons. // SHTDN_REASON_FLAG_PLANNED marks the shutdown as planned rather than an unexpected shutdown or failure if (result == MessageBoxResult.Yes) - PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_SHUTDOWN | EXIT_WINDOWS_FLAGS.EWX_POWEROFF, - SHUTDOWN_REASON.SHTDN_REASON_MAJOR_OTHER | SHUTDOWN_REASON.SHTDN_REASON_FLAG_PLANNED); + if (EnableShutdownPrivilege()) + PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_SHUTDOWN | EXIT_WINDOWS_FLAGS.EWX_POWEROFF, + SHUTDOWN_REASON.SHTDN_REASON_MAJOR_OTHER | SHUTDOWN_REASON.SHTDN_REASON_FLAG_PLANNED); + else + Process.Start("shutdown", "/s /t 0"); return true; } @@ -145,8 +190,11 @@ namespace Flow.Launcher.Plugin.Sys MessageBoxButton.YesNo, MessageBoxImage.Warning); if (result == MessageBoxResult.Yes) - PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_REBOOT, - SHUTDOWN_REASON.SHTDN_REASON_MAJOR_OTHER | SHUTDOWN_REASON.SHTDN_REASON_FLAG_PLANNED); + if (EnableShutdownPrivilege()) + PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_REBOOT, + SHUTDOWN_REASON.SHTDN_REASON_MAJOR_OTHER | SHUTDOWN_REASON.SHTDN_REASON_FLAG_PLANNED); + else + Process.Start("shutdown", "/r /t 0"); return true; } @@ -165,8 +213,11 @@ namespace Flow.Launcher.Plugin.Sys MessageBoxButton.YesNo, MessageBoxImage.Warning); if (result == MessageBoxResult.Yes) - PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_REBOOT | EXIT_WINDOWS_FLAGS.EWX_BOOTOPTIONS, - SHUTDOWN_REASON.SHTDN_REASON_MAJOR_OTHER | SHUTDOWN_REASON.SHTDN_REASON_FLAG_PLANNED); + if (EnableShutdownPrivilege()) + PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_REBOOT | EXIT_WINDOWS_FLAGS.EWX_BOOTOPTIONS, + SHUTDOWN_REASON.SHTDN_REASON_MAJOR_OTHER | SHUTDOWN_REASON.SHTDN_REASON_FLAG_PLANNED); + else + Process.Start("shutdown", "/r /o /t 0"); return true; } diff --git a/Plugins/Flow.Launcher.Plugin.Sys/NativeMethods.txt b/Plugins/Flow.Launcher.Plugin.Sys/NativeMethods.txt index 8fcb6cae9..6159c725b 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/NativeMethods.txt +++ b/Plugins/Flow.Launcher.Plugin.Sys/NativeMethods.txt @@ -3,4 +3,9 @@ LockWorkStation SHEmptyRecycleBin S_OK E_UNEXPECTED -SetSuspendState \ No newline at end of file +SetSuspendState +OpenProcessToken +WIN32_ERROR +LookupPrivilegeValue +AdjustTokenPrivileges +TOKEN_PRIVILEGES \ No newline at end of file From 648e3f268990c6da85eef377d5d1b5bc97dc438b Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 18 Feb 2025 11:07:18 +0800 Subject: [PATCH 14/16] Improve code quality --- Plugins/Flow.Launcher.Plugin.Sys/Main.cs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs index 7d3f66746..f6117aab9 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs @@ -23,6 +23,9 @@ namespace Flow.Launcher.Plugin.Sys private Dictionary KeywordTitleMappings = new Dictionary(); private const string SE_SHUTDOWN_NAME = "SeShutdownPrivilege"; + // 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() { @@ -163,13 +166,9 @@ namespace Flow.Launcher.Plugin.Sys context.API.GetTranslation("flowlauncher_plugin_sys_shutdown_computer"), MessageBoxButton.YesNo, MessageBoxImage.Warning); - // 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 if (result == MessageBoxResult.Yes) if (EnableShutdownPrivilege()) - PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_SHUTDOWN | EXIT_WINDOWS_FLAGS.EWX_POWEROFF, - SHUTDOWN_REASON.SHTDN_REASON_MAJOR_OTHER | SHUTDOWN_REASON.SHTDN_REASON_FLAG_PLANNED); + PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_SHUTDOWN | EXIT_WINDOWS_FLAGS.EWX_POWEROFF, REASON); else Process.Start("shutdown", "/s /t 0"); @@ -191,8 +190,7 @@ namespace Flow.Launcher.Plugin.Sys if (result == MessageBoxResult.Yes) if (EnableShutdownPrivilege()) - PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_REBOOT, - SHUTDOWN_REASON.SHTDN_REASON_MAJOR_OTHER | SHUTDOWN_REASON.SHTDN_REASON_FLAG_PLANNED); + PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_REBOOT, REASON); else Process.Start("shutdown", "/r /t 0"); @@ -214,8 +212,7 @@ namespace Flow.Launcher.Plugin.Sys if (result == MessageBoxResult.Yes) if (EnableShutdownPrivilege()) - PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_REBOOT | EXIT_WINDOWS_FLAGS.EWX_BOOTOPTIONS, - SHUTDOWN_REASON.SHTDN_REASON_MAJOR_OTHER | SHUTDOWN_REASON.SHTDN_REASON_FLAG_PLANNED); + PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_REBOOT | EXIT_WINDOWS_FLAGS.EWX_BOOTOPTIONS, REASON); else Process.Start("shutdown", "/r /o /t 0"); @@ -236,8 +233,7 @@ namespace Flow.Launcher.Plugin.Sys MessageBoxButton.YesNo, MessageBoxImage.Warning); if (result == MessageBoxResult.Yes) - PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_LOGOFF, - SHUTDOWN_REASON.SHTDN_REASON_MAJOR_OTHER | SHUTDOWN_REASON.SHTDN_REASON_FLAG_PLANNED); + PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_LOGOFF, REASON); return true; } From a065179d53aee2146e633e0bf2db2ad3ccd066f6 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 18 Feb 2025 11:12:33 +0800 Subject: [PATCH 15/16] Use PInvoke for const --- Plugins/Flow.Launcher.Plugin.Sys/Main.cs | 3 +-- Plugins/Flow.Launcher.Plugin.Sys/NativeMethods.txt | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs index f6117aab9..3bda99e71 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs @@ -22,7 +22,6 @@ namespace Flow.Launcher.Plugin.Sys private PluginInitContext context; private Dictionary KeywordTitleMappings = new Dictionary(); - private const string SE_SHUTDOWN_NAME = "SeShutdownPrivilege"; // 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; @@ -116,7 +115,7 @@ namespace Flow.Launcher.Plugin.Sys return false; } - if (!PInvoke.LookupPrivilegeValue(null, SE_SHUTDOWN_NAME, out var luid)) + if (!PInvoke.LookupPrivilegeValue(null, PInvoke.SE_SHUTDOWN_NAME, out var luid)) { return false; } diff --git a/Plugins/Flow.Launcher.Plugin.Sys/NativeMethods.txt b/Plugins/Flow.Launcher.Plugin.Sys/NativeMethods.txt index 6159c725b..4567e46a3 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/NativeMethods.txt +++ b/Plugins/Flow.Launcher.Plugin.Sys/NativeMethods.txt @@ -8,4 +8,5 @@ OpenProcessToken WIN32_ERROR LookupPrivilegeValue AdjustTokenPrivileges -TOKEN_PRIVILEGES \ No newline at end of file +TOKEN_PRIVILEGES +SE_SHUTDOWN_NAME \ No newline at end of file From 3bd4ca4105840ab4a679a21817148ade91660263 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 18 Feb 2025 11:17:45 +0800 Subject: [PATCH 16/16] Replace hiberate with PInvoke --- Plugins/Flow.Launcher.Plugin.Sys/Main.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs index 3bda99e71..e81d70c52 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs @@ -269,12 +269,7 @@ namespace Flow.Launcher.Plugin.Sys 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; } },