From 6ed153dfb77a71032db8db8b310e579cb54a64bf Mon Sep 17 00:00:00 2001 From: uye <99072975+ABA2396@users.noreply.github.com> Date: Sat, 17 Aug 2024 15:49:02 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20winapi=20=E5=85=B3=E6=9C=BA/=E7=9D=A1?= =?UTF-8?q?=E7=9C=A0/=E4=BC=91=E7=9C=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MAA.sln.DotSettings | 2 + src/MaaWpfGui/Utilities/PowerManagement.cs | 144 ++++++++++++++++++ .../ViewModels/UI/TaskQueueViewModel.cs | 33 +++- src/MaaWpfGui/Views/UI/ErrorView.xaml | 2 +- 4 files changed, 173 insertions(+), 8 deletions(-) create mode 100644 src/MaaWpfGui/Utilities/PowerManagement.cs diff --git a/MAA.sln.DotSettings b/MAA.sln.DotSettings index 1fa37b254d..3e53fc188c 100644 --- a/MAA.sln.DotSettings +++ b/MAA.sln.DotSettings @@ -31,6 +31,7 @@ WSA XYAZ True + True True True True @@ -92,6 +93,7 @@ True True True + True True True True diff --git a/src/MaaWpfGui/Utilities/PowerManagement.cs b/src/MaaWpfGui/Utilities/PowerManagement.cs new file mode 100644 index 0000000000..fa0fe60972 --- /dev/null +++ b/src/MaaWpfGui/Utilities/PowerManagement.cs @@ -0,0 +1,144 @@ +// +// MaaWpfGui - A part of the MaaCoreArknights project +// Copyright (C) 2021 MistEO and Contributors +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License v3.0 only as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY +// + +using System; +using System.Runtime.InteropServices; +using Serilog; + +namespace MaaWpfGui.Utilities +{ + public class PowerManagement + { + private static readonly ILogger _logger = Log.ForContext(); + + [DllImport("user32.dll", SetLastError = true)] + private static extern bool ExitWindowsEx(uint uFlags, uint dwReason); + + [DllImport("advapi32.dll", SetLastError = true)] + private static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, bool DisableAllPrivileges, ref TOKEN_PRIVILEGES NewState, uint BufferLengthInBytes, IntPtr PreviousState, IntPtr ReturnLengthInBytes); + + [DllImport("advapi32.dll", SetLastError = true)] + private static extern bool OpenProcessToken(IntPtr ProcessHandle, uint DesiredAccess, out IntPtr TokenHandle); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern IntPtr GetCurrentProcess(); + + [DllImport("advapi32.dll", CharSet = CharSet.Auto)] + private static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, out LUID lpLuid); + + [DllImport("powrprof.dll", SetLastError = true, ExactSpelling = true)] + private static extern bool SetSuspendState(bool hibernate, bool forceCritical, bool disableWakeEvent); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool CloseHandle(IntPtr hObject); + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private struct TOKEN_PRIVILEGES + { + public int PrivilegeCount; + public LUID Luid; + public int Attributes; + } + + [StructLayout(LayoutKind.Sequential)] + private struct LUID + { + public uint LowPart; + public int HighPart; + } + + private const int SE_PRIVILEGE_ENABLED = 0x00000002; + private const string SE_SHUTDOWN_NAME = "SeShutdownPrivilege"; + private const int TOKEN_ADJUST_PRIVILEGES = 0x0020; + private const int TOKEN_QUERY = 0x0008; + private const int EWX_SHUTDOWN = 0x00000001; + private const int EWX_FORCE = 0x00000004; + + public static bool Shutdown() + { + IntPtr hToken = IntPtr.Zero; + TOKEN_PRIVILEGES tkp; + + try + { + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, out hToken)) + { + throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); + } + + if (!LookupPrivilegeValue(null, SE_SHUTDOWN_NAME, out tkp.Luid)) + { + throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); + } + + tkp.PrivilegeCount = 1; + tkp.Attributes = SE_PRIVILEGE_ENABLED; + + if (!AdjustTokenPrivileges(hToken, false, ref tkp, 0, IntPtr.Zero, IntPtr.Zero)) + { + throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); + } + + if (Marshal.GetLastWin32Error() != 0) + { + throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); + } + + if (!ExitWindowsEx(EWX_SHUTDOWN | EWX_FORCE, 0)) + { + throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); + } + + return true; + } + catch (Exception ex) + { + _logger.Error($"Shutdown error: {ex.Message}"); + return false; + } + finally + { + if (hToken != IntPtr.Zero) + { + CloseHandle(hToken); + } + } + } + + public static bool Hibernate() + { + try + { + return SetSuspendState(true, true, true); + } + catch (Exception ex) + { + _logger.Error($"Hibernate error: {ex.Message}"); + return false; + } + } + + public static bool Sleep() + { + try + { + return SetSuspendState(false, true, true); + } + catch (Exception ex) + { + _logger.Error($"Sleep error: {ex.Message}"); + return false; + } + } + } +} diff --git a/src/MaaWpfGui/ViewModels/UI/TaskQueueViewModel.cs b/src/MaaWpfGui/ViewModels/UI/TaskQueueViewModel.cs index c49f0a65ea..d4c95178f9 100644 --- a/src/MaaWpfGui/ViewModels/UI/TaskQueueViewModel.cs +++ b/src/MaaWpfGui/ViewModels/UI/TaskQueueViewModel.cs @@ -197,12 +197,16 @@ namespace MaaWpfGui.ViewModels.UI // 休眠提示 AddLog(LocalizationHelper.GetString("HibernatePrompt"), UiLogColor.Error); + /* // 休眠不能加时间参数,https://github.com/MaaAssistantArknights/MaaAssistantArknights/issues/1133 Process.Start("shutdown.exe", "-h"); + */ + PowerManagement.Hibernate(); } - void DoShutDown() + async void DoShutDown() { + /* Process.Start("shutdown.exe", "-s -t 60"); // 关机询问 @@ -211,6 +215,17 @@ namespace MaaWpfGui.ViewModels.UI { Process.Start("shutdown.exe", "-a"); } + */ + if (await TimerCanceledAsync( + LocalizationHelper.GetString("Shutdown"), + LocalizationHelper.GetString("AboutToShutdown"), + LocalizationHelper.GetString("Cancel"), + 60)) + { + return; + } + + PowerManagement.Shutdown(); } } @@ -432,7 +447,11 @@ namespace MaaWpfGui.ViewModels.UI Instances.MainWindowManager?.Show(); } - if (await TimerCanceledAsync()) + if (await TimerCanceledAsync( + LocalizationHelper.GetString("ForceScheduledStart"), + LocalizationHelper.GetString("ForceScheduledStartTip"), + LocalizationHelper.GetString("Cancel"), + 10)) { return; } @@ -456,13 +475,13 @@ namespace MaaWpfGui.ViewModels.UI LinkStart(); } - private static async Task TimerCanceledAsync() + private static async Task TimerCanceledAsync(string content = "", string tipContent = "", string buttonContent="", int seconds = 10) { - var delay = TimeSpan.FromSeconds(10); + var delay = TimeSpan.FromSeconds(seconds); var dialogUserControl = new Views.UserControl.TextDialogWithTimerUserControl( - LocalizationHelper.GetString("ForceScheduledStart"), - LocalizationHelper.GetString("ForceScheduledStartTip"), - LocalizationHelper.GetString("Cancel"), + content, + tipContent, + buttonContent, delay.TotalMilliseconds); var dialog = HandyControl.Controls.Dialog.Show(dialogUserControl, nameof(Views.UI.RootView)); var canceled = false; diff --git a/src/MaaWpfGui/Views/UI/ErrorView.xaml b/src/MaaWpfGui/Views/UI/ErrorView.xaml index 21e5b9263b..8770401762 100644 --- a/src/MaaWpfGui/Views/UI/ErrorView.xaml +++ b/src/MaaWpfGui/Views/UI/ErrorView.xaml @@ -11,12 +11,12 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:styles="clr-namespace:MaaWpfGui.Styles" x:Name="ErrorViewWindow" - Icon="../../newlogo.ico" Title="{DynamicResource Error}" Width="600" Height="480" MinWidth="400" MinHeight="200" + Icon="../../newlogo.ico" ResizeMode="CanResize" Topmost="True" WindowStartupLocation="CenterScreen"