Windows关机/注销时WPF应用发送HTTP Post请求失败问题求助
解决Windows关机/注销时HTTP请求无法完成的问题
你的核心问题在于系统不会等待SessionEnding事件中的异步操作完成——因为你的事件处理方法是async void,WPF和Windows的事件模型不会追踪这类异步方法的完成状态,会直接继续执行关机流程,导致你的HTTP请求还没完成就被终止了,而且这种进程终止不会触发常规的HttpRequestException,所以你的catch块根本没机会执行。
下面是具体的解决方案,结合你的代码修改:
关键思路
- 先阻止系统默认的关机流程:通过设置
ev.Cancel = true,给异步请求留出执行时间; - 完成HTTP请求后手动触发关机/注销:请求成功或失败后,再调用系统API执行对应的操作;
- 添加超时保护:避免HTTP请求卡住导致系统无法正常关机。
修改后的代码
App.xaml.cs
using System; using System.IO; using System.Net.Http; using System.Text; using System.Threading.Tasks; using System.Windows; using Newtonsoft.Json; using System.Runtime.InteropServices; namespace WpfApp1 { public partial class App : Application { static readonly HttpClient client = new(); // 引入Windows API用于触发关机/注销 [DllImport("user32.dll", SetLastError = true)] private static extern bool ExitWindowsEx(uint uFlags, uint dwReason); // 定义关机/注销的常量参数 private const uint EWX_LOGOFF = 0x00; private const uint EWX_SHUTDOWN = 0x01; private const uint EWX_REBOOT = 0x02; private const uint EWX_FORCEIFHUNG = 0x10; private async void App_SessionEnding(object sender, SessionEndingCancelEventArgs ev) { // 第一步:阻止系统默认的关机/注销流程 ev.Cancel = true; var evento = new Evento(Environment.UserName, Environment.MachineName, DateTime.Now, "Logout", "some-data"); var json = JsonConvert.SerializeObject(evento); var data = new StringContent(json, Encoding.UTF8, "application/json"); try { // 设置超时,避免请求无限等待(根据你的需求调整时长) client.Timeout = TimeSpan.FromSeconds(10); HttpResponseMessage response = await client.PostAsync("some-url", data); response.EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); await File.WriteAllTextAsync("WriteLines.txt", $"Success: {responseBody}"); } catch (Exception e) { await File.WriteAllTextAsync("WriteLines.txt", $"Error: {e.Message}\nSession Reason: {ev.ReasonSessionEnding}"); Console.WriteLine($"Exception Caught: {e.Message}"); } finally { // 第二步:请求完成后,手动触发对应的关机/注销操作 TriggerSystemAction(ev.ReasonSessionEnding); } } private void TriggerSystemAction(SessionEndReasons reason) { uint flags = EWX_FORCEIFHUNG; // 强制结束无响应的程序,避免卡住 switch (reason) { case SessionEndReasons.Logoff: flags |= EWX_LOGOFF; break; case SessionEndReasons.Shutdown: case SessionEndReasons.SystemShutdown: flags |= EWX_SHUTDOWN; break; case SessionEndReasons.Restart: flags |= EWX_REBOOT; break; } ExitWindowsEx(flags, 0); } } // 确保你的Evento类定义正确 public class Evento { public string UserName { get; set; } public string MachineName { get; set; } public DateTime Timestamp { get; set; } public string Action { get; set; } public string Data { get; set; } public Evento(string userName, string machineName, DateTime timestamp, string action, string data) { UserName = userName; MachineName = machineName; Timestamp = timestamp; Action = action; Data = data; } } }
关键细节说明
ev.Cancel = true:这是核心,只有阻止了系统的默认流程,你的异步请求才有机会完整执行;HttpClient.Timeout:必须设置超时,否则如果请求卡住(比如目标服务器无响应),系统会一直停留在等待状态,无法关机;ExitWindowsExAPI:相比WPF自带的Application.Shutdown,这个系统API能更准确地触发系统级的注销、关机等操作,符合你的场景;finally块:确保无论请求成功还是失败,都会执行后续的关机操作,避免系统卡住。
测试建议
为了避免频繁关机调试,你可以暂时把client.PostAsync换成一个延迟操作,比如:
await Task.Delay(5000); // 模拟5秒的请求耗时
这样能验证你的代码逻辑是否正确,确认系统会等待异步操作完成后再执行关机。
内容的提问来源于stack exchange,提问作者Sebastian Ciocarlan
相关产品推荐
相关产品推荐

