mirror of
https://github.com/ChrisTitusTech/winutil.git
synced 2026-10-08 11:39:10 +11:00
Compare commits
3
Commits
ad81e0a7af
...
48885d7c1e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48885d7c1e | ||
|
|
abcbc23144 | ||
|
|
311fde6f31 |
@@ -30,7 +30,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install uv and Python
|
||||
uses: astral-sh/setup-uv@v10.1.0
|
||||
uses: astral-sh/setup-uv@v10.2.0
|
||||
with:
|
||||
version: "0.12.0"
|
||||
python-version: "3.13"
|
||||
|
||||
@@ -183,7 +183,7 @@ When the user corrects an agent approach, add or tighten one concrete rule here
|
||||
- Have each Pester file load the assemblies and dot-source the functions it needs; several passed only because an earlier file in alphabetical order happened to load them.
|
||||
- Log install/uninstall package names and package-manager IDs before queuing background runspace work; do not rely on runspace host output for the package identity.
|
||||
- For Win11 Creator, start each new ISO modification in a fresh `WinUtil_Win11ISO_*` temp directory; existing-work detection is only for resuming/exporting already modified media.
|
||||
- For Win11 Creator driver injection, keep offline WIM servicing to one mount and one commit: add each root package folder with its own `/Add-Driver /Recurse` so a single bad driver cannot fail the rest, and skip any folder whose ancestor is already in the set, since that ancestor's `/Recurse` covers it. Warn per failure and commit only when at least one package was added; when none were, warn and discard rather than throwing, so the run still produces an ISO. The discard in the cleanup block carries both orphaned mounts and that intentional zero-added case; keep it. Do not export editions or run unrelated WIM cleanup, and reject damaged metadata before ISO export. Use `-LiteralPath` for driver export paths, since `%TEMP%` can contain wildcard characters.
|
||||
- For Win11 Creator driver injection, inject storage controllers (`SCSIAdapter` / `HDC`) into `boot.wim` index 2 with DISM, not `$WinpeDriver$`. Keep `install.wim` on its own per-package `/Add-Driver` retry loop. For each image, add each root package folder with its own `/Add-Driver /Recurse` so a single bad driver cannot fail the rest, and skip any folder whose ancestor is already in the set, since that ancestor's `/Recurse` covers it. Warn per failure and commit only when at least one package was added; when none were, warn and discard rather than throwing, so the run still produces an ISO. The discard in the cleanup block carries both orphaned mounts and that intentional zero-added case; keep it. Do not export editions or run unrelated WIM cleanup, and reject damaged metadata before ISO export. Use `-LiteralPath` for driver export paths, since `%TEMP%` can contain wildcard characters.
|
||||
- For Script Analyzer cleanup, fix actionable source warnings first and do not globally suppress accepted convention warnings such as plural names, `ShouldProcess` on UI helpers, `$global:sync`, or compile-time cross-file false positives.
|
||||
- For DNS DHCP reset, keep the cmdlet reset and explicitly set IPv4 and IPv6 DNS source to DHCP.
|
||||
- Public pull-request diffs may be sent to configured external review services without a separate privacy approval; do not block the review loop on upload authorization for this public repository.
|
||||
|
||||
@@ -135,21 +135,31 @@ The **Win11 Creator** is a specialized subsystem within Winutil that creates cus
|
||||
### Win11 Creator Components
|
||||
|
||||
**Core Functions** (`functions/private/`):
|
||||
- `Invoke-WinUtilISO.ps1`: Main orchestrator containing all Win11 Creator functions
|
||||
- `Invoke-WinUtilISO.ps1`: Main orchestrator containing ISO workflow and helper functions
|
||||
- `Invoke-WinUtilISOBrowse`: ISO file selection dialog
|
||||
- `Invoke-WinUtilISOMountAndVerify`: Validates and mounts ISO, verifies it is an official Windows 11 ISO
|
||||
- `Invoke-WinUtilISOModify`: Launches modification in background runspace
|
||||
- `Invoke-WinUtilISOExport`: Handles ISO and USB export
|
||||
- `Invoke-WinUtilISOExport`: Builds bootable ISO via `oscdimg.exe`
|
||||
- `Invoke-WinUtilISOCheckExistingWork`: Recovers incomplete work sessions
|
||||
- `Invoke-WinUtilISOCleanAndReset`: Cleans up temp directories and resets UI
|
||||
- `Write-WinUtilISOLog`: Posts log messages to UI status box and session log
|
||||
- `Set-WinUtilISOStep`: Controls wizard navigation state
|
||||
- `Get-WinUtilEditionIdFromName`: Maps edition display names to setup edition IDs
|
||||
- `Invoke-WinUtilRobocopy`: Copies files with exit code verification
|
||||
- `Find-WinUtilOscdimg` / `Get-WinUtilOscdimgPath`: Locates or installs `oscdimg.exe`
|
||||
|
||||
- `Invoke-WinUtilISOScript.ps1`: Applies modifications to mounted install.wim
|
||||
- Removes provisioned AppX packages (40+ bloatware apps)
|
||||
- Injects drivers (optional) from the current system
|
||||
- Removes OneDrive setup files
|
||||
- Applies offline registry tweaks (hardware bypass, privacy, telemetry, OOBE)
|
||||
- Deletes telemetry scheduled task definitions
|
||||
- Pre-stages setup scripts from autounattend.xml
|
||||
- `Invoke-WinUtilISOUSB.ps1`: USB drive detection and formatting
|
||||
- `Invoke-WinUtilISORefreshUSBDrives`: Enumerates USB drives
|
||||
- `Get-WinUtilFreeDriveLetter`: Finds available drive letters
|
||||
- `Invoke-WinUtilISOWriteUSB`: Formats USB drive as GPT/FAT32, splits WIM if needed, and copies files
|
||||
|
||||
- `Invoke-WinUtilISOScript.ps1`: Prepares setup media and customizations
|
||||
- Stages AppX removal (19 bloatware packages) into `autounattend.xml` for first logon
|
||||
- Applies 50+ registry tweaks during Windows Setup and first logon; removes scheduled tasks at first logon
|
||||
- Triggers OneDrive uninstall during first logon
|
||||
- Stages setup script fallbacks to `sources\$OEM$\$$\Setup\Scripts\`
|
||||
- Pins selected edition in `autounattend.xml` and writes `sources\ei.cfg`
|
||||
- Injects eligible current system drivers into WIM images if enabled (WIM mount is only used for driver servicing)
|
||||
|
||||
### Win11 Creator Data Flow
|
||||
|
||||
@@ -169,18 +179,19 @@ User optionally enables the Driver Injection checkbox
|
||||
↓
|
||||
Invoke-WinUtilISOModify (runs in background runspace)
|
||||
├─ Create work directory: ~WinUtil_Win11ISO_[timestamp]
|
||||
├─ Copy ISO contents to disk (~5-6 GB)
|
||||
├─ Mount install.wim at selected edition/index
|
||||
├─ Copy ISO contents to disk via robocopy (~5-6 GB)
|
||||
├─ Invoke-WinUtilISOScript:
|
||||
│ ├─ Remove 40+ bloat AppX packages
|
||||
│ ├─ Export and inject drivers (if enabled)
|
||||
│ ├─ Remove OneDrive setup
|
||||
│ ├─ Load offline registry hives
|
||||
│ ├─ Apply 50+ registry tweaks (hardware bypass, privacy, telemetry, OOBE, etc.)
|
||||
│ ├─ Delete telemetry scheduled task files
|
||||
│ ├─ Pre-stage setup scripts from autounattend.xml to C:\Windows\Setup\Scripts\
|
||||
│ └─ Unload registry hives
|
||||
├─ Dismount and save the modified install.wim (~10+ minutes, slowest step)
|
||||
│ ├─ Generate autounattend.xml with Windows PE and specialize safeguards
|
||||
│ ├─ Add first-logon script (19 AppX removals, registry tweaks, OneDrive uninstall)
|
||||
│ ├─ Pin selected edition index in autounattend.xml (/IMAGE/INDEX)
|
||||
│ ├─ Stage setup script fallbacks to sources\$OEM$\$$\Setup\Scripts\
|
||||
│ ├─ Write sources\ei.cfg and remove stale sources\PID.txt
|
||||
│ └─ If driver injection enabled:
|
||||
│ ├─ Require install.wim; install.esd cannot accept driver injection
|
||||
│ ├─ Export current system drivers via DISM
|
||||
│ ├─ Exclude stale duplicate packages
|
||||
│ ├─ Inject storage drivers into boot.wim index 2 and eligible drivers into install.wim
|
||||
│ └─ Validate WIM metadata before and after injection
|
||||
├─ Dismount source ISO
|
||||
└─ Report completion, enable export options
|
||||
↓
|
||||
@@ -191,12 +202,15 @@ Invoke-WinUtilISOExport (user chooses output)
|
||||
│
|
||||
└─ Option 2: Write to USB
|
||||
├─ Format USB as GPT
|
||||
├─ Create 512 MB EFI partition
|
||||
├─ Copy modified ISO contents
|
||||
├─ Create single FAT32 partition (capped at 32 GB)
|
||||
├─ Split install.wim into .swm files if > 3.8 GB
|
||||
├─ Reject install.esd files of 4 GB or more
|
||||
├─ Copy files via robocopy
|
||||
└─ Output: Bootable USB (minimum 8 GB)
|
||||
↓
|
||||
Invoke-WinUtilISOCleanAndReset (optional)
|
||||
└─ Delete temp working directory (~10-15 GB)
|
||||
├─ Dismount any open WIM mounts with discard
|
||||
├─ Delete temp working directory (~10-15 GB)
|
||||
└─ Reset UI to initial state
|
||||
```
|
||||
|
||||
@@ -208,20 +222,30 @@ Invoke-WinUtilISOCleanAndReset (optional)
|
||||
- Checks image metadata for "Windows 11" string
|
||||
- Rejects custom, modified, or non-Windows 11 ISOs
|
||||
|
||||
**WIM Metadata Validation**:
|
||||
- During driver injection, `Assert-WinUtilISOWimMetadata` validates critical fields (`Languages`, `Installation`, `Edition`, `ProductSuite`, `ProductType`) before and after WIM servicing
|
||||
- Driver injection stops before export if the required metadata is missing or changes
|
||||
- Without driver injection, WinUtil preserves the original installation image and skips WIM metadata validation
|
||||
|
||||
**Edition Pinning & Setup Fallback**:
|
||||
- Writes `sources\ei.cfg` and removes `sources\PID.txt` so setup does not use mismatched OEM product keys
|
||||
- Pins the selected edition index in `autounattend.xml` (`/IMAGE/INDEX`)
|
||||
- Stages fallback setup scripts under `sources\$OEM$\$$\Setup\Scripts\` with `UseConfigurationSet` enabled
|
||||
|
||||
**Work Session Recovery**:
|
||||
- Auto-detects incomplete work from previous sessions
|
||||
- Allows resuming the export step without re-running selection and modification
|
||||
- Prevents redundant modifications
|
||||
|
||||
**Modification Safety**:
|
||||
- All registry changes are documented in a script (reversible)
|
||||
- Windows PE and specialize set hardware bypass and setup safeguards; `WinUtil-PostInstall.ps1` applies additional registry changes at first logon
|
||||
- Original ISO never modified; only working copy
|
||||
- Logged to `WinUtil_Win11ISO.log` for debugging
|
||||
- DISM handles image dismount with automatic cleanup on error
|
||||
- Logged to the WinUtil session log and the live UI status panel
|
||||
- DISM handles image dismount with automatic cleanup and discard on error
|
||||
|
||||
### Win11 Creator Registry Tweaks
|
||||
|
||||
The `Invoke-WinUtilISOScript` function applies **50+ offline registry tweaks**:
|
||||
The `Invoke-WinUtilISOScript` function applies **50+ registry tweaks** during setup and first logon:
|
||||
|
||||
**Hardware Bypass**:
|
||||
- TPM 2.0 check bypass
|
||||
@@ -259,9 +283,9 @@ The `Invoke-WinUtilISOScript` function applies **50+ offline registry tweaks**:
|
||||
|
||||
### Driver Injection Feature
|
||||
|
||||
**Optional Enhancement**: When enabled, exports all drivers from the running system and injects them into both:
|
||||
- `install.wim` (main OS image)
|
||||
- `boot.wim` index 2 (Windows Setup PE environment)
|
||||
**Optional Enhancement**: When enabled, exports all drivers from the running system and injects them into:
|
||||
- `install.wim` (main OS image), excluding stale duplicates
|
||||
- `boot.wim` index 2 (Windows Setup PE environment), storage controllers only (`SCSIAdapter` / `HDC`)
|
||||
|
||||
**Use Case**: Enables offline installation on systems with missing drivers.
|
||||
|
||||
|
||||
@@ -45,8 +45,8 @@ Once the ISO is verified, WinUtil moves to this step and shows the mounted drive
|
||||
Then click **Run Windows ISO Modification and Creator** to start the customization process. WinUtil will:
|
||||
|
||||
**App & Component Removal:**
|
||||
- **Remove 40+ bloat apps** — Clipchamp, Teams, Copilot, Dev Home, new Outlook, Bing apps, Solitaire, and more
|
||||
- **Delete OneDrive setup** from the image
|
||||
- **Remove 19 bloat apps** — Clipchamp, Teams, Dev Home, new Outlook, Bing apps, Solitaire, and more
|
||||
- **Uninstall OneDrive** during first logon
|
||||
|
||||
**System Customization:**
|
||||
- **Bypass hardware checks** — removes TPM, Secure Boot, CPU, RAM, and storage requirement enforcement so the ISO installs on unsupported hardware
|
||||
@@ -67,9 +67,13 @@ Then click **Run Windows ISO Modification and Creator** to start the customizati
|
||||
- **Disable Copilot and search box suggestions**
|
||||
|
||||
**Optional: Driver Injection**
|
||||
- If enabled, WinUtil exports the drivers from your current system, stages boot-storage drivers for Windows Setup, and injects eligible packages into the selected `install.wim` image. Stale duplicate packages are excluded before injection. Each remaining package is added separately, so one incompatible package does not stop the others. If a package fails, WinUtil discards the partial mount and retries the remaining packages against the original image. Check the live log for warnings: if every package fails, WinUtil keeps the original `install.wim` and still produces the ISO.
|
||||
- If enabled, WinUtil exports the drivers from your current system, injects boot-storage drivers into `boot.wim` (Windows Setup, index 2), and injects eligible packages into the selected `install.wim` image. Stale duplicate packages are excluded before injection. Each remaining package is added separately, so one incompatible package does not stop the others. If a package fails, WinUtil discards the partial mount and retries the remaining packages against the original image. Check the live log for warnings: if every package fails, WinUtil keeps the original `install.wim` and still produces the ISO.
|
||||
|
||||
A live log shows progress as each step completes. This stage usually takes **10–30 minutes** depending on disk speed. The WIM dismount near the end is the slowest part, so do not close WinUtil while it is running.
|
||||
:::note
|
||||
Driver injection requires `sources\install.wim`. If the ISO uses `sources\install.esd`, leave this option off.
|
||||
:::
|
||||
|
||||
A live log shows progress as each step completes. Without driver injection, WinUtil writes the setup changes in a few seconds after the ISO copy. With driver injection, this stage usually takes **10–20 minutes**, depending on hardware. Keep WinUtil open until the log reports completion.
|
||||
|
||||
:::note
|
||||
The resulting ISO is close to the size of the source ISO. WinUtil does not remove the unused editions from `install.wim`; it selects your edition through `sources\ei.cfg` and `autounattend.xml` so Windows Setup installs the right one.
|
||||
@@ -95,7 +99,11 @@ Once the modification is complete, choose how to save your image:
|
||||
1. Click **Write Directly to a USB Drive**.
|
||||
2. Select your USB drive from the dropdown (click **Refresh** if it doesn't appear).
|
||||
3. Click **Erase & Write to USB** and confirm the warning — **all data on the drive will be permanently erased**.
|
||||
4. WinUtil formats the drive as GPT with a 512 MB EFI partition and copies the modified Windows files.
|
||||
4. WinUtil formats the drive as GPT with a single FAT32 partition (capped at 32 GB, splitting `install.wim` into `.swm` files if larger than 3.8 GB) and copies the modified Windows files.
|
||||
|
||||
:::note
|
||||
WinUtil cannot split `install.esd`. If that file is 4 GB or larger, save an ISO instead of writing a FAT32 USB drive.
|
||||
:::
|
||||
|
||||
:::danger
|
||||
Double-check you have selected the correct drive before confirming. This operation cannot be undone.
|
||||
@@ -130,10 +138,10 @@ When you install Windows 11 from your modified ISO:
|
||||
|
||||
| Problem | Fix |
|
||||
|---------|-----|
|
||||
| "install.wim not found" | Not a valid Windows 11 ISO — download a fresh one from Microsoft |
|
||||
| "install.wim / install.esd was not found" | The ISO has no Windows installation image — download a fresh official ISO from Microsoft |
|
||||
| "oscdimg.exe not found" | Run `winget install -e --id Microsoft.OSCDIMG` then retry |
|
||||
| USB drive not showing up | Plug it in, wait a few seconds, then click **Refresh** |
|
||||
| Modification seems stuck | The WIM dismount step is slow — wait at least 10 minutes before assuming it's frozen |
|
||||
| Driver injection seems stuck | WIM servicing can pause at a mount or commit. Allow 10–20 minutes depending on hardware, and check the live log before closing WinUtil |
|
||||
| "Access Denied" error | Make sure WinUtil is running as Administrator |
|
||||
| Driver injection warning | Review the live log for the named package. Other compatible packages continue; if none succeed, the ISO is created with the original `install.wim` |
|
||||
| "Setup has failed to validate the product key" | Recreate the ISO with the latest WinUtil. The creator now removes stale `PID.txt`, writes `sources\ei.cfg`, and pins the selected image in `autounattend.xml` so setup does not use an embedded OEM key for a different edition |
|
||||
|
||||
@@ -401,7 +401,7 @@ function Invoke-WinUtilISOModify {
|
||||
|
||||
if ($driversInjected.Value) {
|
||||
Step-WinUtilJob -Status "Finalizing install image..." -Percent 70
|
||||
Write-WinUtilISOLog "Added current-system drivers to $sourceImageFileName index $SelectedWimIndex with one mount and commit."
|
||||
Write-WinUtilISOLog "Added current-system drivers to $sourceImageFileName index $SelectedWimIndex."
|
||||
} elseif ($InjectDrivers) {
|
||||
Step-WinUtilJob -Status "Preserving install image..." -Percent 70
|
||||
Write-WinUtilISOLog "No current-system drivers were injected into $sourceImageFileName index $SelectedWimIndex; install.wim was left unchanged. Review the warning log entries for details."
|
||||
|
||||
@@ -6,7 +6,8 @@ function Invoke-WinUtilISOScript {
|
||||
.DESCRIPTION
|
||||
Stages WinUtil's AppX removal, registry tweaks, and scheduled-task cleanup
|
||||
in the answer file for first logon, writes sources\ei.cfg for the selected
|
||||
edition, and optionally adds current-system drivers to one install.wim index.
|
||||
edition, and optionally adds current-system drivers to boot.wim index 2 and
|
||||
one install.wim index.
|
||||
|
||||
.PARAMETER ISOContentsDir
|
||||
Root directory of the copied ISO contents.
|
||||
@@ -51,31 +52,9 @@ function Invoke-WinUtilISOScript {
|
||||
)
|
||||
$DriversInjected.Value = $false
|
||||
|
||||
function Copy-WinUtilISODriverFolder {
|
||||
param (
|
||||
[Parameter(Mandatory)][string]$Source,
|
||||
[Parameter(Mandatory)][string]$Destination
|
||||
)
|
||||
|
||||
$folderName = Split-Path $Source -Leaf
|
||||
$targetPath = Join-Path $Destination $folderName
|
||||
$suffix = 1
|
||||
while (Test-Path -LiteralPath $targetPath) {
|
||||
$targetPath = Join-Path $Destination "${folderName}_$suffix"
|
||||
$suffix++
|
||||
}
|
||||
|
||||
Copy-Item -LiteralPath $Source -Destination $targetPath -Recurse -Force -ErrorAction Stop
|
||||
return $targetPath
|
||||
}
|
||||
|
||||
function Test-WinUtilISOStorageDriver {
|
||||
param ([Parameter(Mandatory)][System.IO.FileInfo]$InfFile)
|
||||
|
||||
if ($InfFile.BaseName -match '(?i)(iaahci|iastor|vmd|irst|rst)') {
|
||||
return $true
|
||||
}
|
||||
|
||||
try {
|
||||
return (Get-Content -LiteralPath $InfFile.FullName -Raw -ErrorAction Stop) -match '(?im)^\s*Class\s*=\s*(SCSIAdapter|HDC)\s*(?:;.*)?$'
|
||||
} catch {
|
||||
@@ -267,6 +246,93 @@ function Invoke-WinUtilISOScript {
|
||||
return @(& dism.exe /English /Get-MountedImageInfo 2>$null) -match [regex]::Escape($Path)
|
||||
}
|
||||
|
||||
function Get-WinUtilISODriverFolderName {
|
||||
param ([Parameter(Mandatory)][string]$DriverFolder)
|
||||
|
||||
if ($DriverFolder.StartsWith($driverExportRoot, [System.StringComparison]::OrdinalIgnoreCase)) {
|
||||
return $DriverFolder.Substring($driverExportRoot.Length).TrimStart('\')
|
||||
}
|
||||
return $DriverFolder
|
||||
}
|
||||
|
||||
function Get-WinUtilISORootDriverFolders {
|
||||
param ([Parameter(Mandatory)][AllowEmptyCollection()][string[]]$DriverFolders)
|
||||
|
||||
return @($DriverFolders | Where-Object {
|
||||
$candidate = $_
|
||||
-not ($DriverFolders | Where-Object { $candidate.StartsWith("$_\", [System.StringComparison]::OrdinalIgnoreCase) })
|
||||
})
|
||||
}
|
||||
|
||||
function Add-WinUtilISODriversToImage {
|
||||
param (
|
||||
[Parameter(Mandatory)][string]$ImagePath,
|
||||
[Parameter(Mandatory)][int]$ImageIndex,
|
||||
[Parameter(Mandatory)][string]$MountDir,
|
||||
[Parameter(Mandatory)][AllowEmptyCollection()][string[]]$DriverFolders,
|
||||
[Parameter(Mandatory)][string]$ImageLabel,
|
||||
[Parameter(Mandatory)][ref]$ImageMounted
|
||||
)
|
||||
|
||||
if ($DriverFolders.Count -eq 0) {
|
||||
& $Logger "No driver packages to add to ${ImageLabel}."
|
||||
return 0
|
||||
}
|
||||
|
||||
Set-ItemProperty -LiteralPath $ImagePath -Name IsReadOnly -Value $false
|
||||
New-Item -Path $MountDir -ItemType Directory -Force | Out-Null
|
||||
|
||||
$remainingDriverFolders = @($DriverFolders)
|
||||
while ($remainingDriverFolders.Count -gt 0) {
|
||||
& $Logger "Mounting $ImageLabel index $ImageIndex for driver injection..."
|
||||
Invoke-WinUtilISODism -Arguments @('/English', '/Mount-Image', "/ImageFile:$ImagePath", "/Index:$ImageIndex", "/MountDir:$MountDir") -Operation 'mount' | Out-Null
|
||||
$ImageMounted.Value = $true
|
||||
|
||||
$failedDriverFolder = $null
|
||||
$driverName = $null
|
||||
foreach ($driverFolder in $remainingDriverFolders) {
|
||||
$driverName = Get-WinUtilISODriverFolderName -DriverFolder $driverFolder
|
||||
try {
|
||||
Invoke-WinUtilISODism -Arguments @('/English', "/Image:$MountDir", '/Add-Driver', "/Driver:$driverFolder", '/Recurse') -Operation "add-driver:$driverName" | Out-Null
|
||||
} catch {
|
||||
& $Logger "Warning: failed to add driver package '$driverName': $_"
|
||||
$failedDriverFolder = $driverFolder
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $failedDriverFolder) {
|
||||
break
|
||||
}
|
||||
|
||||
& $Logger "Discarding the potentially partial $ImageLabel mount before continuing without '$driverName'."
|
||||
try {
|
||||
Invoke-WinUtilISODism -Arguments @('/English', '/Unmount-Image', "/MountDir:$MountDir", '/Discard') -Operation 'discard' | Out-Null
|
||||
$ImageMounted.Value = $false
|
||||
} catch {
|
||||
throw "Failed to discard the potentially partial $ImageLabel mount after driver package '$driverName' failed: $_"
|
||||
}
|
||||
|
||||
$remainingDriverFolders = @($remainingDriverFolders | Where-Object { $_ -ne $failedDriverFolder })
|
||||
}
|
||||
|
||||
$addedCount = $remainingDriverFolders.Count
|
||||
if ($addedCount -eq 0) {
|
||||
if ($ImageLabel -eq 'install.wim') {
|
||||
& $Logger "Warning: none of the $($DriverFolders.Count) exported driver packages could be added; continuing with an unmodified install.wim."
|
||||
} else {
|
||||
& $Logger "Warning: none of the $($DriverFolders.Count) driver packages could be added to ${ImageLabel}."
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
& $Logger "Added $addedCount of $($DriverFolders.Count) driver packages to ${ImageLabel}."
|
||||
& $Logger "Committing the driver-only $ImageLabel change..."
|
||||
Invoke-WinUtilISODism -Arguments @('/English', '/Unmount-Image', "/MountDir:$MountDir", '/Commit') -Operation 'commit' | Out-Null
|
||||
$ImageMounted.Value = $false
|
||||
return $addedCount
|
||||
}
|
||||
|
||||
if ([IO.Path]::GetExtension($InstallImagePath) -ne '.wim') {
|
||||
throw 'Current-system driver injection requires install.wim; install.esd cannot be serviced in place.'
|
||||
}
|
||||
@@ -287,7 +353,7 @@ function Invoke-WinUtilISOScript {
|
||||
$imageMounted = $false
|
||||
|
||||
try {
|
||||
& $Logger "Exporting current system drivers before modifying install.wim..."
|
||||
& $Logger "Exporting current system drivers before WIM driver injection..."
|
||||
$dismLog = Join-Path $env:TEMP "WinUtil_DismDriverExport_$(Get-Date -Format 'yyyyMMdd_HHmmss').log"
|
||||
Invoke-WinUtilISODism -Arguments @('/English', '/Online', '/Export-Driver', "/Destination:$driverExportRoot", "/LogPath:$dismLog") -Operation 'export-driver' | Out-Null
|
||||
|
||||
@@ -296,32 +362,6 @@ function Invoke-WinUtilISOScript {
|
||||
throw 'DISM exported no driver INF files.'
|
||||
}
|
||||
$driverFolders = @($driverInfs | Group-Object { $_.Directory.FullName })
|
||||
$winpeDriverDir = Join-Path $ContentRoot '$WinpeDriver$'
|
||||
$storageCount = 0
|
||||
$copyFailures = 0
|
||||
|
||||
foreach ($driverFolderGroup in $driverFolders) {
|
||||
$driverFolder = [string]$driverFolderGroup.Name
|
||||
$storageInfs = @($driverFolderGroup.Group | Where-Object { Test-WinUtilISOStorageDriver -InfFile $_ })
|
||||
if ($storageInfs.Count -eq 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
try {
|
||||
New-Item -Path $winpeDriverDir -ItemType Directory -Force | Out-Null
|
||||
$winpeTarget = Copy-WinUtilISODriverFolder -Source $driverFolder -Destination $winpeDriverDir
|
||||
$storageCount++
|
||||
& $Logger "Staged boot-storage package '$driverFolder' for WinPE as '$winpeTarget'."
|
||||
} catch {
|
||||
$copyFailures++
|
||||
& $Logger "Warning: failed to stage boot-storage package '$driverFolder': $_"
|
||||
}
|
||||
}
|
||||
|
||||
if ($copyFailures -gt 0) {
|
||||
throw "Failed to stage $copyFailures boot-storage driver package folders."
|
||||
}
|
||||
|
||||
$stagedDriverFolders = @(Select-WinUtilISOStagedDriverPackages -DriverFolderGroups $driverFolders -Logger $Logger)
|
||||
$metadataBefore = Get-WinUtilISOWimMetadata -ImagePath $InstallImagePath -Index $InstallImageIndex
|
||||
Assert-WinUtilISOWimMetadata -Before $metadataBefore
|
||||
@@ -352,67 +392,33 @@ function Invoke-WinUtilISOScript {
|
||||
}
|
||||
}
|
||||
|
||||
& $Logger "Exported $($stagedDriverFolders.Count) of $($driverFolders.Count) driver packages ($storageCount staged for WinPE, $($excludedDriverFolderGroups.Count) excluded)."
|
||||
& $Logger "Exported $($stagedDriverFolders.Count) of $($driverFolders.Count) driver packages ($($excludedDriverFolderGroups.Count) excluded)."
|
||||
|
||||
Set-ItemProperty -LiteralPath $InstallImagePath -Name IsReadOnly -Value $false
|
||||
New-Item -Path $mountDir -ItemType Directory -Force | Out-Null
|
||||
# Storage for Setup comes from the same surviving set as install.wim, so stale
|
||||
# duplicates never reach boot.wim.
|
||||
$storageFolders = @(
|
||||
$driverFolders |
|
||||
Where-Object { $_.Name -in $stagedDriverFolders } |
|
||||
Where-Object { @($_.Group | Where-Object { Test-WinUtilISOStorageDriver -InfFile $_ }).Count -gt 0 } |
|
||||
ForEach-Object { [string]$_.Name }
|
||||
)
|
||||
$storageRootFolders = @(Get-WinUtilISORootDriverFolders -DriverFolders $storageFolders)
|
||||
$rootPackageFolders = @(Get-WinUtilISORootDriverFolders -DriverFolders $stagedDriverFolders)
|
||||
$imageMountedRef = [ref]$imageMounted
|
||||
|
||||
# Add each package separately so one bad driver cannot fail the rest. Because
|
||||
# /Recurse covers descendants, only the highest surviving folder in each tree
|
||||
# needs its own DISM call.
|
||||
$rootPackageFolders = @($stagedDriverFolders | Where-Object {
|
||||
$candidate = $_
|
||||
-not ($stagedDriverFolders | Where-Object { $candidate.StartsWith("$_\", [System.StringComparison]::OrdinalIgnoreCase) })
|
||||
})
|
||||
|
||||
& $Logger "Adding $($rootPackageFolders.Count) root driver packages to install.wim."
|
||||
$remainingDriverFolders = @($rootPackageFolders)
|
||||
while ($remainingDriverFolders.Count -gt 0) {
|
||||
& $Logger "Mounting install.wim index $InstallImageIndex for driver injection..."
|
||||
Invoke-WinUtilISODism -Arguments @('/English', '/Mount-Image', "/ImageFile:$InstallImagePath", "/Index:$InstallImageIndex", "/MountDir:$mountDir") -Operation 'mount' | Out-Null
|
||||
$imageMounted = $true
|
||||
|
||||
$failedDriverFolder = $null
|
||||
foreach ($driverFolder in $remainingDriverFolders) {
|
||||
$driverName = $driverFolder
|
||||
if ($driverFolder.StartsWith($driverExportRoot, [System.StringComparison]::OrdinalIgnoreCase)) {
|
||||
$driverName = $driverFolder.Substring($driverExportRoot.Length).TrimStart('\')
|
||||
}
|
||||
|
||||
try {
|
||||
Invoke-WinUtilISODism -Arguments @('/English', "/Image:$mountDir", '/Add-Driver', "/Driver:$driverFolder", '/Recurse') -Operation "add-driver:$driverName" | Out-Null
|
||||
} catch {
|
||||
& $Logger "Warning: failed to add driver package '$driverName': $_"
|
||||
$failedDriverFolder = $driverFolder
|
||||
break
|
||||
}
|
||||
$bootWim = Join-Path $ContentRoot 'sources\boot.wim'
|
||||
if ($storageRootFolders.Count -gt 0) {
|
||||
if (Test-Path -LiteralPath $bootWim) {
|
||||
& $Logger "Adding $($storageRootFolders.Count) root storage driver packages to boot.wim."
|
||||
$null = Add-WinUtilISODriversToImage -ImagePath $bootWim -ImageIndex 2 -MountDir $mountDir -DriverFolders $storageRootFolders -ImageLabel 'boot.wim' -ImageMounted $imageMountedRef
|
||||
} else {
|
||||
& $Logger 'Warning: boot.wim was not found; Windows Setup will not have injected storage drivers.'
|
||||
}
|
||||
|
||||
if (-not $failedDriverFolder) {
|
||||
break
|
||||
}
|
||||
|
||||
& $Logger "Discarding the potentially partial install.wim mount before continuing without '$driverName'."
|
||||
try {
|
||||
Invoke-WinUtilISODism -Arguments @('/English', '/Unmount-Image', "/MountDir:$mountDir", '/Discard') -Operation 'discard' | Out-Null
|
||||
$imageMounted = $false
|
||||
} catch {
|
||||
throw "Failed to discard the potentially partial install.wim mount after driver package '$driverName' failed: $_"
|
||||
}
|
||||
|
||||
$remainingDriverFolders = @($remainingDriverFolders | Where-Object { $_ -ne $failedDriverFolder })
|
||||
}
|
||||
|
||||
$addedCount = $remainingDriverFolders.Count
|
||||
if ($addedCount -eq 0) {
|
||||
# Boot-storage drivers staged for WinPE remain available to Windows Setup.
|
||||
& $Logger "Warning: none of the $($rootPackageFolders.Count) exported driver packages could be added; continuing with an unmodified install.wim."
|
||||
} else {
|
||||
& $Logger "Added $addedCount of $($rootPackageFolders.Count) driver packages to install.wim."
|
||||
& $Logger 'Committing the driver-only install.wim change...'
|
||||
Invoke-WinUtilISODism -Arguments @('/English', '/Unmount-Image', "/MountDir:$mountDir", '/Commit') -Operation 'commit' | Out-Null
|
||||
$imageMounted = $false
|
||||
|
||||
& $Logger "Adding $($rootPackageFolders.Count) root driver packages to install.wim."
|
||||
$addedCount = Add-WinUtilISODriversToImage -ImagePath $InstallImagePath -ImageIndex $InstallImageIndex -MountDir $mountDir -DriverFolders $rootPackageFolders -ImageLabel 'install.wim' -ImageMounted $imageMountedRef
|
||||
if ($addedCount -gt 0) {
|
||||
$metadataAfter = Get-WinUtilISOWimMetadata -ImagePath $InstallImagePath -Index $InstallImageIndex
|
||||
Assert-WinUtilISOWimMetadata -Before $metadataBefore -After $metadataAfter
|
||||
& $Logger 'Driver injection complete; install.wim metadata validation passed.'
|
||||
@@ -423,7 +429,7 @@ function Invoke-WinUtilISOScript {
|
||||
try {
|
||||
Invoke-WinUtilISODism -Arguments @('/English', '/Unmount-Image', "/MountDir:$mountDir", '/Discard') -Operation 'discard' | Out-Null
|
||||
} catch {
|
||||
& $Logger "Warning: could not discard the failed install.wim mount: $_"
|
||||
& $Logger "Warning: could not discard the failed WIM mount: $_"
|
||||
}
|
||||
}
|
||||
Remove-Item -LiteralPath $mountDir -Recurse -Force -ErrorAction SilentlyContinue
|
||||
|
||||
+102
-19
@@ -214,7 +214,7 @@ Describe "Win11 Creator setup media" {
|
||||
$script:modifyFunction | Should -Match 'if \(\$m -like "Warning:\*"\)[\s\S]*Write-WinUtilISOLog -Level "WARN" -Message \$m -SkipSessionLog[\s\S]*Write-Warning \$m'
|
||||
}
|
||||
|
||||
It "keeps WIM servicing limited to one driver-only mount and commit" {
|
||||
It "keeps WIM servicing limited to DISM Add-Driver without image export or cleanup" {
|
||||
$isoScriptContent = Get-Content -Path $script:isoScriptPath -Raw
|
||||
|
||||
foreach ($expectedText in @(
|
||||
@@ -222,7 +222,8 @@ Describe "Win11 Creator setup media" {
|
||||
"'/Add-Driver'",
|
||||
"'/Commit'",
|
||||
"`$mountDir = Join-Path (Split-Path -Path `$ContentRoot -Parent) 'wim_mount'",
|
||||
'install.wim metadata validation passed'
|
||||
'install.wim metadata validation passed',
|
||||
"Join-Path `$ContentRoot 'sources\boot.wim'"
|
||||
)) {
|
||||
$isoScriptContent | Should -Match ([regex]::Escape($expectedText))
|
||||
}
|
||||
@@ -233,17 +234,19 @@ Describe "Win11 Creator setup media" {
|
||||
'Export-WindowsImage',
|
||||
'Set-WindowsImage',
|
||||
'/ResetBase',
|
||||
'/Cleanup-Image'
|
||||
'/Cleanup-Image',
|
||||
'$WinpeDriver$'
|
||||
)) {
|
||||
$isoScriptContent | Should -Not -Match ([regex]::Escape($forbiddenText))
|
||||
}
|
||||
}
|
||||
|
||||
It "stages only boot-storage drivers in WinPE" {
|
||||
It "injects only SCSIAdapter or HDC storage drivers into boot.wim" {
|
||||
$isoScriptContent = Get-Content -Path $script:isoScriptPath -Raw
|
||||
|
||||
$isoScriptContent | Should -Match ([regex]::Escape("Join-Path `$ContentRoot '`$WinpeDriver$'"))
|
||||
$isoScriptContent | Should -Match 'SCSIAdapter\|HDC'
|
||||
$isoScriptContent | Should -Match ([regex]::Escape("Join-Path `$ContentRoot 'sources\boot.wim'"))
|
||||
$isoScriptContent | Should -Not -Match ([regex]::Escape("Join-Path `$ContentRoot '`$WinpeDriver$'"))
|
||||
$isoScriptContent | Should -Not -Match ([regex]::Escape('sources\$OEM$\$$\Drivers'))
|
||||
$isoScriptContent | Should -Not -Match ([regex]::Escape('WinUtil-InstallDrivers.ps1'))
|
||||
$isoScriptContent | Should -Not -Match ([regex]::Escape('SetupComplete.cmd'))
|
||||
@@ -435,7 +438,7 @@ Describe "Win11 Creator setup media" {
|
||||
|
||||
|
||||
|
||||
It "stages storage drivers for WinPE and adds all drivers to one install.wim index" {
|
||||
It "adds eligible drivers to one install.wim index and does not create `$WinpeDriver$" {
|
||||
$contentRoot = Join-Path ([IO.Path]::GetTempPath()) "WinUtilIsoDrivers_$([guid]::NewGuid())"
|
||||
$installWim = Join-Path $contentRoot 'sources\install.wim'
|
||||
$template = Get-Content -Path $script:autoUnattendPath -Raw
|
||||
@@ -443,7 +446,7 @@ Describe "Win11 Creator setup media" {
|
||||
|
||||
New-WinUtilDriverExportHarness -Fixtures @(
|
||||
@{ Path = 'system_pkg'; Name = 'chipset.inf'; Class = 'System' },
|
||||
@{ Path = 'storage_pkg'; Name = 'iaStorAC.inf'; Class = 'System' },
|
||||
@{ Path = 'storage_pkg'; Name = 'iaStorAC.inf'; Class = 'SCSIAdapter' },
|
||||
@{ Path = 'scsi_pkg'; Name = 'controller.inf'; Class = 'SCSIAdapter' },
|
||||
@{ Path = 'net_pkg'; Name = 'network.inf'; Class = 'Net' },
|
||||
@{ Path = 'group_a\duplicate'; Name = 'audio.inf'; Class = 'Media' },
|
||||
@@ -465,12 +468,8 @@ Describe "Win11 Creator setup media" {
|
||||
$logs.Add([string]$message)
|
||||
}
|
||||
|
||||
$winpeDriverRoot = Join-Path $contentRoot '$WinpeDriver$'
|
||||
@(Get-ChildItem -Path $winpeDriverRoot -Directory).Count | Should -Be 2
|
||||
Test-Path (Join-Path $winpeDriverRoot 'system_pkg\chipset.inf') | Should -BeFalse
|
||||
Test-Path (Join-Path $winpeDriverRoot 'storage_pkg\iaStorAC.inf') | Should -BeTrue
|
||||
Test-Path (Join-Path $winpeDriverRoot 'scsi_pkg\controller.inf') | Should -BeTrue
|
||||
Test-Path (Join-Path $winpeDriverRoot 'net_pkg\network.inf') | Should -BeFalse
|
||||
Test-Path (Join-Path $contentRoot '$WinpeDriver$') | Should -BeFalse
|
||||
($logs -join '|') | Should -Match 'Warning: boot.wim was not found'
|
||||
|
||||
@($script:dismCalls | Where-Object { $_ -match '/Mount-Image' }).Count | Should -Be 1
|
||||
@($script:dismCalls | Where-Object { $_ -match '/Add-Driver' }).Count | Should -Be 9
|
||||
@@ -488,7 +487,7 @@ Describe "Win11 Creator setup media" {
|
||||
$nsMgr = New-Object System.Xml.XmlNamespaceManager($answerFile.NameTable)
|
||||
$nsMgr.AddNamespace('sg', 'https://schneegans.de/windows/unattend-generator/')
|
||||
$answerFile.SelectSingleNode('//sg:File[@path="C:\Windows\Setup\Scripts\WinUtil-InstallDrivers.ps1"]', $nsMgr) | Should -BeNullOrEmpty
|
||||
($logs -join '|') | Should -Match 'Exported 10 of 11 driver packages \(2 staged for WinPE, 1 excluded\)'
|
||||
($logs -join '|') | Should -Match 'Exported 10 of 11 driver packages \(1 excluded\)'
|
||||
($logs -join '|') | Should -Match "Excluding stale duplicate driver package '.*ntprint\.inf_x86_7426e1b60aa62272' \(DriverVer 1/1/2023,10\.0\.26100\.8875\) superseded by '.*ntprint\.inf_x86_58e7118cdecb935e' \(DriverVer 6/1/2024,10\.0\.26100\.9168\)"
|
||||
($logs -join '|') | Should -Match 'install.wim metadata validation passed'
|
||||
($logs -join '|') | Should -Match 'DISM mount completed.'
|
||||
@@ -505,6 +504,91 @@ Describe "Win11 Creator setup media" {
|
||||
}
|
||||
}
|
||||
|
||||
It "injects SCSIAdapter storage drivers into boot.wim index 2 and not `$WinpeDriver`$" {
|
||||
$contentRoot = Join-Path ([IO.Path]::GetTempPath()) "WinUtilIsoBootWim_$([guid]::NewGuid())"
|
||||
$installWim = Join-Path $contentRoot 'sources\install.wim'
|
||||
$bootWim = Join-Path $contentRoot 'sources\boot.wim'
|
||||
$template = Get-Content -Path $script:autoUnattendPath -Raw
|
||||
$logs = [System.Collections.Generic.List[string]]::new()
|
||||
|
||||
New-WinUtilDriverExportHarness -Fixtures @(
|
||||
@{ Path = 'net_pkg'; Name = 'network.inf'; Class = 'Net' },
|
||||
@{ Path = 'scsi_pkg'; Name = 'controller.inf'; Class = 'SCSIAdapter' },
|
||||
@{ Path = 'hdc_pkg'; Name = 'ide.inf'; Class = 'HDC' },
|
||||
@{ Path = 'name_only_pkg'; Name = 'iaStorAC.inf'; Class = 'System' },
|
||||
@{ Path = 'iastorhsacomponent.inf_amd64_1b2a068a8496b6a2'; Name = 'iaStorHsaComponent.inf'; Class = 'SoftwareComponent' },
|
||||
@{ Path = 'iastorhsa_ext.inf_amd64_ba71359697f80d4e'; Name = 'iaStorHsa_Ext.inf'; Class = 'Extension' }
|
||||
)
|
||||
|
||||
try {
|
||||
New-Item -Path (Split-Path $installWim -Parent) -ItemType Directory -Force | Out-Null
|
||||
Set-Content -Path $installWim -Value 'mock-wim'
|
||||
Set-Content -Path $bootWim -Value 'mock-boot'
|
||||
. $script:isoScriptPath
|
||||
Invoke-WinUtilISOScript -ISOContentsDir $contentRoot -AutoUnattendXml $template -InjectCurrentSystemDrivers $true -InstallImagePath $installWim -InstallImageIndex 6 -InstallEditionId 'Professional' -Log {
|
||||
param($message)
|
||||
$logs.Add([string]$message)
|
||||
}
|
||||
|
||||
Test-Path (Join-Path $contentRoot '$WinpeDriver$') | Should -BeFalse
|
||||
@($script:dismCalls | Where-Object { $_ -match '/Mount-Image' -and $_ -match 'boot\.wim' -and $_ -match '/Index:2' }).Count | Should -Be 1
|
||||
@($script:dismCalls | Where-Object { $_ -match '/Mount-Image' -and $_ -match 'install\.wim' }).Count | Should -Be 1
|
||||
@($script:dismCalls | Where-Object { $_ -match '/Unmount-Image\|.*\|/Commit' }).Count | Should -Be 2
|
||||
|
||||
$bootMountCall = $script:dismCalls | Where-Object { $_ -match '/Mount-Image' -and $_ -match 'boot\.wim' } | Select-Object -First 1
|
||||
$installMountCall = $script:dismCalls | Where-Object { $_ -match '/Mount-Image' -and $_ -match 'install\.wim' } | Select-Object -First 1
|
||||
$bootMountIndex = [Array]::IndexOf($script:dismCalls.ToArray(), $bootMountCall)
|
||||
$installMountIndex = [Array]::IndexOf($script:dismCalls.ToArray(), $installMountCall)
|
||||
$bootAdds = @($script:dismCalls[$bootMountIndex..($installMountIndex - 1)] | Where-Object { $_ -match '/Add-Driver' })
|
||||
$bootAdds.Count | Should -Be 2
|
||||
($bootAdds -join "`n") | Should -Match ([regex]::Escape('scsi_pkg'))
|
||||
($bootAdds -join "`n") | Should -Match ([regex]::Escape('hdc_pkg'))
|
||||
($bootAdds -join "`n") | Should -Not -Match ([regex]::Escape('net_pkg'))
|
||||
($bootAdds -join "`n") | Should -Not -Match 'iastorhsa'
|
||||
($bootAdds -join "`n") | Should -Not -Match ([regex]::Escape('name_only_pkg'))
|
||||
|
||||
@($script:dismCalls | Where-Object { $_ -match '/Add-Driver' }).Count | Should -Be 8
|
||||
($logs -join '|') | Should -Match 'Added 2 of 2 driver packages to boot.wim'
|
||||
($logs -join '|') | Should -Match 'Added 6 of 6 driver packages to install.wim'
|
||||
} finally {
|
||||
Remove-Item Function:\dism.exe -ErrorAction SilentlyContinue
|
||||
Remove-Item -Path $contentRoot -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
It "injects only the newest duplicate storage package into boot.wim" {
|
||||
$contentRoot = Join-Path ([IO.Path]::GetTempPath()) "WinUtilIsoBootWimDedup_$([guid]::NewGuid())"
|
||||
$installWim = Join-Path $contentRoot 'sources\install.wim'
|
||||
$bootWim = Join-Path $contentRoot 'sources\boot.wim'
|
||||
$template = Get-Content -Path $script:autoUnattendPath -Raw
|
||||
$logs = [System.Collections.Generic.List[string]]::new()
|
||||
|
||||
New-WinUtilDriverExportHarness -Fixtures @(
|
||||
@{ Path = 'iastorvd.inf_amd64_11111111aaaaaaaa'; Name = 'iaStorVD.inf'; Class = 'SCSIAdapter'; DriverVer = '1/1/2023,20.2.1.1016' },
|
||||
@{ Path = 'iastorvd.inf_amd64_22222222bbbbbbbb'; Name = 'iaStorVD.inf'; Class = 'SCSIAdapter'; DriverVer = '6/1/2024,20.2.8.1028' }
|
||||
)
|
||||
|
||||
try {
|
||||
New-Item -Path (Split-Path $installWim -Parent) -ItemType Directory -Force | Out-Null
|
||||
Set-Content -Path $installWim -Value 'mock-wim'
|
||||
Set-Content -Path $bootWim -Value 'mock-boot'
|
||||
. $script:isoScriptPath
|
||||
Invoke-WinUtilISOScript -ISOContentsDir $contentRoot -AutoUnattendXml $template -InjectCurrentSystemDrivers $true -InstallImagePath $installWim -InstallImageIndex 6 -InstallEditionId 'Professional' -Log {
|
||||
param($message)
|
||||
$logs.Add([string]$message)
|
||||
}
|
||||
|
||||
$bootAdds = @($script:dismCalls | Where-Object { $_ -match '/Add-Driver' })
|
||||
@($bootAdds | Where-Object { $_ -match '22222222bbbbbbbb' }).Count | Should -Be 2
|
||||
@($bootAdds | Where-Object { $_ -match '11111111aaaaaaaa' }).Count | Should -Be 0
|
||||
($logs -join '|') | Should -Match 'Added 1 of 1 driver packages to boot.wim'
|
||||
($logs -join '|') | Should -Match 'Added 1 of 1 driver packages to install.wim'
|
||||
} finally {
|
||||
Remove-Item Function:\dism.exe -ErrorAction SilentlyContinue
|
||||
Remove-Item -Path $contentRoot -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
It "injects Class=Extension driver packages like any other export" {
|
||||
$contentRoot = Join-Path ([IO.Path]::GetTempPath()) "WinUtilIsoExtensionInject_$([guid]::NewGuid())"
|
||||
$installWim = Join-Path $contentRoot 'sources\install.wim'
|
||||
@@ -528,7 +612,7 @@ Describe "Win11 Creator setup media" {
|
||||
}
|
||||
|
||||
@($script:dismCalls | Where-Object { $_ -match '/Add-Driver' }).Count | Should -Be 3
|
||||
($logs -join '|') | Should -Match 'Exported 3 of 3 driver packages \(0 staged for WinPE, 0 excluded\)'
|
||||
($logs -join '|') | Should -Match 'Exported 3 of 3 driver packages \(0 excluded\)'
|
||||
$driversInjected.Value | Should -BeTrue
|
||||
|
||||
$script:exportRootAtAddDriver | Should -Contain (Join-Path $script:driverExportRoot 'ext_pkg_lower')
|
||||
@@ -628,7 +712,7 @@ Describe "Win11 Creator setup media" {
|
||||
}
|
||||
|
||||
@($script:dismCalls | Where-Object { $_ -match '/Add-Driver' }).Count | Should -Be 2
|
||||
($logs -join '|') | Should -Match 'Exported 2 of 2 driver packages \(0 staged for WinPE, 0 excluded\)'
|
||||
($logs -join '|') | Should -Match 'Exported 2 of 2 driver packages \(0 excluded\)'
|
||||
($logs -join '|') | Should -Not -Match 'Excluding stale duplicate driver package'
|
||||
|
||||
$script:exportRootAtAddDriver | Should -Contain (Join-Path $script:driverExportRoot 'device.inf_amd64_11111111aaaaaaaa')
|
||||
@@ -669,7 +753,7 @@ Describe "Win11 Creator setup media" {
|
||||
}
|
||||
|
||||
@($script:dismCalls | Where-Object { $_ -match '/Add-Driver' }).Count | Should -Be 3
|
||||
($logs -join '|') | Should -Match 'Exported 3 of 7 driver packages \(0 staged for WinPE, 4 excluded\)'
|
||||
($logs -join '|') | Should -Match 'Exported 3 of 7 driver packages \(4 excluded\)'
|
||||
($logs -join '|') | Should -Match "Excluding stale duplicate driver package '.*ntprint\.inf_x86_7426e1b60aa62272' \(DriverVer 1/1/2023,10\.0\.26100\.8875\) superseded by '.*ntprint\.inf_x86_58e7118cdecb935e'"
|
||||
($logs -join '|') | Should -Match "Excluding stale duplicate driver package '.*ntprint\.inf_x86_6688e7b66f8d9fb5' \(DriverVer 1/1/2024,10\.0\.26100\.8972\) superseded by '.*ntprint\.inf_x86_58e7118cdecb935e'"
|
||||
($logs -join '|') | Should -Match "Excluding stale duplicate driver package '.*sample\.inf_amd64_11111111aaaaaaaa' \(DriverVer unknown\) superseded by '.*sample\.inf_amd64_22222222bbbbbbbb' \(DriverVer 3/1/2024,1\.2\.3\.4\)"
|
||||
@@ -753,8 +837,7 @@ Describe "Win11 Creator setup media" {
|
||||
|
||||
($logs -join '|') | Should -Match "none of the $script:expectedRootPackages exported driver packages could be added"
|
||||
($logs -join '|') | Should -Not -Match "Added 0 of $script:expectedRootPackages"
|
||||
# WinPE staging is independent of WIM servicing, so it must survive the failure.
|
||||
@(Get-ChildItem -Path (Join-Path $contentRoot '$WinpeDriver$') -Directory).Count | Should -Be 2
|
||||
Test-Path (Join-Path $contentRoot '$WinpeDriver$') | Should -BeFalse
|
||||
$driversInjected.Value | Should -BeFalse
|
||||
} finally {
|
||||
Remove-Item Function:\dism.exe -ErrorAction SilentlyContinue
|
||||
|
||||
Reference in New Issue
Block a user