部署指令碼回報成功之後,回頭讀系統的實際狀態再比對一次。五種判定狀態怎麼分,以及 powercfg 輸出解析錯會得到的鬼數字。
發布於 2026-09-05・約 6 分鐘
新電腦設定指令碼跑完,畫面上一整排綠色的「成功」。交機。三天後使用者說螢幕保護沒有啟動。
去看記錄檔,那一項確實成功了。登錄值也寫進去了。就是沒生效。
從那次之後,我在流程最後補了一段跟執行完全分開的驗證:回頭讀系統現在的實際狀態,跟設定檔對照,印一張交機檢查表。它只讀不寫,隨時可以單獨再跑一次。
執行階段的「成功」意思很窄:那幾行程式碼沒有拋例外。它不保證:
.msi
reg import
HKCU
這四種我都遇過。共通點是失敗不會叫。
驗證跟 dry run 剛好相反:dry run 是「等一下會做什麼」,驗證是「做完了沒有」。
一開始我只做通過跟未通過,很快就發現不夠用。最後定成五種:
中間那三種才是重點。
「需重開機」不能算通過也不能算未套用。 工作列釘選就是這種:版面設定檔產生了、群組原則設好了,但要登出再登入才看得到。算通過是騙自己,算未套用會害交機的人回頭去找哪裡沒設好。
「無法驗證」要誠實掛出來。 有幾類東西真的沒有可靠的檢查方式:
PC-{SERIAL}
把它們硬塞進「通過」,整張表就沒有意義了。列成「無法驗證」加一句原因,看的人知道要自己確認什麼。
最後結束碼設成「未套用的項目數」,可以直接串進別的流程。
電源逾時的驗證我卡了最久。設定用的是這個:
powercfg /change monitor-timeout-ac 20
讀回來要用另一組完全不同的別名:
powercfg /query SCHEME_CURRENT SUB_VIDEO VIDEOIDLE
/change 用 monitor-timeout-ac 這種名字,/query 用 SUB_VIDEO VIDEOIDLE 這種名字,兩套要自己對照:
/change
monitor-timeout-ac
/query
SUB_VIDEO VIDEOIDLE
monitor-timeout-*
SUB_VIDEO
VIDEOIDLE
standby-timeout-*
SUB_SLEEP
STANDBYIDLE
disk-timeout-*
SUB_DISK
DISKIDLE
hibernate-timeout-*
HIBERNATEIDLE
輸出長這樣:
最小可能設定: 0x00000000 最大可能設定: 0xffffffff 可能設定增量: 0x00000001 可能設定單位: 秒 目前的 AC 電源設定索引: 0x00000708 目前的 DC 電源設定索引: 0x00000708
我的第一版是「抓所有 0x 開頭的值,取第 0 個當 AC、第 1 個當 DC」。抓到的是最小與最大可能設定,換算出來就是「永不」跟 71582788 分鐘(0xffffffff 秒除以 60)。檢查表上一整排「未套用」,設定其實好好的。
0x
那些說明文字會跟著系統語言變,不能拿來比對。AC 跟 DC 這兩個字在各語言版本都維持英文,拿它們定位最穩:
AC
DC
foreach ($l in $out) { if ($l -match ('\b' + $line.ToUpperInvariant() + '\b.*?0x([0-9a-fA-F]{8})')) { $hex = $matches[1] break } }
比不到再退回「輸出裡最後兩個 0x 值」,那兩行永遠是 AC 跟 DC。最後再擋一次:
# 0xffffffff 不是合法的逾時值,代表讀到的是「最大可能設定」那一行,判定為讀取失敗 if ($seconds -ge 4294967295) { return $null }
寧可回報「讀不到」,也不要回報一個錯的數字。錯的數字會被當成真的。
檢查電源逾時的時候,要照使用者在那個項目實際填的 powercfg 名稱去查,不能照項目 ID 猜。這幾個項目的參數是可以改的,有人把「AC 關螢幕」那一格改成去控制硬碟逾時,猜出來的就對不上。
powercfg
信任的網站那一項也是同一個道理。我原本數「ZoneMap 底下有幾個網域」,但 Windows 本來就可能有別的來源塞進去的項目,數字對不代表你匯的那些有進去。改成把 .reg 檔裡真正列到的機碼撈出來,一個一個回去查:
.reg
foreach ($l in $lines) { if ($l -match '^\s*\[(HKEY_[^\]]+)\]\s*$') { $wanted += ($matches[1] -replace '^HKEY_CURRENT_USER', 'HKCU:' ` -replace '^HKEY_LOCAL_MACHINE', 'HKLM:') } } $missing = @($wanted | Where-Object { -not (Test-Path $_ -ErrorAction SilentlyContinue) })
順帶一提,reg export 出來的檔案是 UTF-16LE。用 Get-Content 讀不指定編碼會得到一堆亂碼,然後正則一個都比不到,結果是「0 個機碼」而不是報錯。[System.IO.File]::ReadAllLines() 會依 BOM 自己判斷。
reg export
Get-Content
[System.IO.File]::ReadAllLines()
HKCU 類的設定(螢幕保護、工作列、檔案總管),驗證只驗得到「執行驗證的那個帳號」。這跟套用時是同一個限制。
如果套用跟驗證是不同帳號跑的,那張檢查表就是假的。這句話我直接印在表格底下,不指望人記得。
標籤:Windows、PowerShell、部署、驗證、powercfg