From 14565cf81294b05a9963321b88065dd6898e911d Mon Sep 17 00:00:00 2001 From: Steffen Carlsen Date: Mon, 1 Jun 2026 14:53:51 +0200 Subject: [PATCH 1/2] fix: position windows above relative target --- src/ModernOverlay.Win32/NativeMethods.cs | 1 + src/ModernOverlay.Win32/Win32WindowZOrder.cs | 8 ++++- tests/ModernOverlay.Tests/Win32StyleTests.cs | 34 ++++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/ModernOverlay.Win32/NativeMethods.cs b/src/ModernOverlay.Win32/NativeMethods.cs index 08e41c5..8c5f7bd 100644 --- a/src/ModernOverlay.Win32/NativeMethods.cs +++ b/src/ModernOverlay.Win32/NativeMethods.cs @@ -72,6 +72,7 @@ internal static class NativeMethods internal const int VkRWin = 0x5C; internal static readonly nint HwndTopMost = new(-1); + internal static readonly nint HwndTop = new(0); internal static readonly nint HwndNoTopMost = new(-2); internal static readonly nint DpiAwarenessContextPerMonitorAwareV2 = new(-4); diff --git a/src/ModernOverlay.Win32/Win32WindowZOrder.cs b/src/ModernOverlay.Win32/Win32WindowZOrder.cs index e677f4d..d7ccafd 100644 --- a/src/ModernOverlay.Win32/Win32WindowZOrder.cs +++ b/src/ModernOverlay.Win32/Win32WindowZOrder.cs @@ -19,7 +19,13 @@ public static void PlaceAbove(nint hwnd, nint hwndInsertAfter) throw new ArgumentException("The relative z-order HWND must be a valid window.", nameof(hwndInsertAfter)); } - SetZOrder(hwnd, hwndInsertAfter, "SetWindowPos(place above)"); + nint previousWindow = NativeMethods.GetWindow(hwndInsertAfter, NativeMethods.GwHwndPrev); + if (previousWindow == hwnd) + { + return; + } + + SetZOrder(hwnd, previousWindow == 0 ? NativeMethods.HwndTop : previousWindow, "SetWindowPos(place above)"); } private static void SetZOrder(nint hwnd, nint insertAfter, string operation) diff --git a/tests/ModernOverlay.Tests/Win32StyleTests.cs b/tests/ModernOverlay.Tests/Win32StyleTests.cs index 31ef57b..a5852e4 100644 --- a/tests/ModernOverlay.Tests/Win32StyleTests.cs +++ b/tests/ModernOverlay.Tests/Win32StyleTests.cs @@ -98,6 +98,40 @@ public void Win32QueryExposesRequiredWindowMetadata() Assert.AreEqual(Win32WindowDisplayAffinity.None, Win32WindowEffects.GetDisplayAffinity(window.Hwnd)); } + [TestMethod] + [TestCategory("WindowsIntegration")] + public void PlaceAbovePositionsWindowImmediatelyAboveRelativeWindow() + { + using Win32OverlayWindow target = Win32OverlayWindow.Create(new Win32OverlayWindowOptions( + ClassName: null, + Title: "ModernOverlay z-order target", + X: 20, + Y: 20, + Width: 160, + Height: 90, + ClickThrough: false, + TopMost: false, + ToolWindow: true)); + using Win32OverlayWindow overlay = Win32OverlayWindow.Create(new Win32OverlayWindowOptions( + ClassName: null, + Title: "ModernOverlay z-order overlay", + X: 20, + Y: 20, + Width: 160, + Height: 90, + ClickThrough: true, + TopMost: false, + ToolWindow: true)); + + target.Show(); + overlay.Show(); + + Win32WindowZOrder.PlaceAbove(overlay.Hwnd, target.Hwnd); + + Assert.IsTrue(Win32WindowQuery.TryGetPreviousWindow(target.Hwnd, out nint previous)); + Assert.AreEqual(overlay.Hwnd, previous); + } + [TestMethod] [TestCategory("WindowsIntegration")] public async Task OverlayOptionAppliesCaptureExclusionBeforeUse() From 4dbd6412822b502299035bfaa57e3b374a6126c0 Mon Sep 17 00:00:00 2001 From: Steffen Carlsen Date: Mon, 1 Jun 2026 15:08:09 +0200 Subject: [PATCH 2/2] fix: validate moved hwnd before z-order shortcut --- src/ModernOverlay.Win32/Win32WindowZOrder.cs | 5 +++++ tests/ModernOverlay.Tests/Win32StyleTests.cs | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/ModernOverlay.Win32/Win32WindowZOrder.cs b/src/ModernOverlay.Win32/Win32WindowZOrder.cs index d7ccafd..fb61547 100644 --- a/src/ModernOverlay.Win32/Win32WindowZOrder.cs +++ b/src/ModernOverlay.Win32/Win32WindowZOrder.cs @@ -14,6 +14,11 @@ public static void RemoveTopmost(nint hwnd) public static void PlaceAbove(nint hwnd, nint hwndInsertAfter) { + if (!Win32WindowQuery.IsWindow(hwnd)) + { + throw new ArgumentException("The HWND must be a valid window.", nameof(hwnd)); + } + if (!Win32WindowQuery.IsWindow(hwndInsertAfter)) { throw new ArgumentException("The relative z-order HWND must be a valid window.", nameof(hwndInsertAfter)); diff --git a/tests/ModernOverlay.Tests/Win32StyleTests.cs b/tests/ModernOverlay.Tests/Win32StyleTests.cs index a5852e4..8c6ed24 100644 --- a/tests/ModernOverlay.Tests/Win32StyleTests.cs +++ b/tests/ModernOverlay.Tests/Win32StyleTests.cs @@ -132,6 +132,24 @@ public void PlaceAbovePositionsWindowImmediatelyAboveRelativeWindow() Assert.AreEqual(overlay.Hwnd, previous); } + [TestMethod] + [TestCategory("WindowsIntegration")] + public void PlaceAboveRejectsInvalidMovedWindowHandle() + { + using Win32OverlayWindow target = Win32OverlayWindow.Create(new Win32OverlayWindowOptions( + ClassName: null, + Title: "ModernOverlay invalid z-order target", + X: 20, + Y: 20, + Width: 160, + Height: 90, + ClickThrough: false, + TopMost: false, + ToolWindow: true)); + + Assert.ThrowsExactly(() => Win32WindowZOrder.PlaceAbove(0, target.Hwnd)); + } + [TestMethod] [TestCategory("WindowsIntegration")] public async Task OverlayOptionAppliesCaptureExclusionBeforeUse()