Share via


使用動態更新更新 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 Update Catalog 取得動態更新套件。 在該網站上,使用右上方的搜尋列來尋找特定版本的動態更新套件。 各種動態更新套件可能不會全部出現在單一搜尋的結果中,因此您可能必須使用不同的關鍵詞進行搜尋,以尋找所有更新。 檢查結果的各個部分,確定您已識別出所需的檔案。 下表顯示要在結果中搜尋或尋找的索引鍵值。

Windows 11 版本 22H2 動態更新套件

標題 可以區分每個動態套件。 累積更新內嵌服務堆疊。 只有在指定的累積更新需要時,才會發佈服務堆疊。

更新套件 Title
安全作業系統動態更新 適用於 Windows 11 版本 22H2 的 YYYY-MM 安全 OS 動態更新
設定動態更新 YYYY-MM 安裝程式 22H2 版 Windows 11 動態更新
最新的累積更新 Windows 11 22H2 版的 YYYY-MM 累積更新
服務堆疊動態更新 YYYY-MM 版本 22H2 的 Windows 11 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
新增安全OS動態更新 6
新增安裝程序動態更新 26
從 WinPE 新增 setup.exe 27
從 WinPE 新增開機管理員 28
新增最新的累積更新 12 23
清除映像 7 13 24
新增選擇性元件 14
新增 .NET 和 .NET 累積更新 15
匯出映像 8 16 25

注意

從 2021 年 2 月開始,最新的累積更新和服務堆疊更新將會合併並散發在 Microsoft Update Catalog 中,作為新的合併累積更新。 針對需要服務堆疊更新以更新安裝媒體的步驟 1、9 和 18,您應該使用合併的累積更新。 如需合併累積更新的詳細資訊,請參閱 服務堆疊更新

注意

Microsoft 將從 Windows 移除 Flash 元件,KB4577586「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 PowerShell 腳本將動態 匯報 套用至現有的映像

這些範例僅供說明之用,因此缺少錯誤處理。 文稿假設下列套件會儲存在此資料夾結構的本機:

資料夾 描述
C:\mediaRefresh 包含 PowerShell 腳本的父資料夾
C:\mediaRefresh\oldMedia 包含將重新整理之原始媒體的資料夾。 例如, 包含 Setup.exe 和 \sources資料夾。
C:\mediaRefresh\newMedia 將包含更新媒體的資料夾。 它會從 \oldMedia 複製,然後作為所有更新和清除作業的目標。

入門

腳本一開始會宣告全域變數,並建立要用於掛接影像的資料夾。 然後,製作原始媒體的複本,從 \oldMedia 到 \newMedia,在發生腳本錯誤時保留原始媒體,而且必須從已知狀態重新開始。 此外,它提供舊媒體與新媒體的比較,以評估變更。 若要確保新的媒體更新,請確定它們不是唯讀的。

#Requires -RunAsAdministrator

function Get-TS { return "{0:HH:mm:ss}" -f [DateTime]::Now }

Write-Output "$(Get-TS): Starting media refresh"

# Declare language for showcasing adding optional localized components
$LANG  = "ja-jp"
$LANG_FONT_CAPABILITY = "jpan"

# Declare media for FOD and LPs
# Note: Starting with Windows 11, version 21H2, the language pack (LANGPACK) ISO has been superseded by the FOD ISO.
# Language packs and the \Windows Preinstallation Environment packages are part of the LOF ISO.
# If you are using this script for Windows 10, modify to mount and use the LANGPACK ISO.
$FOD_ISO_PATH    = "C:\mediaRefresh\packages\FOD-PACKAGES_OEM_PT1_amd64fre_MULTI.iso"

# Declare Dynamic Update packages
$LCU_PATH        = "C:\mediaRefresh\packages\LCU.msu"
$SSU_PATH        = "C:\mediaRefresh\packages\SSU_DU.msu"
$SETUP_DU_PATH   = "C:\mediaRefresh\packages\Setup_DU.cab"
$SAFE_OS_DU_PATH = "C:\mediaRefresh\packages\SafeOS_DU.cab"
$DOTNET_CU_PATH  = "C:\mediaRefresh\packages\DotNet_CU.msu"

# Declare folders for mounted images and temp files
$MEDIA_OLD_PATH  = "C:\mediaRefresh\oldMedia"
$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"

# 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

# Note: Starting with Windows 11, version 21H2, the correct path for main OS language and optional features
# moved to \LanguagesAndOptionalFeatures instead of the root. For Windows 10, use $FOD_PATH = $FOD_ISO_DRIVE_LETTER + ":\"
$FOD_PATH = $FOD_ISO_DRIVE_LETTER + ":\LanguagesAndOptionalFeatures"

