Skip to content

Commit 98306ec

Browse files
author
Steve Lee (POWERSHELL HE/HIM) (from Dev Box)
committed
fix dism usage
1 parent 61c9a2e commit 98306ec

7 files changed

Lines changed: 97 additions & 32 deletions

File tree

resources/dism_dsc/locales/en-us.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ failedParseInput = "Failed to parse input: %{err}"
3333
capabilitiesArrayEmpty = "Capabilities array cannot be empty for set operation"
3434
nameRequired = "name is required for set operation"
3535
stateRequired = "state is required for set operation"
36+
capabilityNotFound = "Capability '%{name}' was not found on this system"
3637
unsupportedDesiredState = "Unsupported desired state '%{state}'. Supported states for set are: Installed, NotPresent"
3738
failedSerializeOutput = "Failed to serialize output: %{err}"
3839

resources/dism_dsc/src/feature_on_demand/get.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@ pub fn handle_get(input: &str) -> Result<String, String> {
3333
} else {
3434
FeatureOnDemandInfo {
3535
name: Some(raw.name),
36-
exist: None,
3736
state: CapabilityState::from_dism(raw.state),
3837
display_name: Some(raw.display_name),
3938
description: Some(raw.description),
4039
download_size: Some(raw.download_size),
4140
install_size: Some(raw.install_size),
41+
..FeatureOnDemandInfo::default()
4242
}
4343
};
4444
results.push(info);

