日韩欧美国产精品免费一二-日韩欧美国产精品亚洲二区-日韩欧美国产精品专区-日韩欧美国产另-日韩欧美国产免费看-日韩欧美国产免费看清风阁

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發文檔 其他文檔  
 
網站管理員

.NET 阻止Windows關機以及阻止失敗的一些原因

freeflydom
2025年4月14日 8:32 本文熱度 174

本文主要介紹Windows在關閉時,如何正確、可靠的阻止系統關機以及關機前執行相應業務

Windows關機,默認會給應用幾s的關閉時間,但有一些場景需要在關機/重啟前執行更長時間的業務邏輯,確保下次開機時數據的一致性以及可靠性。我司目前業務也用到關機阻止,但這塊之前并未梳理清楚,依賴BUG編程,導致后續維護項目時又會導致關機這塊出現新問題。

統一整理,以下是實現這一需求的幾種方法,

1. Windows消息Hook勾子

 1     public MainWindow()
 2     {
 3         InitializeComponent();
 4         Loaded += OnLoaded;
 5     }
 6 
 7     private void OnLoaded(object sender, RoutedEventArgs e)
 8     {
 9         Loaded -= OnLoaded;
10         var source = PresentationSource.FromVisual(this) as HwndSource;
11         source?.AddHook(WndProc);
12     }
13     const int WM_QUERYENDSESSION = 0x11;
14     const int WM_ENDSESSION = 0x16;
15     private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
16     {
17         if (msg == WM_QUERYENDSESSION)
18         {
19             var handle = new WindowInteropHelper(this).Handle;
20             ShutdownBlockReasonCreate(handle, "應用保存數據中,請等待...");
21             // 可以在這里執行你的業務邏輯
22             bool executeSuccess = ExecuteShutdownWork();
23             // 返回0表示阻止關機,1表示允許關機
24             handled = true;
25             return executeSuccess ? (IntPtr)1 : (IntPtr)0;
26         }
27         return (IntPtr)1;
28     }
29 
30     private bool ExecuteShutdownWork()
31     {
32         Thread.Sleep(TimeSpan.FromSeconds(20));
33         //測試,默認返回操作失敗
34         return false;
35     }
36 
37     [DllImport("user32.dll")]
38     private static extern bool ShutdownBlockReasonCreate(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)] string reason);
39     [DllImport("user32.dll")]
40     private static extern bool ShutdownBlockReasonDestroy(IntPtr hWnd);

通過Hook循環windows窗口消息,WndProc接收到WM_QUERYENDSESSION時表示有關機調用,詳細的可以查看官網文檔:(WinUser.h) WM_QUERYENDSESSION消息 - Win32 apps | Microsoft Learn

WndProc返回1表示業務正常,0表示取消、阻止關機。這里我們默認操作失敗,阻止關機

拿到每個應用的關機確認結果,再廣播WM_ENDSESSION、執行真正的關閉

拿到窗口句柄,可以通過ShutdownBlockReasonCreate設置阻止關機原因,ShutdownBlockReasonDestroy清理關機阻止原因,詳見:ShutdownBlockReasonCreate 函數 (winuser.h) - Win32 apps | Microsoft Learn

阻止進行中的效果:

上面demo運行20s之后,系統會退出關機狀態、返回登錄界面

2.Win32系統事件SystemEvents

 1     public partial class App : Application
 2     {
 3         public App()
 4         {
 5             SystemEvents.SessionEnding += SystemEvents_SessionEnding;
 6             Application.Current.Exit += Current_Exit;
 7         }
 8         private void Current_Exit(object sender, ExitEventArgs e)
 9         {
10             SystemEvents.SessionEnding -= SystemEvents_SessionEnding;
11         }
12         private void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e)
13         {
14             if (e.Reason == SessionEndReasons.SystemShutdown)
15             {
16                 var handle = new WindowInteropHelper(Application.Current.MainWindow).Handle;
17                 ShutdownBlockReasonDestroy(handle);
18                 ShutdownBlockReasonCreate(handle, "應用保存數據中,請等待...");
19 
20                 var executeSuccess = ExecuteShutdownWork();
21                 e.Cancel = !executeSuccess;
22             }
23         }
24         private bool ExecuteShutdownWork()
25         {
26             //Test
27             Thread.Sleep(TimeSpan.FromSeconds(200));
28             return false;
29             try
30             {
31                 // XXX
32                 return true;
33             }
34             catch (Exception e)
35             {
36                 return false;
37             }
38         }
39 
40         [DllImport("user32.dll")]
41         private static extern bool ShutdownBlockReasonCreate(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)] string reason);
42         [DllImport("user32.dll")]
43         private static extern bool ShutdownBlockReasonDestroy(IntPtr hWnd);
44     }