# 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"

# 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 會複製到工作資料夾並掛接。 然後,它會套用服務堆疊動態更新,因為其元件用於更新其他元件。 因為腳本是選擇性地新增日文,所以會將語言套件新增至映像,並安裝所有已安裝在 Winre.wim 中之選用套件的日文版本。 然後,它會套用安全OS動態更新套件。 其完成方式是清除和導出影像,以減少影像大小。

接下來,針對掛接的OS映射,腳本會從套用服務堆疊動態更新開始。 然後,它會新增日文語言支援,然後新增日文語言功能。 不同於動態更新套件,它會使用 Add-WindowsCapability 來新增這些功能。 如需這類功能及其相關聯功能名稱的完整清單,請參閱 可用的功能隨選。 現在可以啟用其他選擇性元件或新增其他功能隨選。 如果這類功能具有相關聯的累積更新 (例如 .NET) ,則這是套用這些更新的時間。 接著,腳本會繼續套用最新的累積更新。 最後,腳本會清除並匯出映射。 您可以離線安裝選擇性元件以及 .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 WinPE
$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)

        # Depending on the Windows release that you are updating, there are 2 different approaches for updating the servicing stack
        # The first approach is to use the combined cumulative update. This is for Windows releases that are shipping a combined 
        # cumulative update that includes the servicing stack updates (i.e. SSU + LCU are combined). Windows 11, version 21H2 and 
        # Windows 11, version 22H2 are examples. In these cases, the servicing stack update is not published seperately; the combined 
        # cumulative update should be used for this step. However, in hopefully rare cases, there may breaking change in the combined 
        # cumulative update format, that requires a standalone servicing stack update to be published, and installed first before the 
        # combined cumulative update can be installed. 

        # This is the code to handle the rare case that the SSU is published and required for the combined cumulative update
        # Write-Output "$(Get-TS): Adding package $SSU_PATH"
        # Add-WindowsPackage -Path $WINRE_MOUNT -PackagePath $SSU_PATH | Out-Null  

        # Now, attempt the combined cumulative update.
        # There is a known issue where the servicing stack update is installed, but the cumulative update will fail. This error should 
        # be caught and ignored, as the last step will be to apply the Safe OS update and thus the image will be left with the correct 
        # packages installed.

        try
        {
            Add-WindowsPackage -Path $WINRE_MOUNT -PackagePath $LCU_PATH | Out-Null  
        }
        Catch
        {
            $theError = $_
            Write-Output "$(Get-TS): $theError"
    
            if ($theError.Exception -like "*0x8007007e*") {
                Write-Output "$(Get-TS): This failure is a known issue with combined cumulative update, we can ignore."
            }
            else {
                throw
            }
        }

        # The second approach for Step 1 is for Windows releases that have not adopted the combined cumulative update
        # but instead continue to have a seperate servicing stack update published. In this case, we'll install the SSU
        # update. This second approach is commented out below.

        # Write-Output "$(Get-TS): Adding package $SSU_PATH"
        # Add-WindowsPackage -Path $WINRE_MOUNT -PackagePath $SSU_PATH | Out-Null  

        #
        # Optional: Add the language to recovery environment
        #
        # Install lp.cab cab
        Write-Output "$(Get-TS): Adding package $WINPE_OC_LP_PATH"
        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"
                        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"
            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"
                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"
                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"
        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

        # 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 18 from the table)

    # Depending on the Windows release that you are updating, there are 2 different approaches for updating the servicing stack
    # The first approach is to use the combined cumulative update. This is for Windows releases that are shipping a combined cumulative update that
    # includes the servicing stack updates (i.e. SSU + LCU are combined). Windows 11, version 21H2 and Windows 11, version 22H2 are examples. In these
    # cases, the servicing stack update is not published seperately; the combined cumulative update should be used for this step. However, in hopefully
    # rare cases, there may breaking change in the combined cumulative update format, that requires a standalone servicing stack update to be published, 
    # and installed first before the combined cumulative update can be installed. 

    # This is the code to handle the rare case that the SSU is published and required for the combined cumulative update
    # Write-Output "$(Get-TS): Adding package $SSU_PATH"
    # Add-WindowsPackage -Path $MAIN_OS_MOUNT -PackagePath $SSU_PATH | Out-Null  

    # Now, attempt the combined cumulative update. Unlike WinRE and WinPE, we don't need to check for error 0x8007007e
    Write-Output "$(Get-TS): Adding package $LCU_PATH"
    Add-WindowsPackage -Path $MAIN_OS_MOUNT -PackagePath $LCU_PATH | Out-Null  

    # The second approach for Step 18 is for Windows releases that have not adopted the combined cumulative update
    # but instead continue to have a seperate servicing stack update published. In this case, we'll install the SSU
    # update. This second approach is commented out below.

    # Write-Output "$(Get-TS): Adding package $SSU_PATH"
    # Add-WindowsPackage -Path $MAIN_OS_MOUNT -PackagePath $SSU_PATH | Out-Null  

    # Optional: Add language to main OS
    Write-Output "$(Get-TS): Adding package $OS_LP_PATH"
    Add-WindowsPackage -Path $MAIN_OS_MOUNT -PackagePath $OS_LP_PATH -ErrorAction stop | Out-Null  

    # Optional: Add a Features on Demand to the image
    Write-Output "$(Get-TS): Adding language FOD: Language.Fonts.Jpan~~~und-JPAN~0.0.1.0"
    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"
    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"
    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"
    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"
    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"
    Add-WindowsCapability -Name "Language.Speech~~~$LANG~0.0.1.0" -Path $MAIN_OS_MOUNT -Source $FOD_PATH -ErrorAction stop | Out-Null

    # Note: If I wanted to enable additional Features on Demand, I'd add these here.

    # Add latest cumulative update
    Write-Output "$(Get-TS): Adding package $LCU_PATH"
    Add-WindowsPackage -Path $MAIN_OS_MOUNT -PackagePath $LCU_PATH -ErrorAction stop | Out-Null

    # Perform image cleanup
    Write-Output "$(Get-TS): Performing image cleanup on main OS"
    DISM /image:$MAIN_OS_MOUNT /cleanup-image /StartComponentCleanup | Out-Null

    #
    # Note: If I wanted to enable additional Optional Components, I'd add these here.
    # In addition, we'll add .NET 3.5 here as well. Both .NET and Optional Components might require
    # the image to be booted, and thus if we tried to cleanup after installation, it would fail.
    #

    Write-Output "$(Get-TS): Adding NetFX3~~~~"
    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"
    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 以供稍後使用,以確保此版本符合安裝媒體 \sources\setup.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)

    # Depending on the Windows release that you are updating, there are 2 different approaches for updating the servicing stack
    # The first approach is to use the combined cumulative update. This is for Windows releases that are shipping a combined 
    # cumulative update that includes the servicing stack updates (i.e. SSU + LCU are combined). Windows 11, version 21H2 and 
    # Windows 11, version 22H2 are examples. In these cases, the servicing stack update is not published separately; the combined 
    # cumulative update should be used for this step. However, in hopefully rare cases, there may breaking change in the combined 
    # cumulative update format, that requires a standalone servicing stack update to be published, and installed first before the 
    # combined cumulative update can be installed. 

    # This is the code to handle the rare case that the SSU is published and required for the combined cumulative update
    # Write-Output "$(Get-TS): Adding package $SSU_PATH"
    # Add-WindowsPackage -Path $WINPE_MOUNT -PackagePath $SSU_PATH | Out-Null  

    # Now, attempt the combined cumulative update.
    # There is a known issue where the servicing stack update is installed, but the cumulative update will fail.
    # This error should be caught and ignored, as the last step will be to apply the cumulative update 
    # (or in this case the combined cumulative update) and thus the image will be left with the correct packages installed.

    try
    {
        Add-WindowsPackage -Path $WINPE_MOUNT -PackagePath $LCU_PATH | Out-Null  
    }
    Catch
    {
        $theError = $_
        Write-Output "$(Get-TS): $theError"

        if ($theError.Exception -like "*0x8007007e*") {
            Write-Output "$(Get-TS): This failure is a known issue with combined cumulative update, we can ignore."
        }
        else {
            throw
        }
    }

    # The second approach for Step 9 is for Windows releases that have not adopted the combined cumulative update
    # but instead continue to have a separate servicing stack update published. In this case, we'll install the SSU
    # update. This second approach is commented out below.

    # Write-Output "$(Get-TS): Adding package $SSU_PATH"
    # Add-WindowsPackage -Path $WINPE_MOUNT -PackagePath $SSU_PATH | Out-Null 

    # Install lp.cab cab
    Write-Output "$(Get-TS): Adding package $WINPE_OC_LP_PATH"
    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"
                    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"
        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"
            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"
            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
    }

    # Add latest cumulative update
    Write-Output "$(Get-TS): Adding package $LCU_PATH"
    Add-WindowsPackage -Path $WINPE_MOUNT -PackagePath $LCU_PATH -ErrorAction stop | Out-Null  

    # Perform image cleanup
    Write-Output "$(Get-TS): Performing image cleanup on WinPE"
    DISM /image:$WINPE_MOUNT /cleanup-image /StartComponentCleanup /ResetBase /Defer | Out-Null

    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 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 和開機管理員檔案。

#
# 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

# 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 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!"