HKCU 跟著執行帳號走、UserChoice 擋掉副檔名關聯、DISM 預設關聯只影響新帳號。三道牆,以及各自能繞到哪裡。
發布於 2026-09-05・約 5 分鐘
寫新電腦設定指令碼的時候,我一直以為「用系統管理員執行」是萬能的。實際上有一整類設定,權限再高也改不到你想改的那個人身上。
三道牆,機制各不相同。
螢幕保護、工作列靠左、檔案總管顯示副檔名,這些全部寫在 HKCU:
HKCU
Set-AsRegValue -Path "HKCU:\Control Panel\Desktop" -Name "ScreenSaveTimeOut" -Value "1200" -Type String
HKCU 是「目前使用者」的意思,而「目前使用者」指的是跑這支指令碼的那個帳號,不是那台電腦前面坐的人。
實務上會出事的情境很具體:使用者的帳號不是管理員,所以 UAC 提權時你輸入了 IT 專用的管理員帳密。那一刻 PowerShell 是以 IT 帳號在跑,所有 HKCU 設定都寫進 IT 帳號的設定檔。使用者登入之後看到的還是原樣。
指令碼會全部回報成功。它確實成功了,只是對象不是你要的那個人。
繞法只有一個:用使用者自己的帳號登入之後再執行。 如果那個帳號不是管理員,提權時輸入管理員密碼,仍然會落在管理員身上,所以正確做法是把使用者暫時加進 Administrators,跑完再拿掉。
第二個選擇是改寫 HKEY_USERS 底下對應 SID 的分支,但那要求對方已經登入過(設定檔存在),而且沒登入時得手動掛載 NTUSER.DAT,交機流程裡不值得。
HKEY_USERS
NTUSER.DAT
我最後的處理是在文件跟執行前警告寫死這句話,並且在驗證階段標明:
注意:HKCU 類設定只驗得到「執行驗證的那個帳號」的狀態,跟套用時是同一個限制。
驗證跟套用如果是不同帳號跑的,那份檢查表就是假的。
裝完 7-Zip 想把常見壓縮檔關聯過去。直覺寫法:
Set-Item -Path "HKCU:\SOFTWARE\Classes\.zip" -Value "7-Zip.zip"
.7z、.rar 這些會成功。.zip 不會。寫進去了,重開檔案總管,還是用 Windows 內建的壓縮資料夾開。
.7z
.rar
.zip
原因是 Windows 對使用者「自己選過」的預設程式另外記了一筆:
HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.zip\UserChoice
這個機碼裡有一個雜湊值,是拿副檔名、ProgId、使用者 SID 跟一個時間戳算出來的。系統會驗這個雜湊,對不上就當作沒設定,繼續用它自己的判斷。演算法沒有公開,所以你沒辦法自己算一個合法的出來。.zip 之所以中招,是因為 Windows 出廠就替它建了 UserChoice。
UserChoice
這件事我的處理是不要跟它打。三個判斷條件,任何一個不成立就跳過:
$progId = "7-Zip" + $ext # 7-Zip 安裝時就會註冊這些 ProgId,沒註冊的就不要硬指過去 if (-not (Test-Path "HKLM:\SOFTWARE\Classes\$progId") -and -not (Test-Path "HKCU:\SOFTWARE\Classes\$progId")) { continue } # 使用者已經自己選過預設程式的副檔名,跳過不要蓋掉他的選擇 $userChoice = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FileExts\$ext\UserChoice" if (Test-Path $userChoice) { continue }
跳過已經有 UserChoice 的副檔名,順便解決了另一個問題:使用者自己選過的預設程式不會被你的指令碼默默蓋掉。
清單裡我放了 .7z .rar .tar .gz .bz2 .xz .tgz .cab .iso,就是不放 .zip。要改 .zip 只有兩條路:下面第三道牆講的 DISM 做法(只影響新帳號),或請使用者自己在 7-Zip 的「選項 → 系統」按一下。
.7z .rar .tar .gz .bz2 .xz .tgz .cab .iso
寫這段時還踩到一個 PowerShell 的小坑:註冊機碼的「預設值」要用 Set-Item,用 New-ItemProperty -Name "(Default)" 會建出一個名字剛好叫 (Default) 的普通值,不是真正的預設值。看起來一模一樣,但系統讀不到。
Set-Item
New-ItemProperty -Name "(Default)"
(Default)
設定預設瀏覽器有一個官方管道:
dism /online /Import-DefaultAppAssociations:C:\assoc.xml
XML 長這樣:
<?xml version="1.0" encoding="UTF-8"?> <DefaultAssociations> <Association Identifier=".htm" ProgId="ChromeHTML" ApplicationName="Chrome" /> <Association Identifier=".html" ProgId="ChromeHTML" ApplicationName="Chrome" /> <Association Identifier="http" ProgId="ChromeHTML" ApplicationName="Chrome" /> <Association Identifier="https" ProgId="ChromeHTML" ApplicationName="Chrome" /> </DefaultAssociations>
(可以用 dism /online /Export-DefaultAppAssociations:C:\assoc.xml 從一台設定好的機器匯出。)
dism /online /Export-DefaultAppAssociations:C:\assoc.xml
它的名字就說明了限制:Default Associations,預設值。它決定「一個還沒有任何偏好設定的新使用者第一次登入時要用什麼」,對已經登入過的帳號沒有任何作用。
這不是 bug,是 Windows 10 之後刻意保護使用者預設程式的設計,跟上一節的 UserChoice 是同一套機制的兩面。沒有官方支援的方式可以用指令碼改掉現有帳號的預設瀏覽器。
所以我在驗證表上把這一項直接標成「無法驗證」:
return @{ State = "無法驗證"; Detail = "這個機制只對之後新建立的使用者生效,現有帳號驗不出來" }
交機情境下其實還算堪用:出廠系統重灌完、使用者帳號還沒建,這時候匯入是有效的。順序反過來就沒救了。
遇到「設定寫進去了但沒生效」,先問三個問題:
HKLM
這三個問題我花了不少時間才問對,因為這類失敗全部都不報錯。指令碼回報成功、登錄值查得到、行為就是不對。
標籤:Windows、登錄檔、HKCU、部署、PowerShell