Public/Set-MSIXVirtualRegistryKey.ps1
|
function Set-MSIXVirtualRegistryKey { <# .SYNOPSIS Sets a value in an MSIX registry hive file (Registry.dat / User.dat) and creates the key if needed. .DESCRIPTION Loads the hive file given by -HiveFilePath with RegLoadAppKey, creates -KeyPath (relative to the hive root: 'REGISTRY\MACHINE\SOFTWARE\...' in Registry.dat, 'Software\...' in User.dat whose root is HKCU) when it does not exist yet and writes the value -ValueName. -ValueData depends on -ValueType: String / ExpandString text MultiString string array (a single string is accepted as well) DWord / QWord number, decimal or hex string ('0xFFFFFFFF'); negative numbers are stored as two's complement, like reg.exe does Binary byte array, or a hex string ('01 02 FF', '01,02,FF' or '0102FF') .EXAMPLE Set-MSIXVirtualRegistryKey -HiveFilePath "$pkg\Registry.dat" -KeyPath 'REGISTRY\MACHINE\SOFTWARE\Policies\Adobe\Acrobat Reader\DC\FeatureLockDown' -ValueName 'bEnableProtectedModeAppContainer' -ValueData 1 -ValueType DWord .NOTES RegLoadAppKey works on the hive file directly (no reg load). The keys in package hives usually allow writing only from an elevated PowerShell; reading works without. https://www.nick-it.de Andreas Nick, 2024 #> [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [string]$HiveFilePath, [Parameter(Mandatory = $true)] [string]$KeyPath, [Parameter(Mandatory = $true)] [string]$ValueName, [Parameter(Mandatory = $true)] [AllowEmptyString()] [AllowEmptyCollection()] [object]$ValueData, [Parameter(Mandatory = $true)] [Microsoft.Win32.RegistryValueKind]$ValueType ) # Converts DWord/QWord input into its little-endian bytes ($Size = 4 or 8). $toIntegerBytes = { param($Value, [int]$Size) $text = ([string]$Value).Trim() try { if ($text -match '^0[xX]([0-9a-fA-F]{1,16})$') { $number = [decimal][Convert]::ToUInt64($Matches[1], 16) } else { $number = [decimal]::Parse($text, [System.Globalization.NumberStyles]::Integer, [System.Globalization.CultureInfo]::InvariantCulture) } } catch { throw "ValueData '$Value' is not a valid $ValueType number." } if ($Size -eq 4) { if ($number -lt [int32]::MinValue -or $number -gt [uint32]::MaxValue) { throw "ValueData '$Value' is out of range for a DWord (-2147483648 .. 0xFFFFFFFF)." } if ($number -lt 0) { return [BitConverter]::GetBytes([int32]$number) } return [BitConverter]::GetBytes([uint32]$number) } if ($number -lt [int64]::MinValue -or $number -gt [uint64]::MaxValue) { throw "ValueData '$Value' is out of range for a QWord." } if ($number -lt 0) { return [BitConverter]::GetBytes([int64]$number) } return [BitConverter]::GetBytes([uint64]$number) } switch ($ValueType) { { $_ -in 'String', 'ExpandString' } { $data = [System.Text.Encoding]::Unicode.GetBytes([string]$ValueData + [char]0) } 'MultiString' { # Every string is null-terminated, the list ends with one more null character. $text = (@($ValueData) | ForEach-Object { [string]$_ + [char]0 }) -join '' $data = [System.Text.Encoding]::Unicode.GetBytes($text + [char]0) } 'DWord' { $data = [byte[]](& $toIntegerBytes $ValueData 4) } 'QWord' { $data = [byte[]](& $toIntegerBytes $ValueData 8) } 'Binary' { if ($ValueData -is [byte[]]) { $data = $ValueData } elseif ($ValueData -is [array]) { $data = [byte[]]$ValueData } else { $hex = ([string]$ValueData) -replace '^0[xX]', '' -replace '[\s,\-]', '' if ($hex.Length % 2 -ne 0 -or $hex -notmatch '^[0-9a-fA-F]*$') { throw "ValueData '$ValueData' is not a valid hex string for a Binary value." } $data = New-Object byte[] ($hex.Length / 2) for ($i = 0; $i -lt $data.Length; $i++) { $data[$i] = [Convert]::ToByte($hex.Substring($i * 2, 2), 16) } } } default { throw "Unsupported ValueType '$ValueType' (supported: String, ExpandString, MultiString, DWord, QWord, Binary)." } } if (-not $PSCmdlet.ShouldProcess("$HiveFilePath : $KeyPath\$ValueName", "Set $ValueType value")) { return } $hKey = [IntPtr]::Zero $result = [Win32Apis]::RegLoadAppKey($HiveFilePath, [ref]$hKey, 0xF003F, 0, 0) # KEY_ALL_ACCESS if ($result -ne 0) { $hint = '' if ($result -eq 5) { $hint = ' Writing to package hives usually requires an elevated PowerShell.' } throw "Failed to load hive '$HiveFilePath' (Win32 error $(Format-MSIXWin32Error $result)).$hint" } try { $subKeyHandle = [IntPtr]::Zero $disposition = [uint32]0 $result = [Win32Apis]::RegCreateKeyEx($hKey, $KeyPath, 0, $null, 0, 0xF003F, [IntPtr]::Zero, [ref]$subKeyHandle, [ref]$disposition) if ($result -ne 0) { throw "Failed to open or create key '$KeyPath' in hive '$HiveFilePath' (Win32 error $(Format-MSIXWin32Error $result))." } try { $result = [Win32Apis]::RegSetValueEx($subKeyHandle, $ValueName, 0, $ValueType, $data, $data.Length) if ($result -ne 0) { throw "Failed to set value '$ValueName' in '$KeyPath' of hive '$HiveFilePath' (Win32 error $(Format-MSIXWin32Error $result))." } Write-Verbose "Set '$KeyPath\$ValueName' ($ValueType) in '$HiveFilePath'." } finally { $null = [Win32Apis]::RegCloseKey($subKeyHandle) } } finally { $null = [Win32Apis]::RegCloseKey($hKey) } } |