From 84f064ede0955c97f2dc15be813084d199e0da3a Mon Sep 17 00:00:00 2001 From: DB p Date: Mon, 24 Mar 2025 20:53:53 +0900 Subject: [PATCH 1/5] "Add code to check relative changes in the system --- Flow.Launcher/MainWindow.xaml.cs | 63 ++++++++++++++++++++++++++++- Flow.Launcher/SettingWindow.xaml.cs | 6 +-- 2 files changed, 64 insertions(+), 5 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index ec8649efcbb..c9019fdefbe 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -2,6 +2,7 @@ using System.ComponentModel; using System.Linq; using System.Media; +using System.Runtime.InteropServices; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; @@ -20,6 +21,7 @@ using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin.SharedCommands; using Flow.Launcher.ViewModel; +using Microsoft.Win32; using ModernWpf.Controls; using MouseButtons = System.Windows.Forms.MouseButtons; using NotifyIcon = System.Windows.Forms.NotifyIcon; @@ -31,6 +33,11 @@ public partial class MainWindow { #region Private Fields + // Win32 상수 및 구조체 정의 + private const int WM_WTSSESSION_CHANGE = 0x02B1; + private const int WTS_SESSION_LOCK = 0x7; + private const int WTS_SESSION_UNLOCK = 0x8; + // Dependency Injection private readonly Settings _settings; private readonly Theme _theme; @@ -74,8 +81,18 @@ public MainWindow() InitSoundEffects(); DataObject.AddPastingHandler(QueryTextBox, QueryTextBox_OnPaste); + + SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged; + SystemEvents.SessionEnding += SystemEvents_SessionEnding; + } + private void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e) + { + _viewModel.Show(); + } + private void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e) + { + _viewModel.Show(); } - #endregion #region Window Event @@ -89,8 +106,10 @@ private void OnSourceInitialized(object sender, EventArgs e) win.AddHook(WndProc); Win32Helper.HideFromAltTab(this); Win32Helper.DisableControlBox(this); + // 세션 변경 알림 등록 (Windows 잠금 감지) + WTSRegisterSessionNotification(handle, 0); } - + private async void OnLoaded(object sender, RoutedEventArgs _) { // Check first launch @@ -234,6 +253,14 @@ private async void OnLoaded(object sender, RoutedEventArgs _) private async void OnClosing(object sender, CancelEventArgs e) { + // 세션 변경 알림 등록 해제 + var handle = Win32Helper.GetWindowHandle(this, false); + WTSUnRegisterSessionNotification(handle); + + // 기존 이벤트 구독 해제 + SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged; + SystemEvents.SessionEnding -= SystemEvents_SessionEnding; + _notifyIcon.Visible = false; App.API.SaveAppAllSettings(); e.Cancel = true; @@ -241,6 +268,13 @@ private async void OnClosing(object sender, CancelEventArgs e) Notification.Uninstall(); Environment.Exit(0); } + + // Win32 API 함수 정의 + [DllImport("wtsapi32.dll", SetLastError = true)] + private static extern bool WTSRegisterSessionNotification(IntPtr hWnd, int dwFlags); + + [DllImport("wtsapi32.dll", SetLastError = true)] + private static extern bool WTSUnRegisterSessionNotification(IntPtr hWnd); private void OnLocationChanged(object sender, EventArgs e) { @@ -435,6 +469,31 @@ private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref b handled = true; } + // Windows 잠금(Win+L) 이벤트 처리 + else if (msg == WM_WTSSESSION_CHANGE) + { + int reason = wParam.ToInt32(); + if (reason == WTS_SESSION_LOCK) + { + // Windows 잠금 발생 시 메시지 박스 표시 + Application.Current.Dispatcher.Invoke(() => + { + _viewModel.Show(); + }); + + handled = true; + } + else if (reason == WTS_SESSION_UNLOCK) + { + // Windows 잠금 해제 시 메시지 박스 표시 (선택적) + Application.Current.Dispatcher.Invoke(() => + { + _viewModel.Show(); + }); + + handled = true; + } + } return IntPtr.Zero; } diff --git a/Flow.Launcher/SettingWindow.xaml.cs b/Flow.Launcher/SettingWindow.xaml.cs index 28140f0245c..75d770a5be0 100644 --- a/Flow.Launcher/SettingWindow.xaml.cs +++ b/Flow.Launcher/SettingWindow.xaml.cs @@ -36,9 +36,9 @@ private void OnLoaded(object sender, RoutedEventArgs e) RefreshMaximizeRestoreButton(); // Fix (workaround) for the window freezes after lock screen (Win+L) or sleep // https://stackoverflow.com/questions/4951058/software-rendering-mode-wpf - HwndSource hwndSource = PresentationSource.FromVisual(this) as HwndSource; - HwndTarget hwndTarget = hwndSource.CompositionTarget; - hwndTarget.RenderMode = RenderMode.SoftwareOnly; // Must use software only render mode here + //HwndSource hwndSource = PresentationSource.FromVisual(this) as HwndSource; + //HwndTarget hwndTarget = hwndSource.CompositionTarget; + //hwndTarget.RenderMode = RenderMode.SoftwareOnly; // Must use software only render mode here InitializePosition(); } From f5cfc7623de366de3ac9e201b089fdb0a337917c Mon Sep 17 00:00:00 2001 From: DB p Date: Tue, 25 Mar 2025 14:45:11 +0900 Subject: [PATCH 2/5] - Add Custom Show - Changed SoundPlay location --- Flow.Launcher/MainWindow.xaml.cs | 16 ++++---------- Flow.Launcher/ViewModel/MainViewModel.cs | 27 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index c9019fdefbe..919bfaadb15 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -175,11 +175,6 @@ private async void OnLoaded(object sender, RoutedEventArgs _) { if (_viewModel.MainWindowVisibilityStatus) { - if (_settings.UseSound) - { - SoundPlay(); - } - UpdatePosition(false); _viewModel.ResetPreview(); Activate(); @@ -269,7 +264,6 @@ private async void OnClosing(object sender, CancelEventArgs e) Environment.Exit(0); } - // Win32 API 함수 정의 [DllImport("wtsapi32.dll", SetLastError = true)] private static extern bool WTSRegisterSessionNotification(IntPtr hWnd, int dwFlags); @@ -469,26 +463,24 @@ private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref b handled = true; } - // Windows 잠금(Win+L) 이벤트 처리 + // Windows (Win+L) Event else if (msg == WM_WTSSESSION_CHANGE) { int reason = wParam.ToInt32(); if (reason == WTS_SESSION_LOCK) { - // Windows 잠금 발생 시 메시지 박스 표시 Application.Current.Dispatcher.Invoke(() => { - _viewModel.Show(); + _viewModel.SystemWakeUpShow(); }); handled = true; } else if (reason == WTS_SESSION_UNLOCK) { - // Windows 잠금 해제 시 메시지 박스 표시 (선택적) Application.Current.Dispatcher.Invoke(() => { - _viewModel.Show(); + _viewModel.SystemWakeUpShow(); }); handled = true; @@ -515,7 +507,7 @@ private void InitSoundEffects() } } - private void SoundPlay() + public void SoundPlay() { if (_settings.WMPInstalled) { diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 3ce0ba2f0b6..2c7e458fdf7 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1352,12 +1352,39 @@ public void ToggleFlowLauncher() } } + public void SystemWakeUpShow() + { + Application.Current.Dispatcher.Invoke(() => + { + if (Application.Current.MainWindow is MainWindow mainWindow) + { + // 📌 Remove DWM Cloak (Make the window visible normally) + Win32Helper.DWMSetCloakForWindow(mainWindow, false); + + // 📌 Restore UI elements + mainWindow.ClockPanel.Visibility = Visibility.Visible; + //mainWindow.SearchIcon.Visibility = Visibility.Visible; + SearchIconVisibility = Visibility.Visible; + } + + // Update WPF properties + MainWindowOpacity = 0.01; + MainWindowVisibility = Visibility.Visible; + MainWindowVisibilityStatus = true; + VisibilityChanged?.Invoke(this, new VisibilityChangedEventArgs { IsVisible = true }); + Hide(); + }); + } public void Show() { Application.Current.Dispatcher.Invoke(() => { if (Application.Current.MainWindow is MainWindow mainWindow) { + if (Settings.UseSound) + { + mainWindow.SoundPlay(); + } // 📌 Remove DWM Cloak (Make the window visible normally) Win32Helper.DWMSetCloakForWindow(mainWindow, false); From 9c939b0775197ac4871076574ce9171fd0cf7ec8 Mon Sep 17 00:00:00 2001 From: DB p Date: Tue, 25 Mar 2025 16:04:11 +0900 Subject: [PATCH 3/5] AAdd support for receiving system changes even when Flow is not in focus. --- Flow.Launcher/App.xaml.cs | 25 ++++- Flow.Launcher/MainWindow.xaml.cs | 127 +++++++++-------------- Flow.Launcher/ViewModel/MainViewModel.cs | 71 ++++++++++--- 3 files changed, 130 insertions(+), 93 deletions(-) diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index b102e384e70..2a68b5d31ea 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -165,9 +165,32 @@ await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => API.SaveAppAllSettings(); Log.Info( "|App.OnStartup|End Flow Launcher startup ---------------------------------------------------- "); + Microsoft.Win32.SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged; + Microsoft.Win32.SystemEvents.SessionEnding += SystemEvents_SessionEnding; + Microsoft.Win32.SystemEvents.SessionSwitch += SystemEvents_SessionSwitch; }); } - + private void SystemEvents_PowerModeChanged(object sender, Microsoft.Win32.PowerModeChangedEventArgs e) + { + if (e.Mode == Microsoft.Win32.PowerModes.Resume) + { + var mainViewModel = Ioc.Default.GetRequiredService(); + mainViewModel.SystemWakeUpShow(); + } + } + private void SystemEvents_SessionEnding(object sender, Microsoft.Win32.SessionEndingEventArgs e) + { + var mainViewModel = Ioc.Default.GetRequiredService(); + mainViewModel.SystemWakeUpShow(); + } + private void SystemEvents_SessionSwitch(object sender, Microsoft.Win32.SessionSwitchEventArgs e) + { + if (e.Reason == Microsoft.Win32.SessionSwitchReason.SessionUnlock) + { + var mainViewModel = Ioc.Default.GetRequiredService(); + mainViewModel.SystemWakeUpShow(); + } + } #pragma warning restore VSTHRD100 // Avoid async void methods private void AutoStartup() diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 919bfaadb15..43dfa32f6c0 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -33,10 +33,17 @@ public partial class MainWindow { #region Private Fields - // Win32 상수 및 구조체 정의 + //For restore window Freeze private const int WM_WTSSESSION_CHANGE = 0x02B1; - private const int WTS_SESSION_LOCK = 0x7; private const int WTS_SESSION_UNLOCK = 0x8; + private const int NOTIFY_FOR_ALL_SESSIONS = 1; + private const int NOTIFY_FOR_THIS_SESSION = 0; + + [DllImport("wtsapi32.dll")] + private static extern bool WTSRegisterSessionNotification(IntPtr hWnd, int dwFlags); + + [DllImport("wtsapi32.dll")] + private static extern bool WTSUnRegisterSessionNotification(IntPtr hWnd); // Dependency Injection private readonly Settings _settings; @@ -82,16 +89,22 @@ public MainWindow() InitSoundEffects(); DataObject.AddPastingHandler(QueryTextBox, QueryTextBox_OnPaste); - SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged; - SystemEvents.SessionEnding += SystemEvents_SessionEnding; + } private void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e) { - _viewModel.Show(); + _viewModel.SystemWakeUpShow(); } private void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e) { - _viewModel.Show(); + _viewModel.SystemWakeUpShow(); + } + private void SystemEvents_SessionSwitch(object sender, Microsoft.Win32.SessionSwitchEventArgs e) + { + if (e.Reason == Microsoft.Win32.SessionSwitchReason.SessionUnlock) + { + _viewModel.SystemWakeUpShow(); + } } #endregion @@ -101,13 +114,22 @@ private void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventA private void OnSourceInitialized(object sender, EventArgs e) { - var handle = Win32Helper.GetWindowHandle(this, true); - var win = HwndSource.FromHwnd(handle); - win.AddHook(WndProc); - Win32Helper.HideFromAltTab(this); - Win32Helper.DisableControlBox(this); - // 세션 변경 알림 등록 (Windows 잠금 감지) - WTSRegisterSessionNotification(handle, 0); + IntPtr handle = new WindowInteropHelper(this).Handle; + var result = WTSRegisterSessionNotification(handle, NOTIFY_FOR_THIS_SESSION); + + if (!result) + { + //Log.Error($"|MainWindow.OnSourceInitialized|WTSRegisterSessionNotification Failed: {Marshal.GetLastWin32Error()}"); + //Debug.WriteLine("Failed"); + } + else + { + //Log.Info("|MainWindow.OnSourceInitialized|WTSRegisterSessionNotification Sucesss"); + //Debug.WriteLine("Sucesss"); + } + + HwndSource source = PresentationSource.FromVisual(this) as HwndSource; + source?.AddHook(WndProc); } private async void OnLoaded(object sender, RoutedEventArgs _) @@ -248,13 +270,12 @@ private async void OnLoaded(object sender, RoutedEventArgs _) private async void OnClosing(object sender, CancelEventArgs e) { - // 세션 변경 알림 등록 해제 + // Unregister session notification var handle = Win32Helper.GetWindowHandle(this, false); WTSUnRegisterSessionNotification(handle); - - // 기존 이벤트 구독 해제 SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged; SystemEvents.SessionEnding -= SystemEvents_SessionEnding; + SystemEvents.SessionSwitch -= SystemEvents_SessionSwitch; _notifyIcon.Visible = false; App.API.SaveAppAllSettings(); @@ -263,13 +284,13 @@ private async void OnClosing(object sender, CancelEventArgs e) Notification.Uninstall(); Environment.Exit(0); } - - [DllImport("wtsapi32.dll", SetLastError = true)] - private static extern bool WTSRegisterSessionNotification(IntPtr hWnd, int dwFlags); - - [DllImport("wtsapi32.dll", SetLastError = true)] - private static extern bool WTSUnRegisterSessionNotification(IntPtr hWnd); - + protected override void OnClosed(EventArgs e) + { + IntPtr handle = new WindowInteropHelper(this).Handle; + WTSUnRegisterSessionNotification(handle); + + base.OnClosed(e); + } private void OnLocationChanged(object sender, EventArgs e) { if (_animating) @@ -429,64 +450,18 @@ private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref b } else if (msg == Win32Helper.WM_EXITSIZEMOVE) { - if (_initialHeight != (int)Height) - { - var shadowMargin = 0; - var (_, useDropShadowEffect) = _theme.GetActualValue(); - if (useDropShadowEffect) - { - shadowMargin = 32; - } - - if (!_settings.KeepMaxResults) - { - var itemCount = (Height - (_settings.WindowHeightSize + 14) - shadowMargin) / _settings.ItemHeightSize; - - if (itemCount < 2) - { - _settings.MaxResultsToShow = 2; - } - else - { - _settings.MaxResultsToShow = Convert.ToInt32(Math.Truncate(itemCount)); - } - } - - SizeToContent = SizeToContent.Height; - _viewModel.MainWindowWidth = Width; - } - - if (_initialWidth != (int)Width) - { - SizeToContent = SizeToContent.Height; - } - + // 기존 코드 handled = true; } + // Windows (Win+L) Event - else if (msg == WM_WTSSESSION_CHANGE) + if (msg == WM_WTSSESSION_CHANGE && wParam.ToInt32() == WTS_SESSION_UNLOCK) { - int reason = wParam.ToInt32(); - if (reason == WTS_SESSION_LOCK) - { - Application.Current.Dispatcher.Invoke(() => - { - _viewModel.SystemWakeUpShow(); - }); - - handled = true; - } - else if (reason == WTS_SESSION_UNLOCK) - { - Application.Current.Dispatcher.Invoke(() => - { - _viewModel.SystemWakeUpShow(); - }); - - handled = true; - } + // 기존 코드 + handled = true; } - + + // 여기에 반환문 추가 return IntPtr.Zero; } diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 2c7e458fdf7..6ab6271e003 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -4,6 +4,7 @@ using System.Globalization; using System.Windows.Input; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Channels; @@ -24,6 +25,8 @@ using Flow.Launcher.Plugin.SharedCommands; using Flow.Launcher.Storage; using Microsoft.VisualStudio.Threading; +using System.Windows.Interop; +using System.Diagnostics; namespace Flow.Launcher.ViewModel { @@ -48,7 +51,20 @@ public partial class MainViewModel : BaseModel, ISavable private ChannelWriter _resultsUpdateChannelWriter; private Task _resultsViewUpdateTask; - + + //For restore window Freeze + [DllImport("user32.dll")] + public static extern bool AllowSetForegroundWindow(int processId); + + [DllImport("user32.dll")] + public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); + + [DllImport("user32.dll")] + public static extern bool BringWindowToTop(IntPtr hWnd); + + private const int SW_SHOW = 5; + private const int SW_RESTORE = 9; + #endregion #region Constructor @@ -1356,23 +1372,46 @@ public void SystemWakeUpShow() { Application.Current.Dispatcher.Invoke(() => { - if (Application.Current.MainWindow is MainWindow mainWindow) + try { - // 📌 Remove DWM Cloak (Make the window visible normally) - Win32Helper.DWMSetCloakForWindow(mainWindow, false); - - // 📌 Restore UI elements - mainWindow.ClockPanel.Visibility = Visibility.Visible; - //mainWindow.SearchIcon.Visibility = Visibility.Visible; - SearchIconVisibility = Visibility.Visible; + if (Application.Current.MainWindow is MainWindow mainWindow) + { + Win32Helper.DWMSetCloakForWindow(mainWindow, false); + mainWindow.ClockPanel.Visibility = Visibility.Visible; + SearchIconVisibility = Visibility.Visible; + + MainWindowOpacity = 0; + MainWindowVisibility = Visibility.Visible; + MainWindowVisibilityStatus = true; + + VisibilityChanged?.Invoke(this, new VisibilityChangedEventArgs { IsVisible = true }); + + mainWindow.Topmost = true; + + // 창 표시 및 활성화 + mainWindow.Show(); + mainWindow.Activate(); + mainWindow.Focus(); + + // Win32 메서드로 강제 활성화 + var hwnd = new WindowInteropHelper(mainWindow).Handle; + Win32Helper.SetForegroundWindow(hwnd); + + // 잠시 후 Topmost 해제 + Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => + { + mainWindow.Topmost = false; + })); + } + else + { + Log.Error("|MainViewModel.SystemWakeUpShow|MainWindow can't find"); + } + } + catch (Exception ex) + { + Log.Exception("|MainViewModel.SystemWakeUpShow|error", ex); } - - // Update WPF properties - MainWindowOpacity = 0.01; - MainWindowVisibility = Visibility.Visible; - MainWindowVisibilityStatus = true; - VisibilityChanged?.Invoke(this, new VisibilityChangedEventArgs { IsVisible = true }); - Hide(); }); } public void Show() From bd6ac614064c0b844be32ca24e6d199dba08719d Mon Sep 17 00:00:00 2001 From: DB p Date: Tue, 25 Mar 2025 16:15:11 +0900 Subject: [PATCH 4/5] Fix blink window issue --- Flow.Launcher/ViewModel/MainViewModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 6ab6271e003..03de75fb939 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1376,7 +1376,7 @@ public void SystemWakeUpShow() { if (Application.Current.MainWindow is MainWindow mainWindow) { - Win32Helper.DWMSetCloakForWindow(mainWindow, false); + Win32Helper.DWMSetCloakForWindow(mainWindow, true); mainWindow.ClockPanel.Visibility = Visibility.Visible; SearchIconVisibility = Visibility.Visible; From 514e91d2960b0cc0ce04480da6fc301f165c9eb9 Mon Sep 17 00:00:00 2001 From: DB p Date: Tue, 25 Mar 2025 16:15:34 +0900 Subject: [PATCH 5/5] Remove Comment --- Flow.Launcher/ViewModel/MainViewModel.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 03de75fb939..e7ad6e9effc 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1392,12 +1392,10 @@ public void SystemWakeUpShow() mainWindow.Show(); mainWindow.Activate(); mainWindow.Focus(); - - // Win32 메서드로 강제 활성화 + var hwnd = new WindowInteropHelper(mainWindow).Handle; Win32Helper.SetForegroundWindow(hwnd); - - // 잠시 후 Topmost 해제 + Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => { mainWindow.Topmost = false;