也可以監聽SessionEndReasons.SystemShutdown關機事件。實際上也是基于消息機制,但封裝了細節、提供更高級抽象

這里e.Cancel,false表示不取消用戶請求、不關機,true表示取消用戶請求、阻止關機

因為需要設置關機阻止原因,SystemEvents.SessionEnding也是要依賴窗口的。當然,因為依賴窗口會導致勾子失敗,下面我們會聊

阻止關機失敗的一些原因

以上倆種方式,均可以實現阻止系統關機以及關機前執行相應業務。但Hook勾子也可能失效,不能正常執行完你的業務邏輯

1. 關機勾子只支持UI線程,不支持異步調用

第一種,SessionEnding事件被修改為了async void

第二種,業務內部調用了異步方法,通過.Result、.Wait()期望等待完成。但其實內部并沒有完全添加.ConfigureAwait,這也會導致關機阻止失敗。

我司業務就遇到了第二個問題,在消息循環WndProc之后,添加了上報后臺日志。為了滿足勾子只支持同步調用,日志模塊就使用了.Result轉為同步方法:

對于這類限定UI線程同步執行場景,我的解決辦法是,減少邏輯、去除發送后臺日志。

另外,如果下面業務真的需要使用async,需要業務上下游所有調用鏈條均添加.ConfigureAwait,不切換上下文。否則系統不會等待、往下直接關機了

2. 窗口Hide,導致勾子失效

一些窗口啟動后,需要立即Hide窗口:

1     public MainWindow()
2     {
3         InitializeComponent();
4         //在構造中設置Hide或者Show之后立即設置Hide,均會導致關機阻止失敗
5         Hide();
6     }

在構造中Hide或者Show之后立即Hide,均會導致關機阻止失敗。錯誤demo,可見 kybs0/ShutdownPreventDemo

我的理解是,ShutdownBlockReasonCreate 函數需要窗口處于活動狀態,窗口Hide之后肯定是不行了。那如何解決呢?

在Loaded之后去設置窗口隱藏就行了:

 1     public MainWindow()
 2     {
 3         InitializeComponent();
 4         Loaded += MainWindow_Loaded;
 5     }
 6     private void MainWindow_Loaded(object sender, RoutedEventArgs e)
 7     {
 8         Loaded -= MainWindow_Loaded;
 9         //如果啟動后需要立即隱藏窗口,請放在Loaded之后
10         Hide();
11     }

設置Visibility也沒問題 Visibility=Visibility.Collapsed; 驗證ok

第二,因為根源還是設置關機阻止Resion,那是否可以提前去設置呢?不要等窗口Hide之后再去設置或者關機時去設置...

所以,完全可以在主窗口內提前設置:

 1     public partial class MainWindow : Window
 2     {
 3         public MainWindow()
 4         {
 5             InitializeComponent();
 6             Loaded += MainWindow_Loaded;
 7         }
 8         private void MainWindow_Loaded(object sender, RoutedEventArgs e)
 9         {
10             Loaded -= MainWindow_Loaded;
11             var currentMainWindow = Application.Current.MainWindow;
12             var handle = new WindowInteropHelper(currentMainWindow).Handle;
13             ShutdownBlockReasonDestroy(handle);
14             ShutdownBlockReasonCreate(handle, "應用保存數據中,請等待...");
15 
16             //窗口Hide,并不影響上面的ShutdownBlockReasonDestroy
17             Hide();
18         }
19         [DllImport("user32.dll")]
20         private static extern bool ShutdownBlockReasonCreate(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)] string reason);
21         [DllImport("user32.dll")]
22         private static extern bool ShutdownBlockReasonDestroy(IntPtr hWnd);
23     }

