diff --git a/src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs b/src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs
index cb0387360d..7cef17c3a5 100644
--- a/src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs
+++ b/src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs
@@ -14,6 +14,7 @@
using UniGetUI.Avalonia.Infrastructure;
using UniGetUI.Avalonia.ViewModels;
using UniGetUI.Avalonia.Views.Pages;
+using UniGetUI.Core.Data;
using UniGetUI.Core.Logging;
using UniGetUI.Core.SettingsEngine;
using UniGetUI.Core.Tools;
@@ -140,6 +141,12 @@ public MainWindow()
_trayService = new TrayService(this);
_trayService.UpdateStatus();
+
+ // Let the elevation code activate us right before a UAC prompt, so we own the foreground
+ // and can delegate it to the consent UI (#5146). Only activate when we're already on screen;
+ // don't yank a tray-hidden window forward during silent/background elevation.
+ CoreData.BringMainWindowToForegroundAsync = () =>
+ Dispatcher.UIThread.InvokeAsync(() => { if (IsVisible) ShowFromTray(); }).GetTask();
}
protected override void OnOpened(EventArgs e)
diff --git a/src/UniGetUI.Core.Data/CoreData.cs b/src/UniGetUI.Core.Data/CoreData.cs
index e164d59354..7409e607c7 100644
--- a/src/UniGetUI.Core.Data/CoreData.cs
+++ b/src/UniGetUI.Core.Data/CoreData.cs
@@ -427,6 +427,14 @@ public static string UniGetUIExecutableFile
///
public static string ElevatorArgs = "";
+ ///
+ /// Set by the UI layer to bring the main window to the foreground and wait until it is.
+ /// Called right before a UAC prompt is triggered so the app owns the foreground and can
+ /// delegate it to the consent UI (fixes prompts hiding behind the window with secure desktop off).
+ /// Null in headless mode, where no window exists to activate.
+ ///
+ public static Func? BringMainWindowToForegroundAsync;
+
///
/// This method will return the most appropriate data directory.
/// If the new directory exists, it will be used.
diff --git a/src/UniGetUI.Core.Tools/Tools.cs b/src/UniGetUI.Core.Tools/Tools.cs
index 05ac720f70..05f4f8b847 100644
--- a/src/UniGetUI.Core.Tools/Tools.cs
+++ b/src/UniGetUI.Core.Tools/Tools.cs
@@ -603,6 +603,31 @@ public static string EnsureSafeQueryString(string query)
return new Uri(url);
}
+ // Grants every process the right to set the foreground window, bypassing the
+ // foreground lock so a UAC consent prompt can surface in front. ASFW_ANY = -1.
+ private const int ASFW_ANY = -1;
+
+ [DllImport("user32.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ private static extern bool AllowSetForegroundWindow(int dwProcessId);
+
+ ///
+ /// Windows: bring UniGetUI to the foreground and grant foreground rights so an imminent
+ /// UAC consent prompt surfaces in front instead of only flashing the taskbar (#5146).
+ /// No-op elsewhere, and when the app owns no visible foreground window to delegate.
+ /// Must be called immediately before launching the elevator.
+ ///
+ public static async Task PrepareForegroundForElevationAsync()
+ {
+ if (!OperatingSystem.IsWindows())
+ return;
+
+ var bringToFront = CoreData.BringMainWindowToForegroundAsync;
+ if (bringToFront is not null)
+ await bringToFront();
+ AllowSetForegroundWindow(ASFW_ANY);
+ }
+
///
/// Enables GSudo cache for the current process
///
@@ -656,6 +681,11 @@ public static async Task CacheUACForCurrentProcess()
StandardOutputEncoding = Encoding.UTF8,
},
};
+
+ // When admin-rights caching is enabled, the UAC consent prompt is raised here.
+ // Surface it in front instead of letting it flash unnoticed in the taskbar (#5146).
+ await PrepareForegroundForElevationAsync();
+
p.Start();
await p.WaitForExitAsync();
_isCaching = false;
diff --git a/src/UniGetUI.PackageEngine.Operations/AbstractProcessOperation.cs b/src/UniGetUI.PackageEngine.Operations/AbstractProcessOperation.cs
index a86613dda5..6b125b6e1e 100644
--- a/src/UniGetUI.PackageEngine.Operations/AbstractProcessOperation.cs
+++ b/src/UniGetUI.PackageEngine.Operations/AbstractProcessOperation.cs
@@ -1,5 +1,6 @@
using System.Diagnostics;
using System.Text;
+using UniGetUI.Core.Data;
using UniGetUI.Core.Logging;
using UniGetUI.Core.SettingsEngine;
using UniGetUI.Core.Tools;
@@ -106,6 +107,13 @@ protected override async Task PerformOperation()
}
CancellationToken.ThrowIfCancellationRequested();
+
+ // When admin-rights caching is disabled (or a cache miss), the elevator is launched
+ // directly here and the UAC prompt is raised at this Start() — bring it to the
+ // foreground too, not just on the cached path (#5146).
+ if (process.StartInfo.FileName == CoreData.ElevatorPath)
+ await CoreTools.PrepareForegroundForElevationAsync();
+
process.Start();
if (CancellationToken.IsCancellationRequested)
{