更新紀錄(僅初版,2026年08月22日 發布)
- 初次發布
引用本文(DOI(已登錄存檔): 10.5281/zenodo.22176757)
以下 DOI 指向先前登錄的存檔,內容可能與目前正文不同。引用目前正文時,請使用本頁網址。
Go Komura(2026)。〈從睡眠恢復就壞掉的應用程式 ── 電源事件的機制,以及耐得住恢復的業務應用程式寫法〉。小村軟體有限公司。 https://comcomponent.com/zh-TW/blog/windows-sleep-resume-power-events/
- DOI(已登錄存檔)
- 10.5281/zenodo.22176757
- DOI(上次登錄版本)
- 10.5281/zenodo.22176758
闔上筆電,隔天早上打開,業務應用程式滿是錯誤。設備監控應用程式只有午休之後才漏掉資料。輸出到 Excel 的常駐工具,偶爾會因連線錯誤停住。遇到這類症狀,第一個該懷疑的就是跨過睡眠的行為。
過去的業務應用程式中,有些是在「PC 一直開著」這個前提下寫成的。但筆電已成為業務主力的現在,閒置幾分鐘就會睡眠的環境不能再忽略。而且在支援 Modern Standby(新式待命)的機器上,睡眠的機制本身也和過去不同。
本文寫給在 Windows 上開發業務應用程式與設備控制軟體的開發者。依作業系統送來的通知 → 恢復後會壞掉的東西 → 復原的設計 → 現場的調查這個順序,根據一次資訊整理。
1. 先講結論
設計的重心不是「一定要在睡眠前收拾乾淨」,而是「不論何時被停下來,恢復後都能重建」。要掌握的有三點。
- 事前通知不等於保證你能把工作做完。睡眠無法拒絕,
PBT_APMSUSPEND的寬限大約只有 2 秒。緊急暫停時連通知本身都不會來。即使在 Modern Standby 上,也不能假設桌面應用程式在睡眠期間還會繼續執行。123 - 設計時要假設連線、控制代碼與時間的連續性都無法跨過恢復而保留。除了恢復通知之外,也要讓通訊錯誤走進同一套重連處理,並重新檢視計時器的排程與經過時間的差值。
- 抑制睡眠與為恢復做好準備,是兩件不同的對策。必要的工作區間用
SetThreadExecutionState或電源要求,結束後務必清除。不過使用者明確的睡眠操作等情況擋不住,所以這不能當成省掉復原處理的理由。45
也可以依目的,直接從下列章節讀起。
| 想知道的事 | 該讀的章節 |
|---|---|
| 睡眠前後會收到哪些通知 | 第 2 章:電源事件的流程 |
| Modern Standby 有什麼不同 | 第 3 章:系統的運作與應用程式的暫停 |
| 為什麼會出現連線錯誤或時刻偏差 | 第 4 章:經典的症狀 |
| 重連、時間管理與睡眠抑制該怎麼實作 | 第 5 章:耐得住恢復的設計 |
| 使用者回報該怎麼釐清原因 | 第 6 章:powercfg 與事件記錄 |
2. 睡眠前後會發生什麼 ── 電源事件的流程
作業系統會以 WM_POWERBROADCAST 訊息把電源狀態的變化通知應用程式。2 先掌握暫停轉換前後會用到的三個事件。與 Modern Standby 低耗電閒置的差異,在第 3 章補充。
| 事件 | 意義 |
|---|---|
| PBT_APMSUSPEND | 即將進入睡眠(最後的準備機會) |
| PBT_APMRESUMEAUTOMATIC | 已恢復(恢復時一定會到) |
| PBT_APMRESUMESUSPEND | 使用者操作造成的恢復(這個是有條件的) |
sequenceDiagram
accTitle: 睡眠與恢復的通知流程
accDescr: 睡眠前一刻會收到約 2 秒寬限的 PBT_APMSUSPEND,恢復時一定會收到 PBT_APMRESUMEAUTOMATIC,只有使用者操作造成的恢復才會接著收到 PBT_APMRESUMESUSPEND
participant OS as OS
participant A as 應用程式
OS->>A: PBT_APMSUSPEND(約 2 秒寬限)
A->>A: 儲存狀態、關閉連線
Note over OS: 睡眠(程式碼不會執行)
OS->>A: PBT_APMRESUMEAUTOMATIC(恢復時會到)
A->>A: 重連、重建狀態
OS->>A: PBT_APMRESUMESUSPEND(僅使用者恢復時)
A->>A: 畫面更新等面向使用者的處理
圖 1: 通知只有「事前一句話,恢復後一句或兩句」。重建的主角是恢復側的處理。
睡眠前的通知是「來得及就做準備」的機會
PBT_APMSUSPEND 是進入睡眠前一刻的通知。可以關閉檔案、儲存狀態做準備,但有下列兩個限制。
| 限制 | 對設計的影響 |
|---|---|
| 每個應用程式的處理寬限大約 2 秒 | 超過時間,系統就不等應用程式而繼續往下走1 |
| 緊急暫停時沒有事前通知 | 電池電量危急這類情況,會在沒有準備的狀態下停止2 |
因此,「收到這個通知之後一定能存完」的設計並不成立。事前通知只在來得及的範圍內做準備,復原的主體放在恢復側。
flowchart TB
accTitle: 一般睡眠與緊急暫停的差異
accDescr: 一般睡眠會在事前送到約 2 秒準備時間的 PBT_APMSUSPEND,但電池電量危急這類緊急暫停沒有事前通知就停止,因此依賴事前通知的設計不成立
n2["一般的睡眠"] --> pre["PBT_APMSUSPEND(約 2 秒寬限)"]
pre --> s1["準備之後停止"]
e2["緊急暫停(電池快沒電)"] --> s2["沒有事前通知就停止"]
s2 -.-> l2["假設通知一定會來的設計不成立"]
圖 2: 緊急暫停沒有預告。所以準備是「趕得上就賺到」,主體放在恢復側。
恢復通知要把機械性的復原與面向使用者的處理分開
從暫停恢復時,首先會收到 PBT_APMRESUMEAUTOMATIC。此外,若是以電源按鈕或按鍵恢復,或是恢復後偵測到使用者在場,接著還會收到 PBT_APMRESUMESUSPEND。67
另一方面,經由網路的遠端喚醒,或為了維護而進行的無人值守恢復,只會收到 PBT_APMRESUMEAUTOMATIC。重建連線這類必要的復原處理放在前者,畫面更新或重新登入要求這類面向使用者的動作放在後者。6
flowchart TB
accTitle: 恢復兩階段的處理分工
accDescr: 恢復時會到的 PBT_APMRESUMEAUTOMATIC 放重連這類機械性的重建,只有使用者操作才會到的 PBT_APMRESUMESUSPEND 放畫面更新或重新登入要求這類面向使用者的處理
ra["PBT_APMRESUMEAUTOMATIC(恢復時)"] --> m["機械性的重建"]
rs["PBT_APMRESUMESUSPEND(使用者恢復時)"] --> u["面向使用者的處理"]
m -.-> m1["重連、重新開啟控制代碼"]
u -.-> u1["畫面更新、重新登入要求"]
圖 3: 無人值守恢復不會送到後者,把必要的重建放在後者就會漏掉。
沒有視窗的應用程式也能收到通知
沒有視窗的服務或主控台應用程式,可以用 RegisterSuspendResumeNotification 並指定 DEVICE_NOTIFY_CALLBACK,以回呼收到相同的通知。8
另外,WM_POWERBROADCAST 無法區分低耗電狀態是睡眠還是休眠。7 應用程式這一側,要把它當成「停了,又回來了」這個共通事件來設計復原處理。
3. Modern Standby ── 「睡眠」的意義變了
系統會動,和應用程式能動,是兩回事
傳統的 S3 睡眠是讓整個系統停下來的模型。相對地,Modern Standby 是螢幕熄滅之後系統仍會間歇運作、接近智慧型手機的模型。
不過,這不代表連一般的桌面應用程式也會繼續執行。在進入睡眠的第一個階段,它們就會被 Desktop Activity Moderator(DAM)暫停。3
| 方式 | 系統的運作 | 桌面應用程式的前提 |
|---|---|---|
| 傳統的 S3 睡眠 | 整個系統停止 | 睡眠期間程式碼不會執行 |
| Modern Standby | 為了維持網路、接收通知等目的而間歇運作 | 被 DAM 暫停,一般的程式碼不會執行 |
能享受間歇運作好處的,是有支援這套機制的元件。就業務應用程式的設計而言,兩者的結論都是「睡眠期間自己的程式碼不會執行」。3
flowchart TB
accTitle: 傳統睡眠與 Modern Standby 的差異
accDescr: 傳統 S3 睡眠讓整個系統停止,Modern Standby 下螢幕熄滅後系統仍會間歇運作。但桌面應用程式會被 DAM 暫停,因此兩者的應用程式程式碼都不會執行
s3["傳統的 S3 睡眠:整體停止"] --> conc["應用程式的程式碼不會執行"]
ms["Modern Standby:系統間歇運作"] --> dam["桌面應用程式被 DAM 暫停"]
dam --> conc
圖 4: 模型變了,但對桌面應用程式來說結論一樣是「睡眠期間動不了」。
不要只把恢復通知當成復原的條件
Modern Standby 進出低耗電閒置的時機,有時並不與傳統的暫停轉換一致。也可能通知沒送到,連線卻已經壞了。請把恢復通知視為讓復原提早的輔助,把「從通訊錯誤的偵測就能重連」的路徑放在中心。具體的結構在第 5 章說明。
另外,由於進入低耗電狀態是分階段的,斷線與停止的時機不像 S3 那樣明確。使用者也難以分辨究竟是「只是螢幕熄了」還是「已經睡眠了」,所以詢問症狀時要確認有沒有闔上蓋子、閒置了幾分鐘。
4. 什麼會壞掉 ── 經典的症狀
恢復後的錯誤,分成連線、裝置控制代碼、時間的連續性三類來整理會比較清楚。共用資源還要確認重新驗證與重新建立網路所花的時間。
TCP 連線要等到傳送或接收才會發現斷了
睡眠期間,連線的對方以及 NAT、防火牆會把這一側的沉默當成逾時,把連線丟棄。但這一側的通訊端並不知情,所以要等到恢復後傳送或接收,才會第一次失敗。
也有一直停在等待接收、遲遲不會出錯的情況。這正是需要用 keepalive 確認連線死活的理由。資料庫連線與 WebSocket 也可以用同樣的架構來思考。
序列埠與 USB 裝置要把控制代碼重新開啟
USB 裝置在恢復時,有時看起來像是被拔掉又插回去一次,原本開著的控制代碼就開始傳回錯誤。這正是設備控制應用程式「只有午休之後才通訊錯誤」的典型模式。
不要以「控制代碼可以一直用下去」為前提,而要做成能重新開啟裝置的結構。重連的設計,在序列通訊的文章裡也有談到。
週期處理、經過時間與定時處理要分開重新檢視
與時間有關的問題分成下列三種。
| 處理 | 跨過睡眠會發生的事 | 對策 |
|---|---|---|
| 「每 10 秒輪詢一次」這類週期處理 | 睡眠期間會停止。恢復後何時觸發,依 API 與執行環境而異 | 恢復時重建排程 |
| 使用與上次時刻差值的計算 | 差值突然變成「8 小時份」,平均值或逾時判斷就會壞掉 | 對異常大的差值加上防護 |
| 「每晚 2 點執行」這類定時處理 | 那個時刻 PC 若在睡眠就不會執行 | 必要時使用工作排程器的喚醒功能 |
尤其是週期處理,過期的部分可能在恢復後立刻觸發一次,也可能到下一個週期為止什麼都不發生。重要的是不要把「沒能執行的那些次要怎麼處理」交給計時器的隱含行為。
flowchart TB
accTitle: 時間連續性壞掉的三種形狀
accDescr: 週期處理在睡眠期間會停止而且恢復後的觸發依 API 而異,所以恢復時要重建排程;與上次時刻的差值在恢復後會變得巨大,所以要加上防護;定時處理在機器睡著時不會執行,所以要考慮工作排程器的喚醒功能
t1["週期處理:睡眠期間停止"] -.-> g1["恢復時重建排程"]
t2["經過時間的差值:變得巨大"] -.-> g2["對異常的差值加上防護"]
t3["定時處理:睡著而未執行"] -.-> g3["用喚醒設定把機器叫醒"]
圖 5: 計時器與時刻的處理要以「時間會跳」為前提來寫。三種形狀各有對應的對策類型。
flowchart TB
accTitle: 跨過睡眠就會壞掉的三件事
accDescr: 跨過睡眠後,TCP 連線會被對方逾時丟棄,USB 裝置的控制代碼被當成重新連接而失效,依經過時間的處理會觀測到巨大的時間跳躍。分別用重連、重新開啟與差值防護來重建
sleep["睡眠區間"] --> tcp["TCP 連線:對方已丟棄"]
sleep --> usb["USB 裝置:控制代碼失效"]
sleep --> time["經過時間:巨大的跳躍"]
tcp -.-> r1["偵測錯誤並重連"]
usb -.-> r2["把裝置重新開啟"]
time -.-> r3["對異常的差值加上防護"]
圖 6: 會壞的東西分成「連線」「控制代碼」「時間連續性」三個系統,各自都有既定的重建類型。
網路磁碟機與 VPN 在剛恢復時也有等待時間
網路磁碟機與 VPN 在恢復後有時需要重新驗證、重新建立。因此剛恢復後的數秒到數十秒之間,會有一段存取會失敗的時間帶。
不要在恢復的瞬間一次全部重試,而要設計成稍等一下,再分階段重試。
5. 耐得住恢復的應用程式怎麼寫
原則是即使連線與控制代碼無法跨過睡眠存活,也要做成隨時能重建的結構。重連、時間的處理,以及必要區間的睡眠抑制,都要分別設計。
讓恢復通知與通訊錯誤匯入同一套重連處理
在最上層視窗的 WM_POWERBROADCAST 收到 PBT_APMRESUMEAUTOMATIC 時,把手上持有的連線丟棄並重新建立。不過,不能只依賴恢復通知。因為通知可能漏掉,也可能在通知送到之前就已經發生通訊。
一定要同時備好「偵測到通訊錯誤就重連」這條路徑,並把恢復通知定位成讓那個處理提早開始的觸發。下面的 C# 範例是從恢復通知要求重連的部分。通訊錯誤那一側也要匯入同一套重連處理。
// C#: 讓恢復通知與通訊錯誤都匯入同一套重連處理
protected override void WndProc(ref Message m)
{
const int WM_POWERBROADCAST = 0x0218;
const int PBT_APMRESUMEAUTOMATIC = 0x0012;
if (m.Msg == WM_POWERBROADCAST && (int)m.WParam == PBT_APMRESUMEAUTOMATIC)
{
_connectionManager.RequestReconnect(); // 冪等的重連要求
}
base.WndProc(ref m);
}
重連要把「冪等、退避、keepalive」組合起來
重連處理要備齊下列三點。
| 要素 | 作用 |
|---|---|
| 冪等的重連 | 讓它被要求幾次都能安全重建 |
| 帶指數退避的重試 | 失敗就拉長到下次重試的間隔 |
| 穩態時的 keepalive | 確認連線是否還活著,及早偵測斷線 |
這三件一套不只對睡眠恢復有用,對網路瞬斷或裝置重新啟動也一樣管用。
flowchart TB
accTitle: 耐得住恢復的重連設計
accDescr: 恢復通知、通訊錯誤與 keepalive 失敗都匯入同一套冪等的重連處理,失敗時以指數退避重試
e1["恢復通知(PBT_APMRESUMEAUTOMATIC)"] --> r["冪等的重連處理"]
e2["偵測到通訊錯誤"] --> r
e3["keepalive 失敗"] --> r
r --> ok{"成功?"}
ok -->|"是"| run["回到正常運作"]
ok -->|"否"| back["指數退避後重試"]
back --> r
圖 7: 把重連集中成一條冪等的處理,從恢復通知、錯誤偵測或 keepalive 都走進同一條路。
不要把時間跳過的區間直接混進計算
在使用「距離上次經過多久」的處理裡,偵測到異常大的差值就加上讓該區間失效的防護。目的是不要把它混進平均,也不要當成一般的逾時來處理。
量測跨過恢復的時間時,要區分睡眠期間仍會前進的時刻(實際時間)與實際花在處理上的時間。週期處理的排程重建,以及定時處理沒有執行時的處理方式,也請依第 4 章的分類重新檢視。
不能中斷的處理區間要明確抑制睡眠
資料移轉、與裝置連續通訊這類不能被睡眠打斷的處理期間,要用 SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED)。也想維持螢幕亮著時,再加上 ES_DISPLAY_REQUIRED。74
另一個手段是以 PowerCreateRequest 與 PowerSetRequest 發出的電源要求。因為可以附上原因字串,用 powercfg /requests 就能知道「誰、為什麼擋住睡眠」。以維運時能確認原因這一點來說,這個手段比較貼心。5
| 手段 | 管理的單位與注意事項 |
|---|---|
SetThreadExecutionState |
以執行緒為單位。清除要從當初設定的同一條執行緒進行 |
電源要求(PowerCreateRequest + PowerSetRequest) |
以控制代碼管理。async/await 這類會換執行緒的處理就用這個 |
不過,設定了抑制,並不代表任何中斷都能擋下來。下列限制要與復原處理分開來確認。
| 限制 | 必要的因應 |
|---|---|
| 抑制的對象是無操作造成的自動睡眠 | 要為闔上蓋子、從開始功能表選睡眠這類明確操作做準備,保留重連處理 |
| Modern Standby 機器以電池運作時,睡眠逾時後過一段時間,電源要求也會被切斷 | 不能中斷的處理要靠交流電源或維運面來保證5 |
| 漏掉清除,會變成 PC 睡不著的原因 | 處理結束後務必清除 |
flowchart TB
accTitle: 抑制睡眠的兩種手段
accDescr: 無論用方便的 SetThreadExecutionState,還是能附上原因字串、管理員可用 powercfg 看到的電源要求 API,處理結束時都務必清除
need["不想被睡眠打斷的處理區間"] --> a["SetThreadExecutionState"]
need --> b["電源要求(PowerSetRequest)"]
a -.-> a1["方便、只要指定旗標"]
b -.-> b1["附上原因、powercfg 看得到"]
a --> off["處理結束時務必清除"]
b --> off
圖 8: 兩種手段都把「做完就清除」當成絕對條件。能讓原因可見的電源要求對維運比較貼心。
若持續運作是需求,就重新檢視放在哪裡與怎麼維運
服務或無視窗的應用程式,一樣能用 RegisterSuspendResumeNotification 的 DEVICE_NOTIFY_CALLBACK 收到通知。8
不過,如果真的需要持續運作,就要重新檢視「常駐在會睡眠的用戶端 PC」這件事本身。把處理搬到伺服器側,或搬到不睡眠維運的電腦上,才是根本的對策。
6. 怎麼調查 ── powercfg 與事件記錄
收到使用者回報時,先確認「錯誤發生前一刻 PC 是不是在睡眠」。問清楚有沒有闔上蓋子、閒置了幾分鐘之後,再用符合症狀的工具。
| 想查的事 | 工具 | 要確認的內容 |
|---|---|---|
| 睡不著的原因 | powercfg /requests |
正在發出電源要求的處理程序與驅動程式。也一併確認 SetThreadExecutionState 是否忘了清除 |
| 自己醒來的原因 | powercfg /lastwake、powercfg /waketimers |
最近一次的喚醒原因,以及目前預約喚醒的計時器 |
| Modern Standby 的品質 | powercfg /sleepstudy |
每個睡眠區間的耗電與活動9 |
| 睡眠與恢復的時間軸 | 系統事件記錄的 Kernel-Power |
進入睡眠與恢復的紀錄 |
把事件記錄和應用程式的記錄對起來,就能客觀確認「錯誤發生前一刻有沒有恢復」。不要停在懷疑睡眠,而要對齊發生時刻來釐清原因。
flowchart TB
accTitle: 電源問題的症狀與調查指令的對應
accDescr: 睡不著的症狀用 powercfg /requests 查是誰發出電源要求,自己醒來的症狀用 /lastwake 與 /waketimers 查喚醒原因,確認時間軸則用事件記錄裡的 Kernel-Power
s1["睡不著"] --> c1["powercfg /requests"]
s2["自己醒來"] --> c2["powercfg /lastwake、/waketimers"]
s3["想確認時間軸"] --> c3["事件記錄裡的 Kernel-Power"]
c1 -.-> note["忘了清除的睡眠抑制也會現形"]
圖 9: 症狀與調查指令的對應分成三個系統。先確認「前一刻有沒有睡眠」再分開使用。
7. 總結
睡眠的對策,光是收到通知並不算完成。要把事前準備來不及、恢復通知沒送到這兩件事都算進去,像下面這樣分工。
| 設計與調查的對象 | 要掌握的重點 |
|---|---|
| 睡眠前 | 無法拒絕。用 PBT_APMSUSPEND 約 2 秒的寬限做準備,但緊急時通知不會來 |
| 恢復通知 | 用 PBT_APMRESUMEAUTOMATIC 做必要的復原,用 PBT_APMRESUMESUSPEND 做面向使用者的處理。不要只依賴通知 |
| 連線與控制代碼 | 以「由通訊錯誤觸發的重連」為中心,組合冪等性、指數退避與 keepalive |
| 時間 | 對異常的經過時間差值加上防護,並重建週期處理的排程。定時處理在睡著時不會執行 |
| 睡眠抑制 | 只在必要的區間設定,結束後清除。對明確的睡眠操作等情況,仍要保留復原的準備 |
| 原因調查 | 詢問前一刻是否睡過,並把 powercfg 與 Kernel-Power 的紀錄和應用程式的記錄比對 |
從應用程式的角度看,睡眠是「時間毫無預告地跳躍、與周邊的連線被切斷,然後又回來」的事件。能不能把它當成日常的一部分而非異常狀況織進設計,決定了筆電時代業務應用程式的穩定度。
相關文章
- 序列通訊應用程式的陷阱 - 從重連到日誌設計
- 從應用程式看 Windows 關機 ── 正確扛住結束通知、重新啟動與斷電
- Windows 的效率模式是什麼 - Windows 11 的綠色葉子圖示代表什麼,以及如何關閉
- Windows 上應優先採用事件等待而非 Sleep(1) 的理由
- 「沒有回應」的真面目 ── Windows 如何判定應用程式當住,以及不卡住的設計
相關諮詢領域
小村軟體有限公司承接「從睡眠恢復後通訊就壞掉」「與裝置的連線在午休之後就斷」這類問題的原因調查、為既有應用程式補上重連邏輯與電源事件處理,以及以筆電維運為前提的業務應用程式與設備控制軟體的設計審查。
參考連結
-
Microsoft Learn, PBT_APMSUSPEND event. 關於這是電腦即將進入暫停狀態前一刻送到的事件;關於應用程式應完成儲存資料所需的處理;以及系統允許處理這個通知的時間大約是 2 秒,超過還繼續處理的應用程式可能被中斷。 ↩ ↩2
-
Microsoft Learn, System Power Management Events. 關於系統會事先廣播睡眠等運作模式的變更;關於閒置睡眠前會通知 PBT_APMSUSPEND,讓應用程式能做關閉檔案、儲存資料的準備;關於緊急暫停(電池電量危急等)不會進行事前通知;關於處理這個訊息時每個應用程式最多約 2 秒,逾時後就會被切斷;以及恢復時所有應用程式都會收到通知。 ↩ ↩2 ↩3
-
Microsoft Learn, Prepare software for modern standby. 關於轉換到 Modern Standby 的第一個階段,Desktop Activity Moderator(DAM)會暫停桌面應用程式;以及系統其後會分階段進入低耗電階段與韌性階段,只有獲准的元件才會間歇運作。 ↩ ↩2 ↩3
-
Microsoft Learn, SetThreadExecutionState function (winbase.h). 關於用 ES_SYSTEM_REQUIRED 或 ES_DISPLAY_REQUIRED 可以抑制系統的閒置睡眠與螢幕關閉;以及用 ES_CONTINUOUS 宣告持續抑制,用完之後單獨呼叫 ES_CONTINUOUS 來清除的用法。 ↩ ↩2
-
Microsoft Learn, PowerSetRequest function (winbase.h). 關於可以對用 PowerCreateRequest 建立的電源要求物件設定維持系統、維持螢幕等要求類型;關於可以附上診斷用的原因字串;以及尚未解除的電源要求可以用 powercfg /requests 列舉。 ↩ ↩2 ↩3
-
Microsoft Learn, PBT_APMRESUMESUSPEND event. 關於使用者操作造成的恢復,或其後偵測到使用者輸入時,會接在 PBT_APMRESUMEAUTOMATIC 之後送出;關於遠端喚醒這類外部原因造成的恢復只會送出 PBT_APMRESUMEAUTOMATIC;以及應用程式應重新開啟睡眠時關閉的檔案並準備接受使用者輸入。 ↩ ↩2
-
Microsoft Learn, WM_POWERBROADCAST message. 關於恢復時一定會送出 PBT_APMRESUMEAUTOMATIC,使用者輸入造成的恢復還會再送出 PBT_APMRESUMESUSPEND;關於這個訊息無法區分低耗電狀態的種類;關於電源狀態轉換的細節會記錄在系統事件記錄;以及要防止系統進入低耗電狀態就呼叫 SetThreadExecutionState。 ↩ ↩2 ↩3
-
Microsoft Learn, RegisterSuspendResumeNotification function (winuser.h). 關於這是註冊接收暫停與恢復通知的 API,除了把訊息送到視窗控制代碼之外,指定 DEVICE_NOTIFY_CALLBACK 還能讓沒有視窗的應用程式或服務以回呼收到通知。 ↩ ↩2
-
Microsoft Learn, Modern standby SleepStudy. 關於用 powercfg /sleepstudy 產生的報告,可以確認每個 Modern Standby 區間的耗電、活動與喚醒原因(電源按鈕、使用者輸入、喚醒計時器等)。 ↩
相關文章
共用相同標籤的最新文章。能以相近的主題延伸理解。
DllMain 與載入器鎖定 ── 「DLL 初始化時什麼都別做」真正的理由
為什麼不能在 DllMain 裡呼叫 LoadLibrary 或與其他執行緒同步?本文依據一次資料,從序列化所有 DLL 通知的載入器鎖定機制,一路說明到死結成立的典型情境、延遲初始化等正確設計,以及無回應的調查步驟。
「沒有回應」的真面目 ── Windows 如何判定應用程式卡住,以及不卡住的設計
Windows 的「沒有回應」,是視窗 5 秒沒有取出訊息時由作業系統判定、並換成幽靈視窗的機制。本文從判定的內部運作,講到卡住的經典原因、把重工作移出 UI 執行緒的設計,以及無回應的調查程序。
解讀 Windows 錯誤碼 ── Win32、HRESULT、NTSTATUS 三層結構
出現 0x80004005 時,先分解再搜尋。本文整理 Win32 錯誤、HRESULT、NTSTATUS 的三層結構、0x8007xxxx 是包起來的 Win32 錯誤這個最重要模式,以及用 err.exe 與 PowerShell 查詢的方法。
委外・委託開發 Windows 應用程式前該整理的事項
在委外・委託開發 Windows 應用程式之前,整理既有軟體改版、設備整合、COM/ActiveX、發布與更新、維護等應留意的重點。
快速啟動的真面目 ── Windows 的「關機」為什麼和重新啟動不一樣
Windows 的「關機」預設會變成混合關機,核心與驅動程式被保存到休眠檔,並在下次開機時還原。本文說明為什麼有些問題只有重新啟動才會好、對運作時間・更新・Wake on LAN 的影響、確認方法以及是否停用的判斷。
相關主題
與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。
Windows 技術主題
彙整 KomuraSoft LLC 關於 Windows 開發、故障調查與既有資產活用文章的主題中心。
故障調查 & 長期運行故障
整理間歇性故障、通訊診斷、長期運行當機、失敗路徑測試基礎的主題頁面。
與本主題相關的服務
本文連結到以下服務頁面,歡迎從最接近的入口查看。
Windows 應用程式開發
支援包含常駐處理、設備連動、運作日誌與可維護結構的 Windows 桌面應用程式。
故障調查 & 根本原因分析
調查難以重現的故障、長時間執行後的問題、記憶體洩漏、通訊停滯等棘手的正式環境問題。
常見問題
整理諮詢這個主題時常見的問題。
- 應用程式能事先得知睡眠並拒絕嗎?
- 在現行 Windows 上可以收到通知,但不能拒絕。睡眠前一刻會以 WM_POWERBROADCAST 訊息送出 PBT_APMSUSPEND 事件,此時可以關閉檔案、儲存狀態來準備,但每個應用程式允許的處理時間大約只有 2 秒,超時系統就不再等待而繼續。電池電量危急這類緊急暫停時,事前通知本身也不會來。因此「必須在睡眠前做完」的設計不成立;需要的是無論何時被切斷,恢復時都能重建的設計。真的不想被睡眠打斷的工作區間,要用 SetThreadExecutionState 或電源要求(PowerSetRequest)明確抑制睡眠。
- 要怎麼偵測機器已經恢復?
- 應用程式有視窗就處理 WM_POWERBROADCAST。從暫停恢復時會收到 PBT_APMRESUMEAUTOMATIC;若恢復是使用者操作(電源按鈕或按鍵)造成的,接著還會收到 PBT_APMRESUMESUSPEND。無人值守恢復後立刻再睡,只會送到 PBT_APMRESUMEAUTOMATIC,因此基本分法是:重連這類必要工作放在 PBT_APMRESUMEAUTOMATIC 側,畫面更新這類面向使用者的工作放在 PBT_APMRESUMESUSPEND 側。沒有視窗的服務與主控台應用程式,可用 RegisterSuspendResumeNotification 搭配 DEVICE_NOTIFY_CALLBACK,以回呼收到相同通知。
- 睡眠期間能讓應用程式繼續跑嗎?
- 原則上不能。睡眠期間 CPU 執行本身會停(Modern Standby 機器上,桌面應用程式會被 Desktop Activity Moderator 暫停),應用程式的程式碼不會跑。選擇有兩個。一是只在工作進行中抑制睡眠。用 SetThreadExecutionState 指定 ES_SYSTEM_REQUIRED,或用 PowerCreateRequest/PowerSetRequest 發出電源要求,就能在該區間抑制閒置自動睡眠(可用 powercfg /requests 確認)。這仍擋不住使用者闔上蓋子這類明確的睡眠動作,因此即使正在抑制,仍要為恢復做準備。二是接受睡眠,設計成「恢復後再追上」。夜間批次這類排程工作,也可以用工作排程器的「喚醒電腦以執行這個工作」把 PC 叫醒。真正需要持續運作的工作,應放在伺服器或不睡眠的服務上。
- 為什麼恢復後 TCP 連線和序列埠就不能用了?
- 因為網路介面卡和 USB 裝置在睡眠期間也會進入低耗電狀態。TCP 連線早已被對方或 NAT、防火牆逾時丟棄,恢復後的傳送/接收會傳回錯誤(常常要等到出錯才發現)。USB 轉序列埠這類裝置,恢復時有時會被當成裝置拔除再插入,原本開著的控制代碼就失效。兩者都應假設「控制代碼和連線跨過恢復活不下來」,正解是在恢復通知或通訊錯誤時重建連線的重連邏輯。週期性 keepalive 加上失敗時指數退避重試,是既定做法。
- 要怎麼調查非預期的睡眠或非預期的恢復?
- 第一個工具是 powercfg 指令。「睡不著」方向用 powercfg /requests 列出哪些處理程序和驅動程式發出了擋住睡眠的電源要求。「自己醒來」方向用 powercfg /lastwake 看最近一次喚醒原因,用 powercfg /waketimers 看目前預約要喚醒機器的計時器。Modern Standby 機器上,powercfg /sleepstudy 會產出睡眠期間耗電與活動的報告。睡眠與恢復的歷史也會記在事件記錄(系統記錄的 Kernel-Power 來源),因此能依時間軸確認「何時睡了、何時因何醒來」。