diff --git a/src/UniGetUI.Core.Logger/Logger.cs b/src/UniGetUI.Core.Logger/Logger.cs index 14789f024..a6a40a34e 100644 --- a/src/UniGetUI.Core.Logger/Logger.cs +++ b/src/UniGetUI.Core.Logger/Logger.cs @@ -7,64 +7,64 @@ public static class Logger private static readonly List LogContents = []; // String parameter log functions - public static void ImportantInfo(string s) + public static void ImportantInfo(string s, [System.Runtime.CompilerServices.CallerMemberName] string caller = "") { - Diagnostics.Debug.WriteLine(s); + Diagnostics.Debug.WriteLine($"[{caller}] " + s); LogContents.Add(new LogEntry(s, LogEntry.SeverityLevel.Success)); } - public static void Debug(string s) + public static void Debug(string s, [System.Runtime.CompilerServices.CallerMemberName] string caller = "") { - Diagnostics.Debug.WriteLine(s); + Diagnostics.Debug.WriteLine($"[{caller}] " + s); LogContents.Add(new LogEntry(s, LogEntry.SeverityLevel.Debug)); } - public static void Info(string s) + public static void Info(string s, [System.Runtime.CompilerServices.CallerMemberName] string caller = "") { - Diagnostics.Debug.WriteLine(s); + Diagnostics.Debug.WriteLine($"[{caller}] " + s); LogContents.Add(new LogEntry(s, LogEntry.SeverityLevel.Info)); } - public static void Warn(string s) + public static void Warn(string s, [System.Runtime.CompilerServices.CallerMemberName] string caller = "") { - Diagnostics.Debug.WriteLine(s); + Diagnostics.Debug.WriteLine($"[{caller}] " + s); LogContents.Add(new LogEntry(s, LogEntry.SeverityLevel.Warning)); } - public static void Error(string s) + public static void Error(string s, [System.Runtime.CompilerServices.CallerMemberName] string caller = "") { - Diagnostics.Debug.WriteLine(s); + Diagnostics.Debug.WriteLine($"[{caller}] " + s); LogContents.Add(new LogEntry(s, LogEntry.SeverityLevel.Error)); } // Exception parameter log functions - public static void ImportantInfo(Exception e) + public static void ImportantInfo(Exception e, [System.Runtime.CompilerServices.CallerMemberName] string caller = "") { - Diagnostics.Debug.WriteLine(e.ToString()); + Diagnostics.Debug.WriteLine($"[{caller}] " + e.ToString()); LogContents.Add(new LogEntry(e.ToString(), LogEntry.SeverityLevel.Success)); } - public static void Debug(Exception e) + public static void Debug(Exception e, [System.Runtime.CompilerServices.CallerMemberName] string caller = "") { - Diagnostics.Debug.WriteLine(e.ToString()); + Diagnostics.Debug.WriteLine($"[{caller}] " + e.ToString()); LogContents.Add(new LogEntry(e.ToString(), LogEntry.SeverityLevel.Debug)); } - public static void Info(Exception e) + public static void Info(Exception e, [System.Runtime.CompilerServices.CallerMemberName] string caller = "") { - Diagnostics.Debug.WriteLine(e.ToString()); + Diagnostics.Debug.WriteLine($"[{caller}] " + e.ToString()); LogContents.Add(new LogEntry(e.ToString(), LogEntry.SeverityLevel.Info)); } - public static void Warn(Exception e) + public static void Warn(Exception e, [System.Runtime.CompilerServices.CallerMemberName] string caller = "") { - Diagnostics.Debug.WriteLine(e.ToString()); + Diagnostics.Debug.WriteLine($"[{caller}] " + e.ToString()); LogContents.Add(new LogEntry(e.ToString(), LogEntry.SeverityLevel.Warning)); } - public static void Error(Exception e) + public static void Error(Exception e, [System.Runtime.CompilerServices.CallerMemberName] string caller = "") { - Diagnostics.Debug.WriteLine(e.ToString()); + Diagnostics.Debug.WriteLine($"[{caller}] " + e.ToString()); LogContents.Add(new LogEntry(e.ToString(), LogEntry.SeverityLevel.Error)); } diff --git a/src/UniGetUI.Core.Tools/DWMThreadHelper.cs b/src/UniGetUI.Core.Tools/DWMThreadHelper.cs new file mode 100644 index 000000000..57f54f3d6 --- /dev/null +++ b/src/UniGetUI.Core.Tools/DWMThreadHelper.cs @@ -0,0 +1,181 @@ +using System.Diagnostics; +using System.Runtime.InteropServices; +using UniGetUI.Core.Logging; +using UniGetUI.Core.SettingsEngine; + +namespace UniGetUI; + +public class DWMThreadHelper +{ + [DllImport("ntdll.dll")] + private static extern int NtQueryInformationThread(IntPtr ThreadHandle, int ThreadInformationClass, + ref IntPtr ThreadInformation, int ThreadInformationLength, out int ReturnLength); + + [DllImport("kernel32.dll")] + private static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId); + + [DllImport("kernel32.dll")] + private static extern uint SuspendThread(IntPtr hThread); + + [DllImport("kernel32.dll")] + private static extern uint ResumeThread(IntPtr hThread); + + [DllImport("kernel32.dll")] + private static extern bool CloseHandle(IntPtr hObject); + + [Flags] + private enum ThreadAccess : uint + { + QUERY_INFORMATION = 0x0040, + SUSPEND_RESUME = 0x0002 + } + + private const int ThreadQuerySetWin32StartAddress = 9; + private static bool DWM_IsSuspended; + private static bool XAML_IsSuspended; + + private static int? DWMThreadId; + private static IntPtr? DWMThreadAdress; + private static int? XAMLThreadId; + private static IntPtr? XAMLThreadAdress; + + public static void ChangeState_DWM(bool suspend) + { + if (Settings.Get("DisableDMWThreadOptimizations")) return; + + if (DWM_IsSuspended && suspend) + { + Logger.Debug("DWM Thread was already suspended"); return; + } + else if (!DWM_IsSuspended && !suspend) + { + Logger.Debug("DWM Thread was already running"); return; + } + + DWMThreadAdress ??= GetTargetFunctionAddress("dwmcorei.dll", 0x54F70); + if (DWMThreadAdress is null) + { + Logger.Error("Failed to resolve thread start adress."); return; + } + + ChangeState(suspend, (IntPtr)DWMThreadAdress, ref DWM_IsSuspended, ref DWMThreadId, "DWM"); + } + + public static void ChangeState_XAML(bool suspend) + { + if (Settings.Get("DisableDMWThreadOptimizations")) return; + + if (XAML_IsSuspended && suspend) + { + Logger.Debug("XAML Thread was already suspended"); return; + } + else if (!XAML_IsSuspended && !suspend) + { + Logger.Debug("XAML Thread was already running"); return; + } + + // The reported offset on ProcessExplorer seems to be missing 0x6280 somehow + // 0x54F70 + 0x6280 = 0x5B1F0 + XAMLThreadAdress ??= GetTargetFunctionAddress("Microsoft.UI.Xaml.dll", 0x5B1F0); + if (XAMLThreadAdress is null) + { + Logger.Error("Failed to resolve thread start adress."); return; + } + + ChangeState(suspend, (IntPtr)XAMLThreadAdress, ref XAML_IsSuspended, ref XAMLThreadId, "XAML"); + } + + private static IntPtr GetThreadStartAdress(int threadId) + { + IntPtr hThread = OpenThread(ThreadAccess.QUERY_INFORMATION | ThreadAccess.SUSPEND_RESUME, false, (uint)threadId); + if (hThread == IntPtr.Zero) return IntPtr.Zero; + + IntPtr adress = 0x00; + int status = NtQueryInformationThread(hThread, ThreadQuerySetWin32StartAddress, ref adress, Marshal.SizeOf(typeof(IntPtr)), out _); + if(status != 0) Logger.Warn($"NtQueryInformationThread returned non-zero status code 0x{(uint)status:X}"); + CloseHandle(hThread); + return adress; + } + + private static void ChangeState(bool suspend, IntPtr threadAdress, ref bool IsSuspended, ref int? threadId, + string loggerName, bool canRetry = true) + { + if (threadId is null) + { + foreach (ProcessThread thread in Process.GetCurrentProcess().Threads) + { + if (GetThreadStartAdress(thread.Id) == threadAdress) + { + threadId = thread.Id; + Logger.Info($"Thread with Id {threadId} was assigned as {loggerName} thread"); + } + } + } + + if (threadId is null) + { + Logger.Error($"No thread matching {loggerName} with start adress {threadAdress:X} was found"); + return; + } + + IntPtr hThread = OpenThread(ThreadAccess.QUERY_INFORMATION | ThreadAccess.SUSPEND_RESUME, false, (uint)threadId); + if (hThread == IntPtr.Zero) + { // When the thread cannot be opened + if (canRetry) + { + threadId = null; // On first try, reset argument threadId so it does get loaded again. + ChangeState(suspend, threadAdress, ref IsSuspended, ref threadId, loggerName, false); + return; + } + // The threadId was already reloaded + Logger.Warn($"Thread with id={threadId} and assigned as {loggerName} exists but could not be opened!"); + return; + } + + + if (suspend) + { + uint res = SuspendThread(hThread); + if (res == 0) + { + IsSuspended = true; + Logger.Info($"{loggerName} Thread was suspended successfully"); + CloseHandle(hThread); + return; + } + else + { + Logger.Warn($"Could not suspend {loggerName} Thread with NTSTATUS = 0x{res:X}"); + } + } + else + { + int res = (int)ResumeThread(hThread); + if (res >= 0) + { + IsSuspended = false; + Logger.Info($"{loggerName} Thread was resumed successfully"); + CloseHandle(hThread); + return; + } + else + { + Logger.Error($"Could not resume {loggerName} Thread with NTSTATUS = 0x{res:X}"); + } + } + + CloseHandle(hThread); + } + + private static IntPtr? GetTargetFunctionAddress(string moduleName, int offset) + { + foreach (ProcessModule module in Process.GetCurrentProcess().Modules) + { + if (module.ModuleName.Equals(moduleName, StringComparison.OrdinalIgnoreCase)) + { + return module.BaseAddress + offset; + } + } + return null; + } +} diff --git a/src/UniGetUI/App.xaml.cs b/src/UniGetUI/App.xaml.cs index a8bd907be..cd1cd9e12 100644 --- a/src/UniGetUI/App.xaml.cs +++ b/src/UniGetUI/App.xaml.cs @@ -2,6 +2,7 @@ using System.Text.RegularExpressions; using Windows.ApplicationModel.Activation; using CommunityToolkit.WinUI.Helpers; +using H.NotifyIcon; using Microsoft.UI.Dispatching; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; @@ -349,14 +350,9 @@ private async Task CheckForMissingDependencies() await MainWindow.HandleMissingDependencies(missing_deps); } - protected override async void OnLaunched(LaunchActivatedEventArgs args) + protected override void OnLaunched(LaunchActivatedEventArgs args) { - if (!CoreData.IsDaemon) - { - await ShowMainWindowFromLaunchAsync(); - } - - CoreData.IsDaemon = false; + MainWindow?.Activate(); } public async Task ShowMainWindowFromRedirectAsync(AppActivationArguments rawArgs) @@ -390,22 +386,16 @@ public async Task ShowMainWindowFromRedirectAsync(AppActivationArguments rawArgs MainWindow.DispatcherQueue.TryEnqueue(MainWindow.Activate); } - public async Task ShowMainWindowFromLaunchAsync() - { - while (MainWindow is null) - await Task.Delay(100); - - MainWindow.DispatcherQueue.TryEnqueue(MainWindow.Activate); - } - public async void DisposeAndQuit(int outputCode = 0) { - Logger.Warn("Quitting..."); + Logger.Warn("Quitting UniGetUI"); + DWMThreadHelper.ChangeState_DWM(false); + DWMThreadHelper.ChangeState_XAML(false); MainWindow?.Close(); BackgroundApi?.Stop(); Exit(); - await Task.Delay(100); - Environment.Exit(outputCode); + // await Task.Delay(100); + // Environment.Exit(outputCode); } public void KillAndRestart() diff --git a/src/UniGetUI/MainWindow.xaml.cs b/src/UniGetUI/MainWindow.xaml.cs index 58c22ad4a..7ff5c8c2e 100644 --- a/src/UniGetUI/MainWindow.xaml.cs +++ b/src/UniGetUI/MainWindow.xaml.cs @@ -19,6 +19,7 @@ using UniGetUI.PackageEngine.Classes.Manager.Classes; using UniGetUI.PackageEngine.Interfaces; using Windows.ApplicationModel.DataTransfer; +using H.NotifyIcon.EfficiencyMode; using Microsoft.Windows.AppNotifications; using UniGetUI.Core.Classes; using UniGetUI.Interface.Enums; @@ -119,6 +120,37 @@ public MainWindow() if (!Settings.Get("TransferredOldSettings")) TransferOldSettingsFormats(); + + Activated += (_, e) => + { + if (e.WindowActivationState is WindowActivationState.CodeActivated + or WindowActivationState.PointerActivated) + { + DWMThreadHelper.ChangeState_DWM(false); + DWMThreadHelper.ChangeState_XAML(false); + } + }; + + if (CoreData.IsDaemon) + { + try + { + TrayIcon?.ForceCreate(true); + } + catch (Exception ex) + { + TrayIcon?.ForceCreate(false); + Logger.Error("Could not create taskbar tray with efficiency mode enabled"); + Logger.Error(ex); + } + DWMThreadHelper.ChangeState_DWM(true); + DWMThreadHelper.ChangeState_XAML(true); + CoreData.IsDaemon = false; + } + else + { + Activate(); + } } private void AddToSubtitle(string line) @@ -212,21 +244,21 @@ public async void HandleClosingEvent(AppWindow sender, AppWindowClosingEventArgs if (!Settings.Get("DisableSystemTray") || AutoUpdater.UpdateReadyToBeInstalled) { args.Cancel = true; + DWMThreadHelper.ChangeState_DWM(true); + DWMThreadHelper.ChangeState_XAML(true); + try { - this.Hide(enableEfficiencyMode: true); - MainContentFrame.Content = null; - AppWindow.Hide(); + EfficiencyModeUtilities.SetEfficiencyMode(true); } catch (Exception ex) { - // Somewhere, Sometimes, MS Window Efficiency mode just crashes - Logger.Debug("Windows efficiency mode API crashed, but this was expected"); - Logger.Debug(ex); - this.Hide(enableEfficiencyMode: false); - MainContentFrame.Content = null; - AppWindow.Hide(); + Logger.Error("Could not disable efficiency mode"); + Logger.Error(ex); } + + MainContentFrame.Content = null; + AppWindow.Hide(); } else { @@ -375,6 +407,19 @@ public void ProcessCommandLineParameters() public new void Activate() { + try + { + EfficiencyModeUtilities.SetEfficiencyMode(false); + } + catch (Exception ex) + { + Logger.Error("Could not disable efficiency mode"); + Logger.Error(ex); + } + + DWMThreadHelper.ChangeState_DWM(false); + DWMThreadHelper.ChangeState_XAML(false); + if (!HasLoadedLastGeometry) { RestoreGeometry(); diff --git a/src/UniGetUI/Pages/DialogPages/DesktopShortcuts.xaml.cs b/src/UniGetUI/Pages/DialogPages/DesktopShortcuts.xaml.cs index ea1934272..a079937fe 100644 --- a/src/UniGetUI/Pages/DialogPages/DesktopShortcuts.xaml.cs +++ b/src/UniGetUI/Pages/DialogPages/DesktopShortcuts.xaml.cs @@ -111,7 +111,7 @@ private void YesResetButton_Click(object sender, RoutedEventArgs e) ConfirmResetFlyout.Hide(); } - private void NoResetButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e) + private void NoResetButton_Click(object sender, RoutedEventArgs e) { ConfirmResetFlyout.Hide(); } diff --git a/src/UniGetUI/Pages/MainView.xaml.cs b/src/UniGetUI/Pages/MainView.xaml.cs index 9a31f545c..93a6ac112 100644 --- a/src/UniGetUI/Pages/MainView.xaml.cs +++ b/src/UniGetUI/Pages/MainView.xaml.cs @@ -260,9 +260,11 @@ public void NavigateTo(PageType NewPage_t) OldPage_t = CurrentPage_t; CurrentPage_t = NewPage_t; + (oldPage as IEnterLeaveListener)?.OnLeave(); + (NewPage as AbstractPackagesPage)?.FocusPackageList(); + (NewPage as AbstractPackagesPage)?.FilterPackages(); (NewPage as IEnterLeaveListener)?.OnEnter(); - (oldPage as IEnterLeaveListener)?.OnLeave(); } private void ReleaseNotesMenu_Click(object sender, RoutedEventArgs e) diff --git a/src/UniGetUI/Pages/SettingsPage.xaml b/src/UniGetUI/Pages/SettingsPage.xaml index ef3b322c1..0cdaa64b3 100644 --- a/src/UniGetUI/Pages/SettingsPage.xaml +++ b/src/UniGetUI/Pages/SettingsPage.xaml @@ -332,6 +332,11 @@ UnderText="Beta features and other options that shouldn't be touched" Icon="experimental"> +