上面代碼也注釋了,設置完關機原因、再去Hide。關機事件觸發后,是能正常保障阻止機制的。驗證ok

這里也推薦大家使用SystemEvents.SessionEnding方式,可以不受MainWindow窗口的勾子入口限定

3.360安全衛士、QQ電腦管家等優化軟件,可能會優化此類關機阻止機制

這些安全軟件關機時可能直接強殺,用來提升關機/重啟速度。個人是不建議使用這些安全軟件的,都是流氓。。。

關機阻止超時的情況及建議

關機重啟是有時間限制的,我試了下,在設置關機阻止原因情況下,應用最多只能持續60秒左右。

超過60s后系統取消關機、回登錄界面,然后當前阻止的進程會在執行完Hook后自動關閉(其它進程不會關閉)

如果Hook勾子內我們執行的業務太過耗時,可能不一定能執行完。建議只執行更少、必須的業務

另外,關機時應用關閉是有順序的。如果想提高一點應用關機時應用能應對的時間,略微提升關機前業務執行的成功率,可以對進程添加關閉優先級:

1         public MainWindow()
2         {
3             InitializeComponent();
4 
5             // 在應用程序啟動時調用
6             SetProcessShutdownParameters(0x4FF, 0);
7         }
8         [DllImport("kernel32.dll")]
9         static extern bool SetProcessShutdownParameters(uint dwLevel, uint dwFlags);

0x100表示最低優先級,確保你的程序最先被關閉

0x4FF表示最高優先級,確保你的程序最后被關閉

詳細的參考文檔: SetProcessShutdownParameters 函數 (processthreadsapi.h) - Win32 apps | Microsoft Learn

 

 以上demo,可從倉庫獲取 ShutdownPreventDemo: 阻止關機demo

轉自https://www.cnblogs.com/kybs0/p/18822799?


該文章在 2025/4/14 8:32:26 編輯過
關鍵字查詢
相關文章
正在查詢...
點晴ERP是一款針對中小制造業的專業生產管理軟件系統,系統成熟度和易用性得到了國內大量中小企業的青睞。
點晴PMS碼頭管理系統主要針對港口碼頭集裝箱與散貨日常運作、調度、堆場、車隊、財務費用、相關報表等業務管理,結合碼頭的業務特點,圍繞調度、堆場作業而開發的。集技術的先進性、管理的有效性于一體,是物流碼頭及其他港口類企業的高效ERP管理信息系統。
點晴WMS倉儲管理系統提供了貨物產品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質期管理,貨位管理,庫位管理,生產管理,WMS管理系統,標簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務都免費,不限功能、不限時間、不限用戶的免費OA協同辦公管理系統。
Copyright 2010-2025 ClickSun All Rights Reserved

主站蜘蛛池模板: 亚洲欧美综合视频 | 国产精品欧 | 日韩欧美中文综合 | 日韩一级 | 午夜性刺激 | 91短视频污下载 | 99视频精品国 | 亚洲欧美日韩另类精品一区二 | 亚洲精品欧美日韩一区二区 | 欧美孕妇xxxx | 午夜欧美 | 一区视频观看 | 丰满妇女强 | 色偷偷亚洲女人天堂观看欧 | 搡老女人露脸 | 亚洲国产99在线精品一区 | 精品日产卡一卡二卡国色天香 | 亚洲中国中文字幕免费 | 免费看美女脱了全身衣服直播 | 欧美性爱精品一级大片 | 欧美区精品系列在线观看不卡 | 青青综合 | 中文字幕巨大乳在线看 | 在线国产精品看片 | 日本一本二本三区免费 | 最新国产在线视频 | 欧美日韩亚洲国产一区二区三区 | 精品va在线观看 | 日本天堂天v在线播放 | 日韩在线免费 | 亚洲欧美日韩一区高清中文字幕 | 一级a做一级a做片性高清视频 | 无人视频在线观看免费播放影院 | 99爱这里只有精品 | 在线精品91青草国产在线观看 | 国产精品视频一区二区三区 | 中文国产欧美影视 | 欧美三级在线播放 | 日本亲子乱子伦xxxx50路 | 日韩精品一线二线三线优势 | 日本成人大片一区二区 |