mirror of
https://github.com/ChrisTitusTech/winutil.git
synced 2026-10-08 11:39:10 +11:00
Add friendly explanations for common DISM exit codes (#5087)
* Add friendly explanations for common DISM exit codes Invoke-WinUtilISODism threw only a raw exit code on DISM failure (e.g. "DISM add-driver failed with exit code 112"), leaving users to look up what the number means themselves. Adds a $knownExitCode lookup table mapping common Windows/DISM exit codes (disk full, access denied, file/path not found, file in use, timeout, etc.) to plain-English explanations. When a failure's exit code is recognized, the thrown message now includes the explanation in parentheses; unrecognized codes fall back to the original plain-number message. Verified the script still parses correctly after the change. * Add missing period to fallback DISM error message * Add tests for DISM known/unknown exit code error messages Adds a test verifying a known exit code (112) produces the friendly explanation in the thrown message, alongside the existing test verifying an unrecognized code still falls back to the plain numeric message. Verified: all 35 tests in win11creator.Tests.ps1 pass. * Document DISM friendly error messages in Win11 Creator troubleshooting Adds a Troubleshooting table row explaining the new DISM error message format (exit code + explanation in parentheses), with guidance for the most common cases and a link to Microsoft's full error code reference for anything not explained. * Fix duplicate DISM calls in unmapped exit code test The unmapped-code test called Invoke-WinUtilISOScript twice — once via Should -Throw, once to capture the message for the no-parens check — causing $script:dismCalls to double-count. Consolidated to a single call, checking both the exit code and the absence of a parenthesized explanation against one captured exception message. Verified: all 36 tests in win11creator.Tests.ps1 pass. * Fix broken DISM error code reference link in docs Replaced the dead windows-hardware/manufacture link with Microsoft's actual System Error Codes reference page, which DISM exit codes correspond to. * Strengthen DISM fallback regression coverage --------- Co-authored-by: Chris Titus <contact@christitus.com>
This commit is contained in:
@@ -144,6 +144,7 @@ When you install Windows 11 from your modified ISO:
|
||||
| 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` |
|
||||
| DISM error with an explanation in parentheses, e.g. "exit code 112 (Disk is full)" | WinUtil now explains common DISM failures in plain language. Free up disk space for a "Disk is full" error, or run WinUtil as Administrator for an "Access denied" error. If the code has no explanation, search the number on Microsoft's [DISM error code reference](https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes) |
|
||||
| "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 |
|
||||
|
||||
---
|
||||
|
||||
@@ -185,6 +185,21 @@ function Invoke-WinUtilISOScript {
|
||||
return @($survivingFolders)
|
||||
}
|
||||
|
||||
$knownExitCode = @{
|
||||
112 = "Disk is full"
|
||||
5 = "Access denied"
|
||||
2 = "File not found"
|
||||
3 = "Path not found"
|
||||
87 = "Invalid parameter"
|
||||
1168 = "Element not found"
|
||||
1392 = "File or directory is corrupted"
|
||||
32 = "File in use / sharing violation"
|
||||
21 = "Device not ready"
|
||||
1460 = "Operation timed out"
|
||||
1223 = "Operation cancelled by user"
|
||||
50 = "Request not supported"
|
||||
}
|
||||
|
||||
function Invoke-WinUtilISODism {
|
||||
param (
|
||||
[Parameter(Mandatory)][string[]]$Arguments,
|
||||
@@ -199,7 +214,11 @@ function Invoke-WinUtilISOScript {
|
||||
& $Logger " dism[$Operation]: $line"
|
||||
}
|
||||
}
|
||||
throw "DISM $Operation failed with exit code $exitCode."
|
||||
if ($knownExitCode.ContainsKey($exitCode)) {
|
||||
throw "DISM $Operation failed with exit code $exitCode ($($knownExitCode[$exitCode]))."
|
||||
} else {
|
||||
throw "DISM $Operation failed with exit code $exitCode."
|
||||
}
|
||||
}
|
||||
if ($Operation -ne 'metadata') {
|
||||
& $Logger "DISM $Operation completed."
|
||||
|
||||
@@ -950,6 +950,109 @@ Describe "Win11 Creator setup media" {
|
||||
}
|
||||
}
|
||||
|
||||
It "reports a friendly explanation when DISM fails with a known exit code" {
|
||||
$contentRoot = Join-Path ([IO.Path]::GetTempPath()) "WinUtilIsoMountFailureKnownCode_$([guid]::NewGuid())"
|
||||
$installWim = Join-Path $contentRoot 'sources\install.wim'
|
||||
$template = Get-Content -Path $script:autoUnattendPath -Raw
|
||||
$script:dismCalls = [System.Collections.Generic.List[string]]::new()
|
||||
|
||||
function dism.exe {
|
||||
param([Parameter(ValueFromRemainingArguments)][string[]]$Arguments)
|
||||
|
||||
$script:dismCalls.Add(($Arguments -join '|'))
|
||||
if ($Arguments -contains '/Get-WimInfo') {
|
||||
$global:LASTEXITCODE = 0
|
||||
'Languages : en-US'
|
||||
'Installation : Client'
|
||||
'Edition : Professional'
|
||||
'ProductSuite : Terminal Server'
|
||||
'ProductType : WinNT'
|
||||
} elseif ($Arguments -contains '/Mount-Image') {
|
||||
$global:LASTEXITCODE = 112
|
||||
'Mount failed'
|
||||
} elseif ($Arguments -contains '/Get-MountedImageInfo') {
|
||||
$global:LASTEXITCODE = 0
|
||||
"Mount Dir : $(Join-Path (Split-Path -Path $contentRoot -Parent) 'wim_mount')"
|
||||
} elseif ($Arguments -contains '/Export-Driver') {
|
||||
$global:LASTEXITCODE = 0
|
||||
Export-WinUtilTestDriverPackage -Arguments $Arguments -Fixtures @(
|
||||
@{ Path = 'storage_pkg'; Name = 'iaStorAC.inf'; Class = 'System' }
|
||||
)
|
||||
} else {
|
||||
$global:LASTEXITCODE = 0
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
New-Item -Path (Split-Path $installWim -Parent) -ItemType Directory -Force | Out-Null
|
||||
Set-Content -Path $installWim -Value 'mock-wim'
|
||||
. $script:isoScriptPath
|
||||
|
||||
{ Invoke-WinUtilISOScript -ISOContentsDir $contentRoot -AutoUnattendXml $template -InjectCurrentSystemDrivers $true -InstallImagePath $installWim -InstallImageIndex 6 -InstallEditionId 'Professional' } |
|
||||
Should -Throw '*112*Disk is full*'
|
||||
|
||||
@($script:dismCalls | Where-Object { $_ -match '/Get-MountedImageInfo' }).Count | Should -Be 1
|
||||
@($script:dismCalls | Where-Object { $_ -match '/Unmount-Image\|.*\|/Discard' }).Count | Should -Be 1
|
||||
} finally {
|
||||
Remove-Item Function:\dism.exe -ErrorAction SilentlyContinue
|
||||
Remove-Item -Path $contentRoot -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
It "falls back to the plain exit code for an unmapped DISM failure" {
|
||||
$contentRoot = Join-Path ([IO.Path]::GetTempPath()) "WinUtilIsoMountFailureUnknownCode_$([guid]::NewGuid())"
|
||||
$installWim = Join-Path $contentRoot 'sources\install.wim'
|
||||
$template = Get-Content -Path $script:autoUnattendPath -Raw
|
||||
$script:dismCalls = [System.Collections.Generic.List[string]]::new()
|
||||
|
||||
function dism.exe {
|
||||
param([Parameter(ValueFromRemainingArguments)][string[]]$Arguments)
|
||||
|
||||
$script:dismCalls.Add(($Arguments -join '|'))
|
||||
if ($Arguments -contains '/Get-WimInfo') {
|
||||
$global:LASTEXITCODE = 0
|
||||
'Languages : en-US'
|
||||
'Installation : Client'
|
||||
'Edition : Professional'
|
||||
'ProductSuite : Terminal Server'
|
||||
'ProductType : WinNT'
|
||||
} elseif ($Arguments -contains '/Mount-Image') {
|
||||
$global:LASTEXITCODE = 999
|
||||
'Mount failed'
|
||||
} elseif ($Arguments -contains '/Get-MountedImageInfo') {
|
||||
$global:LASTEXITCODE = 0
|
||||
"Mount Dir : $(Join-Path (Split-Path -Path $contentRoot -Parent) 'wim_mount')"
|
||||
} elseif ($Arguments -contains '/Export-Driver') {
|
||||
$global:LASTEXITCODE = 0
|
||||
Export-WinUtilTestDriverPackage -Arguments $Arguments -Fixtures @(
|
||||
@{ Path = 'storage_pkg'; Name = 'iaStorAC.inf'; Class = 'System' }
|
||||
)
|
||||
} else {
|
||||
$global:LASTEXITCODE = 0
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
New-Item -Path (Split-Path $installWim -Parent) -ItemType Directory -Force | Out-Null
|
||||
Set-Content -Path $installWim -Value 'mock-wim'
|
||||
. $script:isoScriptPath
|
||||
|
||||
$thrown = $null
|
||||
try {
|
||||
Invoke-WinUtilISOScript -ISOContentsDir $contentRoot -AutoUnattendXml $template -InjectCurrentSystemDrivers $true -InstallImagePath $installWim -InstallImageIndex 6 -InstallEditionId 'Professional'
|
||||
} catch {
|
||||
$thrown = $_.Exception.Message
|
||||
}
|
||||
$thrown | Should -Be 'DISM mount failed with exit code 999.'
|
||||
|
||||
@($script:dismCalls | Where-Object { $_ -match '/Get-MountedImageInfo' }).Count | Should -Be 1
|
||||
@($script:dismCalls | Where-Object { $_ -match '/Unmount-Image\|.*\|/Discard' }).Count | Should -Be 1
|
||||
} finally {
|
||||
Remove-Item Function:\dism.exe -ErrorAction SilentlyContinue
|
||||
Remove-Item -Path $contentRoot -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
It "does not add driver setup artifacts when injection is disabled" {
|
||||
$contentRoot = Join-Path ([IO.Path]::GetTempPath()) "WinUtilIsoNoDrivers_$([guid]::NewGuid())"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user