resources/dism_dsc/src/feature_on_demand/set.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,19 @@ pub fn handle_set(input: &str) -> Result<String, String> {
3232
.ok_or_else(|| t!("fod_set.stateRequired").to_string())?;
3333

3434
let current = session.get_capability_info(name)?;
35+
36+
if current.unknown {
37+
return Err(t!("fod_set.capabilityNotFound", name = name).to_string());
38+
}
39+
3540
let current_state = CapabilityState::from_dism(current.state);
3641

3742
let needs_reboot = match desired_state {
3843
CapabilityState::Installed => {
39-
session.add_capability(name)?
44+
match current_state {
45+
Some(CapabilityState::Installed) => false,
46+
_ => session.add_capability(name)?,
47+
}
4048
}
4149
CapabilityState::NotPresent => {
4250
match current_state {
@@ -58,12 +66,12 @@ pub fn handle_set(input: &str) -> Result<String, String> {
5866
let raw = session.get_capability_info(name)?;
5967
let info = FeatureOnDemandInfo {
6068
name: Some(raw.name),
61-
exist: None,
6269
state: CapabilityState::from_dism(raw.state),
6370
display_name: Some(raw.display_name),
6471
description: Some(raw.description),
6572
download_size: Some(raw.download_size),
6673
install_size: Some(raw.install_size),
74+
..FeatureOnDemandInfo::default()
6775
};
6876
results.push(info);
6977
}

resources/dism_dsc/src/optional_feature/dism.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@ unsafe extern "system" {
2525
) -> *mut c_void;
2626
}
2727

28-
#[repr(C, packed)]
28+
#[repr(C, packed(4))]
2929
struct DismFeature {
3030
feature_name: *const u16,
3131
state: i32,
3232
}
3333

34-
#[repr(C, packed)]
34+
#[repr(C, packed(4))]
3535
struct DismFeatureInfo {
3636
feature_name: *const u16,
3737
state: i32,
@@ -42,13 +42,13 @@ struct DismFeatureInfo {
4242
custom_property_count: u32,
4343
}
4444

45-
#[repr(C, packed)]
45+
#[repr(C, packed(4))]
4646
struct DismCapability {
4747
name: *const u16,
4848
state: i32,
4949
}
5050

51-
#[repr(C, packed)]
51+
#[repr(C, packed(4))]
5252
struct DismCapabilityDetail {
5353
name: *const u16,
5454
state: i32,

resources/dism_dsc/tests/featureOnDemand_export.tests.ps1

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,14 @@ Describe 'Microsoft.Windows/FeatureOnDemandList - export operation' -Skip:(!$IsW
128128
$capabilities.Count | Should -Be 0
129129
}
130130

131+
It 'returns empty results for empty capabilities array input' -Skip:(!$isElevated) {
132+
$inputJson = '{"capabilities":[]}'
133+
$output = dsc resource export -r Microsoft.Windows/FeatureOnDemandList -i $inputJson | ConvertFrom-Json
134+
$LASTEXITCODE | Should -Be 0
135+
$capabilities = $output.resources[0].properties.capabilities
136+
$capabilities.Count | Should -Be 0
137+
}
138+
131139
It 'returns complete capability properties when full-info filter is used' -Skip:(!$isElevated) {
132140
$inputJson = '{"capabilities":[{"name":"' + $knownCapabilityNameOne + '","displayName":"*"}]}'
133141
$output = dsc resource export -r Microsoft.Windows/FeatureOnDemandList -i $inputJson | ConvertFrom-Json

resources/dism_dsc/tests/featureOnDemand_get.tests.ps1

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,20 @@ Describe 'Microsoft.Windows/FeatureOnDemandList - get operation' -Skip:(!$IsWind
5757
$inputJson = '{"capabilities":[{"state":"Installed"}]}'
5858
$testError = & { dsc resource get -r Microsoft.Windows/FeatureOnDemandList -i $inputJson 2>&1 }
5959
$LASTEXITCODE | Should -Not -Be 0
60+
"$testError" | Should -Match 'name is required'
6061
}
6162

6263
It 'returns error when capabilities array is empty' -Skip:(!$isElevated) {
6364
$inputJson = '{"capabilities":[]}'
6465
$testError = & { dsc resource get -r Microsoft.Windows/FeatureOnDemandList -i $inputJson 2>&1 }
6566
$LASTEXITCODE | Should -Not -Be 0
67+
"$testError" | Should -Match 'cannot be empty'
68+
}
69+
70+
It 'returns error for malformed JSON input' -Skip:(!$isElevated) {
71+
$inputJson = '{"capabilities":[{invalid'
72+
$testError = & { dsc resource get -r Microsoft.Windows/FeatureOnDemandList -i $inputJson 2>&1 }
73+
$LASTEXITCODE | Should -Not -Be 0
6674
}
6775

6876
It 'returns _exist false for a non-existent capability name' -Skip:(!$isElevated) {

resources/dism_dsc/tests/featureOnDemand_set.tests.ps1

Lines changed: 65 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,80 +11,120 @@ Describe 'Microsoft.Windows/FeatureOnDemandList - set operation' -Skip:(!$IsWind
1111
}
1212
}
1313

14+
BeforeAll {
15+
# Dynamically discover capability names instead of hardcoding
16+
$dismOutput = & dism /Online /Get-Capabilities /Format:Table /English 2>&1
17+
if ($LASTEXITCODE -ne 0) {
18+
throw "Failed to get capabilities using dism: $dismOutput"
19+
}
20+
$installedMatches = $dismOutput | Select-String -Pattern '^\s*(\S+)\s+\|\s+Installed\s*$'
21+
$notPresentMatches = $dismOutput | Select-String -Pattern '^\s*(\S+)\s+\|\s+Not Present\s*$'
22+
if (-not $installedMatches -or -not $notPresentMatches) {
23+
throw "Failed to find both installed and not-present capabilities in DISM output.`nOutput:`n$dismOutput"
24+
}
25+
$knownInstalledCapability = $installedMatches[0].Matches[0].Groups[1].Value
26+
$knownNotPresentCapability = $notPresentMatches[0].Matches[0].Groups[1].Value
27+
28+
# Record initial states for cleanup
29+
$script:initialInstalledState = 'Installed'
30+
$script:initialNotPresentState = 'NotPresent'
31+
}
32+
33+
AfterAll {
34+
# Restore capabilities to their original states
35+
if ($knownInstalledCapability) {
36+
$restoreJson = '{"capabilities":[{"name":"' + $knownInstalledCapability + '","state":"' + $script:initialInstalledState + '"}]}'
37+
dsc resource set -r Microsoft.Windows/FeatureOnDemandList -i $restoreJson 2>&1 | Out-Null
38+
}
39+
if ($knownNotPresentCapability) {
40+
$restoreJson = '{"capabilities":[{"name":"' + $knownNotPresentCapability + '","state":"' + $script:initialNotPresentState + '"}]}'
41+
dsc resource set -r Microsoft.Windows/FeatureOnDemandList -i $restoreJson 2>&1 | Out-Null
42+
}
43+
}
44+
1445
It 'returns error when name is missing' -Skip:(!$isElevated) {
1546
$inputJson = '{"capabilities":[{"state":"Installed"}]}'
1647
$testError = & { dsc resource set -r Microsoft.Windows/FeatureOnDemandList -i $inputJson 2>&1 }
1748
$LASTEXITCODE | Should -Not -Be 0
49+
"$testError" | Should -Match 'name is required'
1850
}
1951

2052
It 'returns error when state is missing' -Skip:(!$isElevated) {
21-
$inputJson = '{"capabilities":[{"name":"Language.Basic~~~en-US~0.0.1.0"}]}'
53+
$inputJson = '{"capabilities":[{"name":"' + $knownInstalledCapability + '"}]}'
2254
$testError = & { dsc resource set -r Microsoft.Windows/FeatureOnDemandList -i $inputJson 2>&1 }
2355
$LASTEXITCODE | Should -Not -Be 0
56+
"$testError" | Should -Match 'state is required'
2457
}
2558

2659
It 'returns error when capabilities array is empty' -Skip:(!$isElevated) {
2760
$inputJson = '{"capabilities":[]}'
2861
$testError = & { dsc resource set -r Microsoft.Windows/FeatureOnDemandList -i $inputJson 2>&1 }
2962
$LASTEXITCODE | Should -Not -Be 0
63+
"$testError" | Should -Match 'cannot be empty'
3064
}
3165

3266
It 'returns error for unsupported desired state' -Skip:(!$isElevated) {
33-
$inputJson = '{"capabilities":[{"name":"Language.Basic~~~en-US~0.0.1.0","state":"Staged"}]}'
67+
$inputJson = '{"capabilities":[{"name":"' + $knownInstalledCapability + '","state":"Staged"}]}'
68+
$testError = & { dsc resource set -r Microsoft.Windows/FeatureOnDemandList -i $inputJson 2>&1 }
69+
$LASTEXITCODE | Should -Not -Be 0
70+
"$testError" | Should -Match 'Unsupported desired state'
71+
}
72+
73+
It 'returns error for malformed JSON input' -Skip:(!$isElevated) {
74+
$inputJson = '{"capabilities":[{invalid'
75+
$testError = & { dsc resource set -r Microsoft.Windows/FeatureOnDemandList -i $inputJson 2>&1 }
76+
$LASTEXITCODE | Should -Not -Be 0
77+
}
78+
79+
It 'returns error when installing a non-existent capability' -Skip:(!$isElevated) {
80+
$inputJson = '{"capabilities":[{"name":"NonExistent-Capability-1234567890","state":"Installed"}]}'
3481
$testError = & { dsc resource set -r Microsoft.Windows/FeatureOnDemandList -i $inputJson 2>&1 }
3582
$LASTEXITCODE | Should -Not -Be 0
83+
"$testError" | Should -Match 'not found'
3684
}
3785

38-
It 'can install a capability and returns updated state' -Skip:(!$isElevated) {
39-
# Use a capability known to exist on most Windows systems
40-
$inputJson = '{"capabilities":[{"name":"Language.Basic~~~en-US~0.0.1.0","state":"Installed"}]}'
86+
It 'can install a capability and returns updated state' -Skip:(!$isElevated) -Tag 'Mutating' {
87+
$inputJson = '{"capabilities":[{"name":"' + $knownInstalledCapability + '","state":"Installed"}]}'
4188
$output = dsc resource set -r Microsoft.Windows/FeatureOnDemandList -i $inputJson | ConvertFrom-Json
4289
$LASTEXITCODE | Should -Be 0
4390
$output.afterState.capabilities | Should -Not -BeNullOrEmpty
4491
$output.afterState.capabilities.Count | Should -Be 1
4592
$cap = $output.afterState.capabilities[0]
46-
$cap.name | Should -BeExactly 'Language.Basic~~~en-US~0.0.1.0'
93+
$cap.name | Should -BeExactly $knownInstalledCapability
4794
$cap.state | Should -BeIn @('Installed', 'InstallPending')
4895
$cap.displayName | Should -Not -BeNullOrEmpty
4996
}
5097

51-
It 'can remove a capability and returns updated state' -Skip:(!$isElevated) {
52-
$inputJson = '{"capabilities":[{"name":"Language.Basic~~~en-US~0.0.1.0","state":"NotPresent"}]}'
98+
It 'can remove a capability and returns updated state' -Skip:(!$isElevated) -Tag 'Mutating' {
99+
$inputJson = '{"capabilities":[{"name":"' + $knownNotPresentCapability + '","state":"NotPresent"}]}'
53100
$output = dsc resource set -r Microsoft.Windows/FeatureOnDemandList -i $inputJson | ConvertFrom-Json
54101
$LASTEXITCODE | Should -Be 0
55102
$output.afterState.capabilities | Should -Not -BeNullOrEmpty
56103
$output.afterState.capabilities.Count | Should -Be 1
57104
$cap = $output.afterState.capabilities[0]
58-
$cap.name | Should -BeExactly 'Language.Basic~~~en-US~0.0.1.0'
59-
# Installed is accepted because some base capabilities cannot be removed by DISM
60-
$cap.state | Should -BeIn @('NotPresent', 'Removed', 'UninstallPending', 'Staged', 'Installed')
105+
$cap.name | Should -BeExactly $knownNotPresentCapability
106+
$cap.state | Should -BeIn @('NotPresent', 'Removed', 'UninstallPending', 'Staged')
61107
}
62108

63-
It 'sets state to Installed for an already installed capability' -Skip:(!$isElevated) {
64-
# First ensure the capability is installed
65-
$installJson = '{"capabilities":[{"name":"Language.Basic~~~en-US~0.0.1.0","state":"Installed"}]}'
66-
dsc resource set -r Microsoft.Windows/FeatureOnDemandList -i $installJson | Out-Null
67-
$LASTEXITCODE | Should -Be 0
68-
69-
# Set Installed again — should succeed idempotently
109+
It 'sets state to Installed for an already installed capability' -Skip:(!$isElevated) -Tag 'Mutating' {
110+
# Set Installed on an already-installed capability — should succeed idempotently
111+
$installJson = '{"capabilities":[{"name":"' + $knownInstalledCapability + '","state":"Installed"}]}'
70112
$output = dsc resource set -r Microsoft.Windows/FeatureOnDemandList -i $installJson | ConvertFrom-Json
71113
$LASTEXITCODE | Should -Be 0
72114
$output.afterState.capabilities | Should -Not -BeNullOrEmpty
73115
$cap = $output.afterState.capabilities[0]
74-
$cap.name | Should -BeExactly 'Language.Basic~~~en-US~0.0.1.0'
116+
$cap.name | Should -BeExactly $knownInstalledCapability
75117
$cap.state | Should -Be 'Installed'
76118
}
77119

78-
It 'sets state to NotPresent for an already not-present capability' -Skip:(!$isElevated) {
79-
# Use a capability name that does not exist on the system, so it is already NotPresent
80-
$notPresentJson = '{"capabilities":[{"name":"Test.NonExistent.Capability~~~0.0.1.0","state":"NotPresent"}]}'
81-
82-
# Set NotPresent — should succeed idempotently since it is already not present
120+
It 'sets state to NotPresent for an already not-present capability' -Skip:(!$isElevated) -Tag 'Mutating' {
121+
# Set NotPresent on an already not-present capability — should succeed idempotently
122+
$notPresentJson = '{"capabilities":[{"name":"' + $knownNotPresentCapability + '","state":"NotPresent"}]}'
83123
$output = dsc resource set -r Microsoft.Windows/FeatureOnDemandList -i $notPresentJson | ConvertFrom-Json
84124
$LASTEXITCODE | Should -Be 0
85125
$output.afterState.capabilities | Should -Not -BeNullOrEmpty
86126
$cap = $output.afterState.capabilities[0]
87-
$cap.name | Should -BeExactly 'Test.NonExistent.Capability~~~0.0.1.0'
127+
$cap.name | Should -BeExactly $knownNotPresentCapability
88128
$cap.state | Should -BeIn @('NotPresent', 'Removed', 'Staged')
89129

90130
# Set NotPresent again — should still succeed idempotently

0 commit comments

Comments
 (0)