Last active
May 10, 2022 19:19
-
-
Save chsbuffer/264f73ca9a9d22e264c867cc5d9fcb31 to your computer and use it in GitHub Desktop.
Resolving Windows file's Component, Deployment, Package, Package's Owner Package
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
https://(gist.)github.com/chsbuffer 2022/05 | |
i don't know where to get a script that can show something like https://forums.mydigitallife.net/threads/removing-features-from-an-online-win10-instalation.79311/#post-1511749 | |
so i wrote one | |
Feel free to send documentation in comments, I don't know what is Component, Servicing, Deployment, Package, Owner | |
and what this script does. (though i wrote it) | |
#> | |
[CmdletBinding(DefaultParameterSetName = 'FullNameAll')] | |
param( | |
[Parameter(Mandatory = $true, | |
ParameterSetName = "FileNameAll", | |
Position = 0, | |
ValueFromPipeline = $true, | |
ValueFromPipelineByPropertyName = $true, | |
HelpMessage = "specify the file name, not recommend")] | |
[ValidateNotNullOrEmpty()] | |
[String]$FileName, | |
[Parameter(Mandatory = $true, | |
ParameterSetName = "FullNameAll", | |
Position = 0, | |
ValueFromPipeline = $true, | |
ValueFromPipelineByPropertyName = $true)] | |
[ValidateNotNullOrEmpty()] | |
[String]$FullName, | |
[Parameter(Mandatory = $false, | |
Position = 1, | |
ValueFromPipeline = $true, | |
ValueFromPipelineByPropertyName = $true, | |
HelpMessage = "Override components Registry path")] | |
[String]$ComponentRegPath = "HKLM:\COMPONENTS", | |
[Parameter(Mandatory = $false, | |
Position = 2, | |
ValueFromPipeline = $true, | |
ValueFromPipelineByPropertyName = $true, | |
HelpMessage = "Override SOFTWARE Registry path")] | |
[String]$SoftwareRegPath = "HKLM:\SOFTWARE" | |
) | |
function Mount-Hive { | |
if ($ComponentRegPath -eq "HKLM:\COMPONENTS") { | |
if ( -not ( Test-Path "HKLM:\COMPONENTS\CanonicalData" ) ) { | |
Start-Process reg -Wait -Verb runas -ArgumentList "load HKLM\COMPONENTS $env:windir\system32\config\COMPONENTS" | |
} | |
} | |
if ( -not ( Test-Path "$ComponentRegPath\CanonicalData" ) ) { | |
Write-Error "components registry key not found" | |
exit(1223) | |
} | |
} | |
function Get-HardLink { | |
[CmdletBinding()] | |
param ( | |
[Parameter(Mandatory = $true, | |
Position = 0, | |
ValueFromPipeline = $true, | |
ValueFromPipelineByPropertyName = $true, | |
HelpMessage = "Path to a location.")] | |
[ValidateNotNullOrEmpty()] | |
[String] | |
$Path | |
) | |
Process { | |
$Output = $( & fsutil.exe hardlink list $Path) | |
if ( $? ) { | |
$Output | ForEach-Object { [System.IO.FileInfo] $_ } | |
} | |
} | |
} | |
function Search-Component { | |
param ( | |
[Parameter(Mandatory = $true, ParameterSetName = "FileNameAll")] | |
[ValidateNotNullOrEmpty()] | |
[String]$FileName, | |
[Parameter(Mandatory = $true, ParameterSetName = "FullNameAll")] | |
[ValidateNotNullOrEmpty()] | |
[String]$FullName | |
) | |
if ($FileName) { | |
foreach ($item in Get-ChildItem -Path "$ComponentRegPath\DerivedData\Components") { | |
if ( [Linq.Enumerable]::Any($item.Property, [Func[object, bool]] { | |
param([String]$s) | |
($s.StartsWith('f!')) -and ($s.SubString(2) -eq $FileName) | |
}) | |
) { | |
return $item | |
} | |
} | |
} else { | |
$fsxs = (Get-HardLink -Path $FullName).Where( { $_.FullName.Contains("\Windows\WinSxS") }) | |
if ($fsxs[0]) { | |
return Get-Item -Path "$ComponentRegPath\DerivedData\Components\$($fsxs[0].Directory.Name)" | |
} | |
} | |
} | |
function Search-Package { | |
param ( | |
[Parameter(Mandatory = $true)] | |
[ValidateNotNullOrEmpty()] | |
[String]$Deployment | |
) | |
[Microsoft.Win32.RegistryKey] $deploymentKey = Get-Item -Path "$ComponentRegPath\CanonicalData\Deployments\$Deployment" | |
[String[]]$entries = $deploymentKey.Property | |
$packages = $entries | Where-Object { ($_.StartsWith('p!')) } | |
return $packages | ForEach-Object { | |
$bytes = $deploymentKey.GetValue($_) | Select-Object -Skip 8 | |
$package = [System.Text.Encoding]::UTF8.GetString($bytes) | |
$package = $package.Substring(0, $package.LastIndexOf('.')) | |
$s = $entries.Contains("s!$($_.SubString(2))") | |
$i = $entries.Contains("i!$($_.SubString(2))") | |
return [PSCustomObject]@{ | |
Name = $package | |
Installed = $s | |
Enabled = $i | |
Owners = Get-PackageOwner -Package $package | |
} | |
} | |
} | |
function Get-PackageOwner { | |
param ( | |
[Parameter(Mandatory = $true)] | |
[ValidateNotNullOrEmpty()] | |
[String]$Package | |
) | |
[Microsoft.Win32.RegistryKey]$ownersKey = Get-Item -Path "$SoftwareRegPath\Microsoft\Windows\CurrentVersion\Component Based Servicing\Packages\$Package\Owners" | |
return $ownersKey.Property | |
} | |
Mount-Hive | |
if ($FileName) { | |
Write-Output "Resolving: $FileName" | |
$Component = Search-Component -FileName $FileName | |
} else { | |
Write-Output "Resolving: $FullName" | |
$Component = Search-Component -FullName $FullName | |
} | |
if (!$Component) { | |
Write-Error "Component not found" | |
exit(1) | |
} | |
Write-Host "____________________________________________________________`n" | |
Write-Output "Component: `n$($Component.PSChildName)`n" | |
$Deployment = $Component.Property.Where( { $_.StartsWith('c!') }, 'First' ).SubString(2) | |
Write-Output "Deployment: `n$Deployment`n" | |
$Packages = Search-Package -Deployment $Deployment | |
Write-Host "Packages: " | |
foreach ($_ in $Packages) { | |
Write-Output "`tName: $($_.Name)`n`t`tOwners: $($_.Owners)" | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment