Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down
8 changes: 8 additions & 0 deletions src/UniGetUI.Core.Data/CoreData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,14 @@ public static string UniGetUIExecutableFile
/// </summary>
public static string ElevatorArgs = "";

/// <summary>
/// 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.
/// </summary>
public static Func<Task>? BringMainWindowToForegroundAsync;

/// <summary>
/// This method will return the most appropriate data directory.
/// If the new directory exists, it will be used.
Expand Down
30 changes: 30 additions & 0 deletions src/UniGetUI.Core.Tools/Tools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Comment thread
GabrielDuf marked this conversation as resolved.

/// <summary>
/// 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.
/// </summary>
public static async Task PrepareForegroundForElevationAsync()
{
if (!OperatingSystem.IsWindows())
return;

var bringToFront = CoreData.BringMainWindowToForegroundAsync;
if (bringToFront is not null)
await bringToFront();
AllowSetForegroundWindow(ASFW_ANY);
}

/// <summary>
/// Enables GSudo cache for the current process
/// </summary>
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -106,6 +107,13 @@ protected override async Task<OperationVeredict> 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)
{
Expand Down
Loading