使用動態更新更新 Windows 安裝媒體
本文說明如何在部署之前取得動態更新套件並套用至現有的 Windows 映像,並包含可用來自動化此程式的 Windows PowerShell 腳本。
大量授權媒體適用於大量授權服務中心的每個 Windows 版本, (VLSC) 和其他相關通道,例如商務用 Windows Update、Windows Server Update Services (WSUS) 和 Visual Studio 訂閱。 您可以使用動態更新,確保 Windows 裝置在就地升級時擁有最新的功能更新套件,同時保留先前可能已安裝的語言套件和功能隨選 (FOD) 。 動態更新也不需要在就地升級程式中安裝個別的品質更新。
動態更新
每當功能更新的安裝開始 (無論是從連線到 Windows Update) 的媒體或環境,動態更新都是第一個步驟之一。 Windows 安裝程式會連絡Microsoft端點以擷取動態更新套件,然後將這些更新套用至您的作系統安裝媒體。 更新套件包含下列類型的更新:
- 匯報 Setup.exe 二進位檔或其他安裝程式用於功能更新的檔案
- 用於 Windows 復原環境的「安全作系統」 (SafeOS) 匯報
- 匯報 至完成功能更新所需的服務堆疊。如需詳細資訊,請參閱維護堆棧更新。
- 最新的累積 (品質) 更新
- 匯報 專為動態更新而由製造商發佈的適用驅動程式
動態更新會藉由重新取得語言套件和功能隨選套件來保留它們。
裝置必須能夠連線到因特網,才能取得動態 匯報。 在某些環境中,這不是取得動態 匯報 的選項。 在裝置上啟動安裝程式之前,您仍然可以取得動態更新套件並將它套用至映像,以進行媒體型功能更新。
取得動態更新套件
您可以從 Microsoft更新類別目錄取得動態更新套件。 在該網站上,使用右上方的搜尋列來尋找特定版本的動態更新套件。 各種動態更新套件可能不會全部出現在單一搜尋的結果中,因此您可能必須使用不同的關鍵詞進行搜尋,以尋找所有更新。 檢查結果的各個部分,確定您已識別出所需的檔案。 下表顯示要在結果中搜尋或尋找的索引鍵值。
Windows Server 2025 動態更新套件
標題 可以區分每個動態套件。 最新的累積更新內嵌服務堆疊。 只有在指定的累積更新需要時,才會發佈服務堆疊。
更新套件 | Title |
---|---|
安全作系統動態更新 | 適用於 Microsoft 伺服器作系統版本 24H2 的 YYYY-MM 安全 OS 動態更新 |
設定動態更新 | Microsoft伺服器作系統 24H2 版的 YYYY-MM 安裝程式動態更新 |
最新的累積更新 | Microsoft伺服器作系統 24H2 版的 YYYY-MM 累積更新 |
服務堆疊動態更新 | Microsoft伺服器作系統 24H2 版的 YYYY-MM 服務堆疊更新 |
Windows Server 版本 23H2 動態更新套件
標題 可以區分每個動態套件。 最新的累積更新內嵌服務堆疊。 只有在指定的累積更新需要時,才會發佈服務堆疊。 Azure Stack HCI 版本 23H2 具有類似的格式。
更新套件 | Title |
---|---|
安全作系統動態更新 | 適用於 Microsoft 伺服器作系統版本 23H2 的 YYYY-MM 安全 OS 動態更新 |
設定動態更新 | Microsoft伺服器作系統 23H2 版的 YYYY-MM 安裝程式動態更新 |
最新的累積更新 | Microsoft伺服器作系統 23H2 版的 YYYY-MM 累積更新 |
服務堆疊動態更新 | Microsoft伺服器作系統 23H2 版的 YYYY-MM 服務堆疊更新 |
Azure Stack HCI 22H2 版動態更新套件
需要標題、 產品和 描述 ,才能區分每個動態套件。 最新的累積更新內嵌服務堆疊。 服務堆疊只有在必要時才會個別發佈,作為指定累積更新的必要條件。
更新套件 | Title | 產品 | 說明 |
---|---|---|---|
安全作系統動態更新 | 適用於 Microsoft 伺服器作系統的 YYYY-MM 動態更新,版本 22H2 | Windows 安全作系統動態更新 | ComponentUpdate |
設定動態更新 | 適用於 Microsoft 伺服器作系統的 YYYY-MM 動態更新,版本 22H2 | Windows 10 和更新版本動態更新 | SetupUpdate |
最新的累積更新 | Microsoft伺服器作系統的YYYY-MM累積更新,版本22H2 | ||
服務堆疊動態更新 | 適用於 Microsoft 伺服器作系統的 YYYY-MM 服務堆疊更新,版本 22H2 |
Windows Server 2022 更新版本動態更新套件
需要標題、 產品和 描述 ,才能區分每個動態套件。 最新的累積更新內嵌服務堆疊。 服務堆疊只有在必要時才會個別發佈,作為指定累積更新的必要條件。
更新套件 | Title | 產品 | 說明 |
---|---|---|---|
安全作系統動態更新 | Microsoft伺服器作系統的 YYYY-MM 動態更新,版本 21H2 | Windows 安全作系統動態更新 | ComponentUpdate |
設定動態更新 | Microsoft伺服器作系統的 YYYY-MM 動態更新,版本 21H2 | Windows 10 和更新版本動態更新 | SetupUpdate |
最新的累積更新 | Microsoft伺服器作系統的 YYYY-MM 累積更新版本 21H2 | ||
服務堆疊動態更新 | 適用於 Microsoft 伺服器作系統的 YYYY-MM 服務堆疊更新,版本 21H2 |
Windows 11 版本 22H2 和更新版本的動態更新套件
標題 可以區分每個動態套件。 最新的累積更新內嵌服務堆疊。 只有在指定的累積更新需要時,才會發佈服務堆疊。 下列標題適用於 Windows 11 版本 22H2。 Windows 11 23H2 版和 24H2 版格式類似:
更新套件 | Title |
---|---|
安全作系統動態更新 | 適用於 Windows 11 版本 22H2 的 YYYY-MM 安全 OS 動態更新 |
設定動態更新 | YYYY-MM 設定 Windows 11 22H2 版的動態更新 |
最新的累積更新 | Windows 11 22H2 版的 YYYY-MM 累積更新 |
服務堆疊動態更新 | Windows 11 22H2 版的YYYY-MM服務堆疊更新 |
Windows 11 版本 21H2 動態更新套件
需要標題、 產品和 描述 ,才能區分每個動態套件。 最新的累積更新內嵌服務堆疊。 服務堆疊只有在必要時才會個別發佈,作為指定累積更新的必要條件。
更新套件 | Title | 產品 | 說明 |
---|---|---|---|
安全作系統動態更新 | 適用於 Windows 11的YYYY-MM動態更新 | Windows 安全作系統動態更新 | ComponentUpdate |
設定動態更新 | 適用於 Windows 11的YYYY-MM動態更新 | Windows 10 和更新版本動態更新 | SetupUpdate |
最新的累積更新 | Windows 11的YYYY-MM累積更新 | ||
服務堆疊動態更新 | Windows 11 21H2 版的YYYY-MM服務堆疊更新 |
Windows 10 版本 22H2 動態更新套件
需要標題、 產品和 描述 ,才能區分每個動態套件。 最新的累積更新內嵌服務堆疊。 服務堆疊只有在必要時才會個別發佈,作為指定累積更新的必要條件。
更新套件 | Title | 產品 | 說明 |
---|---|---|---|
安全作系統動態更新 | YYYY-MM Windows 10 版本 22H2 的動態更新 | Windows 安全作系統動態更新 | ComponentUpdate |
設定動態更新 | YYYY-MM Windows 10 版本 22H2 的動態更新 | Windows 10 和更新版本動態更新 | SetupUpdate |
最新的累積更新 | Windows 10 22H2 版的 YYYY-MM 累積更新 | ||
服務堆疊動態更新 | Windows 10 22H2 版的 YYYY-MM 服務堆疊更新 |
如果您想要使用其他語言或功能隨選自定義映像,請從 大量授權服務中心下載補充媒體 ISO 檔案。 例如,如果您的裝置將停用動態更新,而且如果使用者需要特定的功能隨選,您可以將這些功能預安裝到映射中。
更新 Windows 安裝媒體
正確更新安裝媒體牽涉到在數個不同的目標上運作的許多動作, (映像檔案) 。 某些動作會在不同的目標上重複。 目標映像檔案包括:
- Windows 預安裝環境 (WinPE) :用來安裝、部署和修復 Windows作系統的小型作系統
- Windows Recovery Environment (WinRE) :修復無法啟動作系統的常見原因。 WinRE 是以 WinPE 為基礎,而且可以使用其他驅動程式、語言、選擇性套件和其他疑難解答或診斷工具來自定義。
- Windows作系統:儲存在 \sources\install.wim 中的一或多個 Windows 版本
- Windows 安裝媒體:Windows 安裝媒體中檔案和資料夾的完整集合。 例如,\sources 資料夾、\boot 資料夾、Setup.exe 等等。
下表顯示將各種工作套用至檔案的正確順序。 例如,完整順序會從將維護堆疊更新新增至 WinRE (1) 開始,最後將開機管理員從 WinPE 新增至新的媒體 (28) 。
工作 | winRE (winre.wim) | 作系統 (install.wim) | winPE (boot.wim) | 新媒體 |
---|---|---|---|---|
透過最新的累積更新新增服務堆疊更新 | 1 | 9 | 17 | |
新增語言套件 | 2 | 10 | 18 | |
新增本地化的選擇性套件 | 3 | 19 | ||
新增字型支援 | 4 | 20 | ||
新增文字到語音轉換 | 5 | 21 | ||
更新 Lang.ini | 22 | |||
視需要新增功能 | 11 | |||
新增選擇性元件 | 12 | |||
新增安全OS動態更新 | 6 | |||
新增安裝程序動態更新 | 26 | |||
從 WinPE 新增 Setup.exe 和 setuphost.exe | 27 | |||
從 WinPE 新增開機管理員 | 28 | |||
新增最新的累積更新 | 13 | 23 | ||
清除映像 | 7 | 14 | 24 | |
新增 .NET 和 .NET 累積更新 | 15 | |||
匯出映像 | 8 | 16 | 25 |
注意
從 2021 年 2 月開始,最新的累積更新和服務堆疊更新會合併並散發於Microsoft更新類別目錄中,作為新的合併累積更新。 針對需要服務堆疊更新以更新安裝媒體的步驟 1、9 和 17,您應該使用合併的累積更新。 如需合併累積更新的詳細資訊,請參閱 服務堆疊更新。
注意
Microsoft會透過下列KB4577586從 Windows 移除 Flash 元件 :移除 Adobe Flash Player 的更新。 您也可以在步驟 20 到 21 之間的目錄) 上部署可用KB4577586 (中的更新,隨時移除 Flash。 自 2021 年 7 月起,KB4577586,Windows 10 版本 1607 和 1507 的最新累積更新中將包含「移除 Adobe Flash Player 的更新」。 更新也會包含在 Windows 8.1、Windows Server 2012 和 Windows Embedded 8 Standard 的每月匯總和僅限安全性更新中。 如需詳細資訊,請 參閱 Adobe Flash Player 終止支援更新。
多個 Windows 版本
install.wim) (主作系統檔案可能包含多個版本的 Windows。 根據索引,部署指定版本可能只需要更新。 或者,可能是所有版本都需要更新。 此外,請確定語言已安裝在功能隨選之前,且最新的累積更新一律會最後套用。
其他語言和功能
您不需要在映像中新增更多語言和功能來完成更新,但除了開始映射之外,還有機會使用更多語言、選擇性元件和功能隨選自定義映像。 當您新增更多語言和功能時,請務必以正確的順序進行這些變更:先套用服務堆棧更新,後面接著新增語言,再新增功能,最後套用最新的累積更新。 提供的範例腳本會安裝第二種語言 (,在此案例中為日文 (ja-JP) ) 。 由於此語言是由 lp.cab 所支援,因此不需要新增語言體驗套件。 日文會同時新增至主要作系統和復原環境,以允許使用者以日文檢視復原畫面。 這包括新增目前安裝在復原映像中的當地語系化套件版本。
選擇性元件以及 .NET 功能可以離線安裝。 不過,這樣做會建立需要裝置重新啟動的暫止作業。 因此,執行映像清除的呼叫將會失敗。 有兩個選項可避免清除失敗。 其中一個選項是略過映射清除步驟,不過這會導致較大的 install.wim。 另一個選項是在清除之後,但在導出之前,於步驟中安裝 .NET 和選擇性元件。 這是範例腳本中的選項。 如此一來,您就必須從原始的 install.wim (開始,當您在下次維護或更新映射時,不會) 任何暫止動作,例如下一個月) (。
檢查點累積更新
從 Windows 11 版本 24H2 和 Windows Server 2025 開始,最新的累積更新可能具有必須先安裝的必要條件累積更新。 這些更新稱為檢查點累積更新。 在這些情況下,累積更新檔案層級差異是以先前的累積更新為基礎,而不是 Windows RTM 版本。 優點是較小的更新套件和更快速的安裝。 當您從 [Microsoft 更新類別目錄] 取得最新的累積更新時,會從 [下載] 按鈕取得檢查點累積更新。 此外,累積更新的 知識庫 文章提供其他資訊。
若要在服務 Windows OS 時安裝檢查點 () (步驟 9 & 12) 和 WinPE (步驟 17 & 23) ,請使用目標累積更新來呼叫 Add-WindowsPackage
。 的資料夾 -PackagePath
可用來視需要探索並安裝一或多個檢查點。 只有目標累積更新和檢查點累積更新應該位於 資料夾中 -PackagePath
。 具有修訂 <的累積更新套件 = 處理目標累積更新。 如果您不是使用其他語言和/或選擇性功能自定義映像,則可以針對上述步驟 9 & 17 使用 (檢查點累積更新的個別呼叫 Add-WindowsPackage
) 。 步驟 12 和 23 無法使用個別呼叫。
Windows PowerShell 腳本將動態 匯報 套用至現有的映像
這些範例僅供說明之用,因此缺少錯誤處理。 文稿假設下列套件會儲存在此資料夾結構的本機:
資料夾 | 描述 |
---|---|
C:\mediaRefresh | 包含 PowerShell 腳本的父資料夾 |
C:\mediaRefresh\oldMedia | 包含將重新整理之原始媒體的資料夾。 例如, 包含 Setup.exe 和 \sources資料夾。 |
C:\mediaRefresh\newMedia | 將包含更新媒體的資料夾。 它會從 \oldMedia 複製,然後作為所有更新和清除作業的目標。 |
入門
腳本一開始會宣告全域變數,並建立要用於掛接影像的資料夾。 然後,製作原始媒體的複本,從 \oldMedia 到 \newMedia,在發生腳本錯誤時保留原始媒體,而且必須從已知狀態重新開始。 此外,它提供舊媒體與新媒體的比較,以評估變更。 若要確保新的媒體更新,請確定它們不是唯讀的。 此腳本也會展示新增其他語言、功能隨選和選擇性元件。 這些並非必要專案,但會在應該新增它們的順序時新增以醒目提示。 從 Windows 11 21H2 版開始,語言套件 (LANGPACK) ISO 會由功能隨選 ISO 取代。 語言套件和 \Windows 預安裝環境套件是功能隨選 ISO 的一部分。 此外,主要OS語言和選擇性功能的路徑已移至 \LanguagesAndOptionalFeatures,而不是根目錄。 如果您使用此腳本進行 Windows 10,請修改 以掛接並使用語言套件 (LANGPACK) ISO。
#Requires -RunAsAdministrator
function Get-TS { return "{0:HH:mm:ss}" -f [DateTime]::Now }
Write-Output "$(Get-TS): Starting media refresh"
# Declare Dynamic Update packages. A dedicated folder is used for the latest cumulative update, and as needed
# checkpoint cumulative updates.
$LCU_PATH = "C:\mediaRefresh\packages\CU\LCU.msu"
$SETUP_DU_PATH = "C:\mediaRefresh\packages\Other\Setup_DU.cab"
$SAFE_OS_DU_PATH = "C:\mediaRefresh\packages\Other\SafeOS_DU.cab"
$DOTNET_CU_PATH = "C:\mediaRefresh\packages\Other\DotNet_CU.msu"
# Declare media for FOD and LPs
$FOD_ISO_PATH = "C:\mediaRefresh\packages\CLIENT_LOF_PACKAGES_OEM.iso"
# Array of Features On Demand for main OS
# This is optional to showcase where these are added
$FOD = @(
'XPS.Viewer~~~~0.0.1.0'
)
# Array of Legacy Features for main OS
# This is optional to showcase where these are added
$OC = @(
'MediaPlayback'
'WindowsMediaPlayer'
)
# Mount the Features on Demand ISO
Write-Output "$(Get-TS): Mounting FOD ISO"
$FOD_ISO_DRIVE_LETTER = (Mount-DiskImage -ImagePath $FOD_ISO_PATH -ErrorAction stop | Get-Volume).DriveLetter
$FOD_PATH = $FOD_ISO_DRIVE_LETTER + ":\LanguagesAndOptionalFeatures"
# Declare language for showcasing adding optional localized components
$LANG = "ja-jp"
$LANG_FONT_CAPABILITY = "jpan"
# Declare language related cabs
$WINPE_OC_PATH = "$FOD_ISO_DRIVE_LETTER`:\Windows Preinstallation Environment\x64\WinPE_OCs"
$WINPE_OC_LANG_PATH = "$WINPE_OC_PATH\$LANG"
$WINPE_OC_LANG_CABS = Get-ChildItem $WINPE_OC_LANG_PATH -Name
$WINPE_OC_LP_PATH = "$WINPE_OC_LANG_PATH\lp.cab"
$WINPE_FONT_SUPPORT_PATH = "$WINPE_OC_PATH\WinPE-FontSupport-$LANG.cab"
$WINPE_SPEECH_TTS_PATH = "$WINPE_OC_PATH\WinPE-Speech-TTS.cab"
$WINPE_SPEECH_TTS_LANG_PATH = "$WINPE_OC_PATH\WinPE-Speech-TTS-$LANG.cab"
$OS_LP_PATH = "$FOD_PATH\Microsoft-Windows-Client-Language-Pack_x64_$LANG.cab"
# Declare folders for mounted images and temp files
$MEDIA_OLD_PATH = "C:\mediaRefresh\oldMedia\Ge\client_professional_en-us"
$MEDIA_NEW_PATH = "C:\mediaRefresh\newMedia"
$WORKING_PATH = "C:\mediaRefresh\temp"
$MAIN_OS_MOUNT = "C:\mediaRefresh\temp\MainOSMount"
$WINRE_MOUNT = "C:\mediaRefresh\temp\WinREMount"
$WINPE_MOUNT = "C:\mediaRefresh\temp\WinPEMount"
# Create folders for mounting images and storing temporary files
New-Item -ItemType directory -Path $WORKING_PATH -ErrorAction Stop | Out-Null
New-Item -ItemType directory -Path $MAIN_OS_MOUNT -ErrorAction stop | Out-Null
New-Item -ItemType directory -Path $WINRE_MOUNT -ErrorAction stop | Out-Null
New-Item -ItemType directory -Path $WINPE_MOUNT -ErrorAction stop | Out-Null
# Keep the original media, make a copy of it for the new, updated media.
Write-Output "$(Get-TS): Copying original media to new media path"
Copy-Item -Path $MEDIA_OLD_PATH"\*" -Destination $MEDIA_NEW_PATH -Force -Recurse -ErrorAction stop | Out-Null
Get-ChildItem -Path $MEDIA_NEW_PATH -Recurse | Where-Object { -not $_.PSIsContainer -and $_.IsReadOnly } | ForEach-Object { $_.IsReadOnly = $false }
更新 WinRE 和每個主要 OS Windows 版本
腳本會更新主要作系統檔案內的每個 Windows 版本, (install.wim) 。 針對每個版本,會掛接主要OS映像。
針對第一個映像,Winre.wim 會複製到工作資料夾並掛接。 然後,它會透過最新的累積更新套用服務堆疊,因為其元件是用來更新其他元件。 視您要更新的 Windows 版本而定,有兩種更新服務堆疊的不同方法。 第一種方法是使用合併的累積更新。 這是針對傳送合併累積更新的 Windows 版本,其中包含服務堆疊更新 (也就是 SSU + LCU 會合併) 。 Windows 11 版本 21H2 和 Windows 11 版本 22H2 都是範例。 在這些情況下,服務堆疊更新不會個別發佈;此步驟應該使用合併的累積更新。 不過,在極少數情況下,合併的累積更新格式變更可能會有重大變更,需要發佈獨立維護堆棧更新,並先安裝,才能安裝合併的累積更新。 因為腳本是選擇性地新增日文,所以會將語言套件新增至映像,並安裝所有已安裝在 Winre.wim 中之選用套件的日文版本。 然後,它會套用安全OS動態更新套件。 其完成方式是清除和導出影像,以減少影像大小。
接下來,針對掛接的OS映射,腳本會從透過最新的累積更新套用服務堆棧開始。 然後,它會新增日文語言支援,然後新增日文語言功能。 不同於動態更新套件,它會使用 Add-WindowsCapability
來新增這些功能。 如需這類功能及其相關聯功能名稱的完整清單,請參閱 可用的功能隨選。 現在可以啟用其他選擇性元件或新增其他功能隨選。 如果這類功能具有相關聯的累積更新 (例如 .NET) ,則這是套用這些更新的時間。 接著,腳本會嘗試清除映像,然後執行最後一個步驟來套用最新的累積更新。 請務必最後套用最新的累積更新,以確保功能隨選、選用元件和語言會從其初始發行狀態更新。 .NET 功能是下一個新增的例外狀況及其累積更新。 最後,腳本會匯出映射。
此程式會針對主要作系統檔案內的每個 Windows 版本重複執行。 為了減少大小,會儲存第一個映射中的服務 Winre.wim 檔案,並用來更新每個後續的 Windows 版本。 這會減少 install.wim 的最終大小。
#
# Update each main OS Windows image including the Windows Recovery Environment (WinRE)
#
# Get the list of images contained within the main OS
$WINOS_IMAGES = Get-WindowsImage -ImagePath $MEDIA_NEW_PATH"\sources\install.wim"
Foreach ($IMAGE in $WINOS_IMAGES)
{
# first mount the main OS image
Write-Output "$(Get-TS): Mounting main OS, image index $($IMAGE.ImageIndex)"
Mount-WindowsImage -ImagePath $MEDIA_NEW_PATH"\sources\install.wim" -Index $IMAGE.ImageIndex -Path $MAIN_OS_MOUNT -ErrorAction stop| Out-Null
if ($IMAGE.ImageIndex -eq "1")
{
#
# update Windows Recovery Environment (WinRE) within this OS image
#
Copy-Item -Path $MAIN_OS_MOUNT"\windows\system32\recovery\winre.wim" -Destination $WORKING_PATH"\winre.wim" -Force -ErrorAction stop | Out-Null
Write-Output "$(Get-TS): Mounting WinRE"
Mount-WindowsImage -ImagePath $WORKING_PATH"\winre.wim" -Index 1 -Path $WINRE_MOUNT -ErrorAction stop | Out-Null
# Add servicing stack update (Step 1 from the table)
Write-Output "$(Get-TS): Adding package $LCU_PATH to WinRE"
try
{
Add-WindowsPackage -Path $WINRE_MOUNT -PackagePath $LCU_PATH | Out-Null
}
Catch
{
$theError = $_
Write-Output "$(Get-TS): $theError"
if ($theError.Exception -like "*0x8007007e*")
{
Write-Warning "$(Get-TS): Failed with error 0x8007007e. This failure is a known issue with combined cumulative update, we can ignore."
}
else
{
throw
}
}
#
# Optional: Add the language to recovery environment
#
# Install lp.cab cab
Write-Output "$(Get-TS): Adding package $WINPE_OC_LP_PATH to WinRE"
Add-WindowsPackage -Path $WINRE_MOUNT -PackagePath $WINPE_OC_LP_PATH -ErrorAction stop | Out-Null
# Install language cabs for each optional package installed
$WINRE_INSTALLED_OC = Get-WindowsPackage -Path $WINRE_MOUNT
Foreach ($PACKAGE in $WINRE_INSTALLED_OC)
{
if ( ($PACKAGE.PackageState -eq "Installed") -and ($PACKAGE.PackageName.startsWith("WinPE-")) -and ($PACKAGE.ReleaseType -eq "FeaturePack") )
{
$INDEX = $PACKAGE.PackageName.IndexOf("-Package")
if ($INDEX -ge 0)
{
$OC_CAB = $PACKAGE.PackageName.Substring(0, $INDEX) + "_" + $LANG + ".cab"
if ($WINPE_OC_LANG_CABS.Contains($OC_CAB))
{
$OC_CAB_PATH = Join-Path $WINPE_OC_LANG_PATH $OC_CAB
Write-Output "$(Get-TS): Adding package $OC_CAB_PATH to WinRE"
Add-WindowsPackage -Path $WINRE_MOUNT -PackagePath $OC_CAB_PATH -ErrorAction stop | Out-Null
}
}
}
}
# Add font support for the new language
if ( (Test-Path -Path $WINPE_FONT_SUPPORT_PATH) )
{
Write-Output "$(Get-TS): Adding package $WINPE_FONT_SUPPORT_PATH to WinRE"
Add-WindowsPackage -Path $WINRE_MOUNT -PackagePath $WINPE_FONT_SUPPORT_PATH -ErrorAction stop | Out-Null
}
# Add TTS support for the new language
if (Test-Path -Path $WINPE_SPEECH_TTS_PATH)
{
if ( (Test-Path -Path $WINPE_SPEECH_TTS_LANG_PATH) )
{
Write-Output "$(Get-TS): Adding package $WINPE_SPEECH_TTS_PATH to WinRE"
Add-WindowsPackage -Path $WINRE_MOUNT -PackagePath $WINPE_SPEECH_TTS_PATH -ErrorAction stop | Out-Null
Write-Output "$(Get-TS): Adding package $WINPE_SPEECH_TTS_LANG_PATH to WinRE"
Add-WindowsPackage -Path $WINRE_MOUNT -PackagePath $WINPE_SPEECH_TTS_LANG_PATH -ErrorAction stop | Out-Null
}
}
# Add Safe OS
Write-Output "$(Get-TS): Adding package $SAFE_OS_DU_PATH to WinRE"
Add-WindowsPackage -Path $WINRE_MOUNT -PackagePath $SAFE_OS_DU_PATH -ErrorAction stop | Out-Null
# Perform image cleanup
Write-Output "$(Get-TS): Performing image cleanup on WinRE"
DISM /image:$WINRE_MOUNT /cleanup-image /StartComponentCleanup /ResetBase /Defer | Out-Null
if ($LastExitCode -ne 0)
{
throw "Error: Failed to perform image cleanup on WinRE. Exit code: $LastExitCode"
}
# Dismount
Dismount-WindowsImage -Path $WINRE_MOUNT -Save -ErrorAction stop | Out-Null
# Export
Write-Output "$(Get-TS): Exporting image to $WORKING_PATH\winre.wim"
Export-WindowsImage -SourceImagePath $WORKING_PATH"\winre.wim" -SourceIndex 1 -DestinationImagePath $WORKING_PATH"\winre2.wim" -ErrorAction stop | Out-Null
}
Copy-Item -Path $WORKING_PATH"\winre2.wim" -Destination $MAIN_OS_MOUNT"\windows\system32\recovery\winre.wim" -Force -ErrorAction stop | Out-Null
#
# update Main OS
#
# Add servicing stack update (Step 17 from the table). Unlike WinRE and WinPE, we don't need to check for error 0x8007007e
Write-Output "$(Get-TS): Adding package $LCU_PATH to main OS, index $($IMAGE.ImageIndex)"
Add-WindowsPackage -Path $MAIN_OS_MOUNT -PackagePath $LCU_PATH | Out-Null
# Optional: Add language to main OS and corresponding language experience Features on Demand
Write-Output "$(Get-TS): Adding package $OS_LP_PATH to main OS, index $($IMAGE.ImageIndex)"
Add-WindowsPackage -Path $MAIN_OS_MOUNT -PackagePath $OS_LP_PATH -ErrorAction stop | Out-Null
Write-Output "$(Get-TS): Adding language FOD: Language.Fonts.Jpan~~~und-JPAN~0.0.1.0 to main OS, index $($IMAGE.ImageIndex)"
Add-WindowsCapability -Name "Language.Fonts.$LANG_FONT_CAPABILITY~~~und-$LANG_FONT_CAPABILITY~0.0.1.0" -Path $MAIN_OS_MOUNT -Source $FOD_PATH -ErrorAction stop | Out-Null
Write-Output "$(Get-TS): Adding language FOD: Language.Basic~~~$LANG~0.0.1.0 to main OS, index $($IMAGE.ImageIndex)"
Add-WindowsCapability -Name "Language.Basic~~~$LANG~0.0.1.0" -Path $MAIN_OS_MOUNT -Source $FOD_PATH -ErrorAction stop | Out-Null
Write-Output "$(Get-TS): Adding language FOD: Language.OCR~~~$LANG~0.0.1.0 to main OS, index $($IMAGE.ImageIndex)"
Add-WindowsCapability -Name "Language.OCR~~~$LANG~0.0.1.0" -Path $MAIN_OS_MOUNT -Source $FOD_PATH -ErrorAction stop | Out-Null
Write-Output "$(Get-TS): Adding language FOD: Language.Handwriting~~~$LANG~0.0.1.0 to main OS, index $($IMAGE.ImageIndex)"
Add-WindowsCapability -Name "Language.Handwriting~~~$LANG~0.0.1.0" -Path $MAIN_OS_MOUNT -Source $FOD_PATH -ErrorAction stop | Out-Null
Write-Output "$(Get-TS): Adding language FOD: Language.TextToSpeech~~~$LANG~0.0.1.0 to main OS, index $($IMAGE.ImageIndex)"
Add-WindowsCapability -Name "Language.TextToSpeech~~~$LANG~0.0.1.0" -Path $MAIN_OS_MOUNT -Source $FOD_PATH -ErrorAction stop | Out-Null
Write-Output "$(Get-TS): Adding language FOD: Language.Speech~~~$LANG~0.0.1.0 to main OS, index $($IMAGE.ImageIndex)"
Add-WindowsCapability -Name "Language.Speech~~~$LANG~0.0.1.0" -Path $MAIN_OS_MOUNT -Source $FOD_PATH -ErrorAction stop | Out-Null
# Optional: Add additional Features On Demand
For ( $index = 0; $index -lt $FOD.count; $index++)#
{
Write-Output "$(Get-TS): Adding $($FOD[$index]) to main OS, index $($IMAGE.ImageIndex)"
Add-WindowsCapability -Name $($FOD[$index]) -Path $MAIN_OS_MOUNT -Source $FOD_PATH -ErrorAction stop | Out-Null
}
# Optional: Add Legacy Features
For ( $index = 0; $index -lt $OC.count; $index++)
{
Write-Output "$(Get-TS): Adding $($OC[$index]) to main OS, index $($IMAGE.ImageIndex)"
DISM /Image:$MAIN_OS_MOUNT /Enable-Feature /FeatureName:$($OC[$index]) /All | Out-Null
if ($LastExitCode -ne 0)
{
throw "Error: Failed to add $($OC[$index]) to main OS, index $($IMAGE.ImageIndex). Exit code: $LastExitCode"
}
}
# Add latest cumulative update
Write-Output "$(Get-TS): Adding package $LCU_PATH to main OS, index $($IMAGE.ImageIndex)"
Add-WindowsPackage -Path $MAIN_OS_MOUNT -PackagePath $LCU_PATH -ErrorAction stop | Out-Null
# Perform image cleanup. Some Optional Components might require the image to be booted, and thus
# image cleanup may fail. We'll catch and handle as a warning.
Write-Output "$(Get-TS): Performing image cleanup on main OS, index $($IMAGE.ImageIndex)"
DISM /image:$MAIN_OS_MOUNT /cleanup-image /StartComponentCleanup | Out-Null
if ($LastExitCode -ne 0)
{
if ($LastExitCode -eq -2146498554)
{
# We hit 0x800F0806 CBS_E_PENDING. We will ignore this with a warning
# This is likely due to legacy components being added that require online operations.
Write-Warning "$(Get-TS): Failed to perform image cleanup on main OS, index $($IMAGE.ImageIndex). Exit code: $LastExitCode. The operation cannot be performed until pending servicing operations are completed. The image must be booted to complete the pending servicing operation."
}
else
{
throw "Error: Failed to perform image cleanup on main OS, index $($IMAGE.ImageIndex). Exit code: $LastExitCode"
}
}
# Finally, we'll add .NET 3.5 and the .NET cumulative update
Write-Output "$(Get-TS): Adding NetFX3~~~~ to main OS, index $($IMAGE.ImageIndex)"
Add-WindowsCapability -Name "NetFX3~~~~" -Path $MAIN_OS_MOUNT -Source $FOD_PATH -ErrorAction stop | Out-Null
# Add .NET Cumulative Update
Write-Output "$(Get-TS): Adding package $DOTNET_CU_PATH to main OS, index $($IMAGE.ImageIndex)"
Add-WindowsPackage -Path $MAIN_OS_MOUNT -PackagePath $DOTNET_CU_PATH -ErrorAction stop | Out-Null
# Dismount
Dismount-WindowsImage -Path $MAIN_OS_MOUNT -Save -ErrorAction stop | Out-Null
# Export
Write-Output "$(Get-TS): Exporting image to $WORKING_PATH\install2.wim"
Export-WindowsImage -SourceImagePath $MEDIA_NEW_PATH"\sources\install.wim" -SourceIndex $IMAGE.ImageIndex -DestinationImagePath $WORKING_PATH"\install2.wim" -ErrorAction stop | Out-Null
}
Move-Item -Path $WORKING_PATH"\install2.wim" -Destination $MEDIA_NEW_PATH"\sources\install.wim" -Force -ErrorAction stop | Out-Null
更新 WinPE
此腳本類似於更新 WinRE 的腳本,但會改為掛接 Boot.wim、最後套用具有最新累積更新的套件,然後儲存。 它會針對 Boot.wim 內的所有映像重複此動作,通常是兩個映像。 它一開始會套用服務堆疊動態更新。 因為腳本是使用日文自定義此媒體,所以會從語言套件 ISO 上的 WinPE 資料夾安裝語言套件。 此外,它會將字型支援和文字新增至語音 (TTS) 支援。 由於腳本正在新增語言,因此會重建 lang.ini,用來識別映像中安裝的語言。 針對第二個映像,我們會儲存 setup.exe 和 setuphost.exe 以供稍後使用,以確保這些版本符合安裝媒體的 \sources\setup.exe 和 \sources\setuphost.exe 版本。 如果這些二進位檔不相同,Windows 安裝程式會在安裝期間失敗。 我們也會儲存服務開機管理員檔案,以供稍後在腳本中使用。 最後,腳本會清除並匯出 Boot.wim,並將其複製回新媒體。
#
# update Windows Preinstallation Environment (WinPE)
#
# Get the list of images contained within WinPE
$WINPE_IMAGES = Get-WindowsImage -ImagePath $MEDIA_NEW_PATH"\sources\boot.wim"
Foreach ($IMAGE in $WINPE_IMAGES)
{
# update WinPE
Write-Output "$(Get-TS): Mounting WinPE, image index $($IMAGE.ImageIndex)"
Mount-WindowsImage -ImagePath $MEDIA_NEW_PATH"\sources\boot.wim" -Index $IMAGE.ImageIndex -Path $WINPE_MOUNT -ErrorAction stop | Out-Null
# Add servicing stack update (Step 9 from the table)
try
{
Write-Output "$(Get-TS): Adding package $LCU_PATH to WinPE, image index $($IMAGE.ImageIndex)"
Add-WindowsPackage -Path $WINPE_MOUNT -PackagePath $LCU_PATH | Out-Null
}
Catch
{
$theError = $_
Write-Output "$(Get-TS): $theError"
if ($theError.Exception -like "*0x8007007e*")
{
Write-Warning "$(Get-TS): Failed with error 0x8007007e. This failure is a known issue with combined cumulative update, we can ignore."
}
else
{
throw
}
}
# Install lp.cab cab
Write-Output "$(Get-TS): Adding package $WINPE_OC_LP_PATH to WinPE, image index $($IMAGE.ImageIndex)"
Add-WindowsPackage -Path $WINPE_MOUNT -PackagePath $WINPE_OC_LP_PATH -ErrorAction stop | Out-Null
# Install language cabs for each optional package installed
$WINPE_INSTALLED_OC = Get-WindowsPackage -Path $WINPE_MOUNT
Foreach ($PACKAGE in $WINPE_INSTALLED_OC)
{
if ( ($PACKAGE.PackageState -eq "Installed") -and ($PACKAGE.PackageName.startsWith("WinPE-")) -and ($PACKAGE.ReleaseType -eq "FeaturePack") )
{
$INDEX = $PACKAGE.PackageName.IndexOf("-Package")
if ($INDEX -ge 0)
{
$OC_CAB = $PACKAGE.PackageName.Substring(0, $INDEX) + "_" + $LANG + ".cab"
if ($WINPE_OC_LANG_CABS.Contains($OC_CAB))
{
$OC_CAB_PATH = Join-Path $WINPE_OC_LANG_PATH $OC_CAB
Write-Output "$(Get-TS): Adding package $OC_CAB_PATH to WinPE, image index $($IMAGE.ImageIndex)"
Add-WindowsPackage -Path $WINPE_MOUNT -PackagePath $OC_CAB_PATH -ErrorAction stop | Out-Null
}
}
}
}
# Add font support for the new language
if ( (Test-Path -Path $WINPE_FONT_SUPPORT_PATH) )
{
Write-Output "$(Get-TS): Adding package $WINPE_FONT_SUPPORT_PATH to WinPE, image index $($IMAGE.ImageIndex)"
Add-WindowsPackage -Path $WINPE_MOUNT -PackagePath $WINPE_FONT_SUPPORT_PATH -ErrorAction stop | Out-Null
}
# Add TTS support for the new language
if (Test-Path -Path $WINPE_SPEECH_TTS_PATH)
{
if ( (Test-Path -Path $WINPE_SPEECH_TTS_LANG_PATH) )
{
Write-Output "$(Get-TS): Adding package $WINPE_SPEECH_TTS_PATH to WinPE, image index $($IMAGE.ImageIndex)"
Add-WindowsPackage -Path $WINPE_MOUNT -PackagePath $WINPE_SPEECH_TTS_PATH -ErrorAction stop | Out-Null
Write-Output "$(Get-TS): Adding package $WINPE_SPEECH_TTS_LANG_PATH to WinPE, image index $($IMAGE.ImageIndex)"
Add-WindowsPackage -Path $WINPE_MOUNT -PackagePath $WINPE_SPEECH_TTS_LANG_PATH -ErrorAction stop | Out-Null
}
}
# Generates a new Lang.ini file which is used to define the language packs inside the image
if ( (Test-Path -Path $WINPE_MOUNT"\sources\lang.ini") )
{
Write-Output "$(Get-TS): Updating lang.ini"
DISM /image:$WINPE_MOUNT /Gen-LangINI /distribution:$WINPE_MOUNT | Out-Null
if ($LastExitCode -ne 0)
{
throw "Error: Failed to update lang.ini. Exit code: $LastExitCode"
}
}
# Add latest cumulative update
Write-Output "$(Get-TS): Adding package $LCU_PATH to WinPE, image index $($IMAGE.ImageIndex)"
Add-WindowsPackage -Path $WINPE_MOUNT -PackagePath $LCU_PATH -ErrorAction stop | Out-Null
# Perform image cleanup
Write-Output "$(Get-TS): Performing image cleanup on WinPE, image index $($IMAGE.ImageIndex)"
DISM /image:$WINPE_MOUNT /cleanup-image /StartComponentCleanup /ResetBase /Defer | Out-Null
if ($LastExitCode -ne 0)
{
throw "Error: Failed to perform image cleanup on WinPE, image index $($IMAGE.ImageIndex). Exit code: $LastExitCode"
}
if ($IMAGE.ImageIndex -eq "2")
{
# Save setup.exe for later use. This will address possible binary mismatch with the version in the main OS \sources folder
Copy-Item -Path $WINPE_MOUNT"\sources\setup.exe" -Destination $WORKING_PATH"\setup.exe" -Force -ErrorAction stop | Out-Null
# Save setuphost.exe for later use. This will address possible binary mismatch with the version in the main OS \sources folder
# This is only required starting with Windows 11 version 24H2
$TEMP = Get-WindowsImage -ImagePath $MEDIA_NEW_PATH"\sources\boot.wim" -Index $IMAGE.ImageIndex
if ([System.Version]$TEMP.Version -ge [System.Version]"10.0.26100")
{
Copy-Item -Path $WINPE_MOUNT"\sources\setuphost.exe" -Destination $WORKING_PATH"\setuphost.exe" -Force -ErrorAction stop | Out-Null
}
else
{
Write-Output "$(Get-TS): Skipping copy of setuphost.exe; image version $($TEMP.Version)"
}
# Save serviced boot manager files later copy to the root media.
Copy-Item -Path $WINPE_MOUNT"\Windows\boot\efi\bootmgfw.efi" -Destination $WORKING_PATH"\bootmgfw.efi" -Force -ErrorAction stop | Out-Null
Copy-Item -Path $WINPE_MOUNT"\Windows\boot\efi\bootmgr.efi" -Destination $WORKING_PATH"\bootmgr.efi" -Force -ErrorAction stop | Out-Null
}
# Dismount
Dismount-WindowsImage -Path $WINPE_MOUNT -Save -ErrorAction stop | Out-Null
#Export WinPE
Write-Output "$(Get-TS): Exporting image to $WORKING_PATH\boot2.wim"
Export-WindowsImage -SourceImagePath $MEDIA_NEW_PATH"\sources\boot.wim" -SourceIndex $IMAGE.ImageIndex -DestinationImagePath $WORKING_PATH"\boot2.wim" -ErrorAction stop | Out-Null
}
Move-Item -Path $WORKING_PATH"\boot2.wim" -Destination $MEDIA_NEW_PATH"\sources\boot.wim" -Force -ErrorAction stop | Out-Null
更新剩餘的媒體檔案
腳本的這個部分會更新安裝程序檔案。 它只會將安裝程序動態更新套件中的個別檔案複製到新媒體。 此步驟會視需要帶入更新的安裝程式檔案,以及最新的相容性資料庫和取代元件指令清單。 此腳本也會使用先前從 WinPE 儲存的版本,最終取代 setup.exe、setuphost.exe 和開機管理員檔案。
#
# update remaining files on media
#
# Add Setup DU by copy the files from the package into the newMedia
Write-Output "$(Get-TS): Adding package $SETUP_DU_PATH"
cmd.exe /c $env:SystemRoot\System32\expand.exe $SETUP_DU_PATH -F:* $MEDIA_NEW_PATH"\sources" | Out-Null
if ($LastExitCode -ne 0)
{
throw "Error: Failed to expand $SETUP_DU_PATH. Exit code: $LastExitCode"
}
# Copy setup.exe from boot.wim, saved earlier.
Write-Output "$(Get-TS): Copying $WORKING_PATH\setup.exe to $MEDIA_NEW_PATH\sources\setup.exe"
Copy-Item -Path $WORKING_PATH"\setup.exe" -Destination $MEDIA_NEW_PATH"\sources\setup.exe" -Force -ErrorAction stop | Out-Null
# Copy setuphost.exe from boot.wim, saved earlier.
if (Test-Path -Path $WORKING_PATH"\setuphost.exe")
{
Write-Output "$(Get-TS): Copying $WORKING_PATH\setuphost.exe to $MEDIA_NEW_PATH\sources\setuphost.exe"
Copy-Item -Path $WORKING_PATH"\setuphost.exe" -Destination $MEDIA_NEW_PATH"\sources\setuphost.exe" -Force -ErrorAction stop | Out-Null
}
# Copy bootmgr files from boot.wim, saved earlier.
$MEDIA_NEW_FILES = Get-ChildItem $MEDIA_NEW_PATH -Force -Recurse -Filter b*.efi
Foreach ($File in $MEDIA_NEW_FILES)
{
if (($File.Name -ieq "bootmgfw.efi") -or ($File.Name -ieq "bootx64.efi") -or ($File.Name -ieq "bootia32.efi") -or ($File.Name -ieq "bootaa64.efi"))
{
Write-Output "$(Get-TS): Copying $WORKING_PATH\bootmgfw.efi to $($File.FullName)"
Copy-Item -Path $WORKING_PATH"\bootmgfw.efi" -Destination $File.FullName -Force -ErrorAction stop | Out-Null
}
elseif ($File.Name -ieq "bootmgr.efi")
{
Write-Output "$(Get-TS): Copying $WORKING_PATH\bootmgr.efi to $($File.FullName)"
Copy-Item -Path $WORKING_PATH"\bootmgr.efi" -Destination $File.FullName -Force -ErrorAction stop | Out-Null
}
}
完成
在最後一個步驟中,腳本會移除暫存盤的工作資料夾,並卸除我們的語言套件和功能隨選 ISO。
#
# Perform final cleanup
#
# Remove our working folder
Remove-Item -Path $WORKING_PATH -Recurse -Force -ErrorAction stop | Out-Null
# Dismount ISO images
Write-Output "$(Get-TS): Dismounting ISO images"
Dismount-DiskImage -ImagePath $FOD_ISO_PATH -ErrorAction stop | Out-Null
Write-Output "$(Get-TS): Media refresh completed!"