using System.Diagnostics; using System.Globalization; using System.IO; using System.Security.Principal; using System.Xml.Linq; namespace AutoShutdown.Services; internal sealed class WakeService { private const string TaskName = "AutoShutdown Wake"; public void ScheduleWake(DateTime wakeAt) { var taskXmlPath = Path.Combine(Path.GetTempPath(), $"AutoShutdown-Wake-{Guid.NewGuid():N}.xml"); try { File.WriteAllText(taskXmlPath, CreateWakeTaskXml(wakeAt)); RunProcess("schtasks.exe", $"/Create /TN \"{TaskName}\" /XML \"{taskXmlPath}\" /F"); } finally { TryDelete(taskXmlPath); } } public void CancelWake() { RunProcess("schtasks.exe", $"/Delete /TN \"{TaskName}\" /F", ignoreErrors: true); } private static string CreateWakeTaskXml(DateTime wakeAt) { XNamespace ns = "http://schemas.microsoft.com/windows/2004/02/mit/task"; var startBoundary = wakeAt.ToString("yyyy-MM-dd'T'HH:mm:ss", CultureInfo.InvariantCulture); var userId = WindowsIdentity.GetCurrent().User?.Value ?? Environment.UserName; var task = new XDocument( new XElement(ns + "Task", new XAttribute("version", "1.4"), new XElement(ns + "RegistrationInfo", new XElement(ns + "Description", "AutoShutdown wake timer")), new XElement(ns + "Triggers", new XElement(ns + "TimeTrigger", new XElement(ns + "StartBoundary", startBoundary), new XElement(ns + "Enabled", "true"))), new XElement(ns + "Principals", new XElement(ns + "Principal", new XAttribute("id", "Author"), new XElement(ns + "UserId", userId), new XElement(ns + "LogonType", "InteractiveToken"), new XElement(ns + "RunLevel", "LeastPrivilege"))), new XElement(ns + "Settings", new XElement(ns + "MultipleInstancesPolicy", "IgnoreNew"), new XElement(ns + "DisallowStartIfOnBatteries", "false"), new XElement(ns + "StopIfGoingOnBatteries", "false"), new XElement(ns + "AllowHardTerminate", "true"), new XElement(ns + "StartWhenAvailable", "true"), new XElement(ns + "RunOnlyIfNetworkAvailable", "false"), new XElement(ns + "IdleSettings", new XElement(ns + "StopOnIdleEnd", "false"), new XElement(ns + "RestartOnIdle", "false")), new XElement(ns + "AllowStartOnDemand", "true"), new XElement(ns + "Enabled", "true"), new XElement(ns + "Hidden", "true"), new XElement(ns + "RunOnlyIfIdle", "false"), new XElement(ns + "WakeToRun", "true"), new XElement(ns + "ExecutionTimeLimit", "PT1M"), new XElement(ns + "Priority", "7")), new XElement(ns + "Actions", new XAttribute("Context", "Author"), new XElement(ns + "Exec", new XElement(ns + "Command", "cmd.exe"), new XElement(ns + "Arguments", "/c exit"))))); return task.ToString(SaveOptions.DisableFormatting); } private static void RunProcess(string fileName, string arguments, bool ignoreErrors = false) { using var process = Process.Start(new ProcessStartInfo { FileName = fileName, Arguments = arguments, UseShellExecute = false, CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true }); if (process is null) { throw new InvalidOperationException("无法启动 Windows 任务计划程序命令。"); } var output = process.StandardOutput.ReadToEnd(); var error = process.StandardError.ReadToEnd(); process.WaitForExit(); if (!ignoreErrors && process.ExitCode != 0) { var message = string.Join(Environment.NewLine, new[] { error, output }.Where(item => !string.IsNullOrWhiteSpace(item))); throw new InvalidOperationException(string.IsNullOrWhiteSpace(message) ? "创建唤醒计划失败。" : message.Trim()); } } private static void TryDelete(string path) { try { File.Delete(path); } catch { // 临时任务 XML 删除失败不影响计划本身。 } } }