diff --git a/src/common/ManagedCommon/NativeMethods.cs b/src/common/ManagedCommon/NativeMethods.cs index b4c2a78fe3..feffafa946 100644 --- a/src/common/ManagedCommon/NativeMethods.cs +++ b/src/common/ManagedCommon/NativeMethods.cs @@ -38,5 +38,67 @@ namespace ManagedCommon [DllImport("user32.dll")] internal static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach); + + [DllImport("user32.dll")] + internal static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize); + + [StructLayout(LayoutKind.Sequential)] + public struct INPUT + { + internal INPUTTYPE type; + internal InputUnion data; + + internal static int Size + { + get { return Marshal.SizeOf(typeof(INPUT)); } + } + } + + [StructLayout(LayoutKind.Explicit)] + internal struct InputUnion + { + [FieldOffset(0)] + internal MOUSEINPUT mi; + [FieldOffset(0)] + internal KEYBDINPUT ki; + [FieldOffset(0)] + internal HARDWAREINPUT hi; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct MOUSEINPUT + { + internal int dx; + internal int dy; + internal int mouseData; + internal uint dwFlags; + internal uint time; + internal UIntPtr dwExtraInfo; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct KEYBDINPUT + { + internal short wVk; + internal short wScan; + internal uint dwFlags; + internal int time; + internal UIntPtr dwExtraInfo; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct HARDWAREINPUT + { + internal int uMsg; + internal short wParamL; + internal short wParamH; + } + + internal enum INPUTTYPE : uint + { + INPUT_MOUSE = 0, + INPUT_KEYBOARD = 1, + INPUT_HARDWARE = 2, + } } } diff --git a/src/common/ManagedCommon/WindowHelpers.cs b/src/common/ManagedCommon/WindowHelpers.cs index 271bbc827c..bd9d334722 100644 --- a/src/common/ManagedCommon/WindowHelpers.cs +++ b/src/common/ManagedCommon/WindowHelpers.cs @@ -10,20 +10,29 @@ namespace ManagedCommon { public static void BringToForeground(IntPtr handle) { + NativeMethods.INPUT input = new NativeMethods.INPUT { type = NativeMethods.INPUTTYPE.INPUT_MOUSE, data = { } }; + NativeMethods.INPUT[] inputs = new NativeMethods.INPUT[] { input }; + _ = NativeMethods.SendInput(1, inputs, NativeMethods.INPUT.Size); + + NativeMethods.SetForegroundWindow(handle); + var fgHandle = NativeMethods.GetForegroundWindow(); - var threadId1 = NativeMethods.GetWindowThreadProcessId(handle, System.IntPtr.Zero); - var threadId2 = NativeMethods.GetWindowThreadProcessId(fgHandle, System.IntPtr.Zero); + if (fgHandle != handle) + { + var threadId1 = NativeMethods.GetWindowThreadProcessId(handle, System.IntPtr.Zero); + var threadId2 = NativeMethods.GetWindowThreadProcessId(fgHandle, System.IntPtr.Zero); - if (threadId1 != threadId2) - { - NativeMethods.AttachThreadInput(threadId1, threadId2, true); - NativeMethods.SetForegroundWindow(handle); - NativeMethods.AttachThreadInput(threadId1, threadId2, false); - } - else - { - NativeMethods.SetForegroundWindow(handle); + if (threadId1 != threadId2) + { + NativeMethods.AttachThreadInput(threadId1, threadId2, true); + NativeMethods.SetForegroundWindow(handle); + NativeMethods.AttachThreadInput(threadId1, threadId2, false); + } + else + { + NativeMethods.SetForegroundWindow(handle); + } } } } diff --git a/src/modules/Hosts/Hosts/HostsXAML/MainWindow.xaml.cs b/src/modules/Hosts/Hosts/HostsXAML/MainWindow.xaml.cs index a4fb624610..d6a474b841 100644 --- a/src/modules/Hosts/Hosts/HostsXAML/MainWindow.xaml.cs +++ b/src/modules/Hosts/Hosts/HostsXAML/MainWindow.xaml.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using Hosts.Helpers; +using ManagedCommon; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Media; using WinUIEx; @@ -25,7 +26,7 @@ namespace Hosts AppTitleTextBlock.Text = title; var handle = this.GetWindowHandle(); - ManagedCommon.WindowHelpers.BringToForeground(handle); + WindowHelpers.BringToForeground(handle); Activated += MainWindow_Activated; } diff --git a/src/modules/imageresizer/ui/App.xaml.cs b/src/modules/imageresizer/ui/App.xaml.cs index 2840739a20..c2978725d9 100644 --- a/src/modules/imageresizer/ui/App.xaml.cs +++ b/src/modules/imageresizer/ui/App.xaml.cs @@ -10,6 +10,7 @@ using ImageResizer.Properties; using ImageResizer.Utilities; using ImageResizer.ViewModels; using ImageResizer.Views; +using ManagedCommon; namespace ImageResizer { @@ -41,15 +42,7 @@ namespace ImageResizer mainWindow.Show(); // Temporary workaround for issue #1273 - BecomeForegroundWindow(new System.Windows.Interop.WindowInteropHelper(mainWindow).Handle); - } - - private static void BecomeForegroundWindow(IntPtr hWnd) - { - NativeMethods.INPUT input = new NativeMethods.INPUT { type = NativeMethods.INPUTTYPE.INPUT_MOUSE, data = { } }; - NativeMethods.INPUT[] inputs = new NativeMethods.INPUT[] { input }; - _ = NativeMethods.SendInput(1, inputs, NativeMethods.INPUT.Size); - NativeMethods.SetForegroundWindow(hWnd); + WindowHelpers.BringToForeground(new System.Windows.Interop.WindowInteropHelper(mainWindow).Handle); } public void Dispose() diff --git a/src/modules/imageresizer/ui/Utilities/NativeMethods.cs b/src/modules/imageresizer/ui/Utilities/NativeMethods.cs index 4a96ae68dc..98f5b0bf1e 100644 --- a/src/modules/imageresizer/ui/Utilities/NativeMethods.cs +++ b/src/modules/imageresizer/ui/Utilities/NativeMethods.cs @@ -15,69 +15,7 @@ namespace ImageResizer.Utilities [DllImport("user32.dll")] internal static extern bool SetForegroundWindow(IntPtr hWnd); - [DllImport("user32.dll")] - internal static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize); - [DllImport("user32.dll", SetLastError = true)] internal static extern bool SetProcessDPIAware(); - - [StructLayout(LayoutKind.Sequential)] - public struct INPUT - { - internal INPUTTYPE type; - internal InputUnion data; - - internal static int Size - { - get { return Marshal.SizeOf(typeof(INPUT)); } - } - } - - [StructLayout(LayoutKind.Explicit)] - internal struct InputUnion - { - [FieldOffset(0)] - internal MOUSEINPUT mi; - [FieldOffset(0)] - internal KEYBDINPUT ki; - [FieldOffset(0)] - internal HARDWAREINPUT hi; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct MOUSEINPUT - { - internal int dx; - internal int dy; - internal int mouseData; - internal uint dwFlags; - internal uint time; - internal UIntPtr dwExtraInfo; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct KEYBDINPUT - { - internal short wVk; - internal short wScan; - internal uint dwFlags; - internal int time; - internal UIntPtr dwExtraInfo; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HARDWAREINPUT - { - internal int uMsg; - internal short wParamL; - internal short wParamH; - } - - internal enum INPUTTYPE : uint - { - INPUT_MOUSE = 0, - INPUT_KEYBOARD = 1, - INPUT_HARDWARE = 2, - } } } diff --git a/src/modules/peek/Peek.UI/Extensions/WindowExtensions.cs b/src/modules/peek/Peek.UI/Extensions/WindowExtensions.cs index 7deb604d1a..c7fd32a875 100644 --- a/src/modules/peek/Peek.UI/Extensions/WindowExtensions.cs +++ b/src/modules/peek/Peek.UI/Extensions/WindowExtensions.cs @@ -22,19 +22,6 @@ namespace Peek.UI.Extensions return hwnd.GetMonitorScale(); } - public static void BringToForeground(this Window window) - { - // Ability to be able to set the Window to the Foreground is very limited. A current workaround is simulating mouse input before bringing to the foreground. - var windowHandle = window.GetWindowHandle(); - Windows.Win32.UI.Input.KeyboardAndMouse.INPUT input = new Windows.Win32.UI.Input.KeyboardAndMouse.INPUT { type = Windows.Win32.UI.Input.KeyboardAndMouse.INPUT_TYPE.INPUT_MOUSE, Anonymous = { } }; - Windows.Win32.UI.Input.KeyboardAndMouse.INPUT[] inputs = new Windows.Win32.UI.Input.KeyboardAndMouse.INPUT[] { input }; - _ = PInvoke.SendInput(inputs.AsSpan(), Marshal.SizeOf(typeof(Windows.Win32.UI.Input.KeyboardAndMouse.INPUT))); - if (PInvoke.SetForegroundWindow(new HWND(windowHandle)) == 0) - { - Logger.LogWarning("Couldn't set the Peek window as the foreground window."); - } - } - internal static void CenterOnMonitor(this Window window, HWND hwndDesktop, double? width = null, double? height = null) { var hwndToCenter = new HWND(window.GetWindowHandle()); diff --git a/src/modules/peek/Peek.UI/NativeMethods.txt b/src/modules/peek/Peek.UI/NativeMethods.txt index 3f9cc3e8f7..3e15b22f94 100644 --- a/src/modules/peek/Peek.UI/NativeMethods.txt +++ b/src/modules/peek/Peek.UI/NativeMethods.txt @@ -4,7 +4,6 @@ GetDpiForWindow GetForegroundWindow SetForegroundWindow SetActiveWindow -SendInput GetWindowThreadProcessId GetCurrentThreadId AttachThreadInput diff --git a/src/modules/peek/Peek.UI/PeekXAML/MainWindow.xaml.cs b/src/modules/peek/Peek.UI/PeekXAML/MainWindow.xaml.cs index b28fed13c0..5254e54513 100644 --- a/src/modules/peek/Peek.UI/PeekXAML/MainWindow.xaml.cs +++ b/src/modules/peek/Peek.UI/PeekXAML/MainWindow.xaml.cs @@ -184,7 +184,7 @@ namespace Peek.UI } this.Show(); - this.BringToForeground(); + WindowHelpers.BringToForeground(this.GetWindowHandle()); } private Size GetMonitorMaxContentSize(Size monitorSize, double scaling) diff --git a/src/modules/registrypreview/RegistryPreviewUI/RegistryPreviewXAML/MainWindow.xaml.cs b/src/modules/registrypreview/RegistryPreviewUI/RegistryPreviewXAML/MainWindow.xaml.cs index da38339b0d..37e455c503 100644 --- a/src/modules/registrypreview/RegistryPreviewUI/RegistryPreviewXAML/MainWindow.xaml.cs +++ b/src/modules/registrypreview/RegistryPreviewUI/RegistryPreviewXAML/MainWindow.xaml.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.IO; +using ManagedCommon; using Microsoft.UI; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; @@ -98,7 +99,7 @@ namespace RegistryPreview UpdateWindowTitle(resourceLoader.GetString("FileNotFound")); } - ManagedCommon.WindowHelpers.BringToForeground(windowHandle); + WindowHelpers.BringToForeground(windowHandle); } private void MainWindow_Activated(object sender, WindowActivatedEventArgs args) diff --git a/src/settings-ui/Settings.UI/Helpers/Utils.cs b/src/settings-ui/Settings.UI/Helpers/Utils.cs index 7f34815bf8..43a406feba 100644 --- a/src/settings-ui/Settings.UI/Helpers/Utils.cs +++ b/src/settings-ui/Settings.UI/Helpers/Utils.cs @@ -45,13 +45,5 @@ namespace Microsoft.PowerToys.Settings.UI.Helpers { } } - - public static void BecomeForegroundWindow(IntPtr hWnd) - { - NativeKeyboardHelper.INPUT input = new NativeKeyboardHelper.INPUT { type = NativeKeyboardHelper.INPUTTYPE.INPUT_MOUSE, data = { } }; - NativeKeyboardHelper.INPUT[] inputs = new NativeKeyboardHelper.INPUT[] { input }; - _ = NativeMethods.SendInput(1, inputs, NativeKeyboardHelper.INPUT.Size); - NativeMethods.SetForegroundWindow(hWnd); - } } } diff --git a/src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs index 202cadc306..ecb2f6e50a 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs @@ -170,7 +170,7 @@ namespace Microsoft.PowerToys.Settings.UI // https://github.com/microsoft/microsoft-ui-xaml/issues/7595 - Activate doesn't bring window to the foreground // Need to call SetForegroundWindow to actually gain focus. - Utils.BecomeForegroundWindow(settingsWindow.GetWindowHandle()); + WindowHelpers.BringToForeground(settingsWindow.GetWindowHandle()); } else { diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Flyout/LaunchPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Flyout/LaunchPage.xaml.cs index f28ccd25ce..ad4cbb4b45 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Flyout/LaunchPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Flyout/LaunchPage.xaml.cs @@ -33,6 +33,10 @@ namespace Microsoft.PowerToys.Settings.UI.Flyout { FlyoutMenuButton selectedModuleBtn = sender as FlyoutMenuButton; bool moduleRun = true; + + // Closing manually the flyout to workaround focus gain problems + App.GetFlyoutWindow()?.Hide(); + switch ((string)selectedModuleBtn.Tag) { case "ColorPicker": // Launch ColorPicker diff --git a/src/settings-ui/Settings.UI/SettingsXAML/MainWindow.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/MainWindow.xaml.cs index 874e3b6130..78216278ae 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/MainWindow.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/MainWindow.xaml.cs @@ -205,7 +205,7 @@ namespace Microsoft.PowerToys.Settings.UI // https://github.com/microsoft/microsoft-ui-xaml/issues/7595 - Activate doesn't bring window to the foreground // Need to call SetForegroundWindow to actually gain focus. - Utils.BecomeForegroundWindow(flyout.GetWindowHandle()); + WindowHelpers.BringToForeground(flyout.GetWindowHandle()); }); });