private/Test-MachineSatisfiesDependency.ps1
function Test-MachineSatisfiesDependency { [CmdletBinding()] [OutputType('System.Int32')] Param ( [ValidateNotNullOrEmpty()] [System.Xml.XmlElement]$Dependency, [int]$DebugIndent = 0 ) # 0 SUCCESS, Dependency is met # -1 FAILRE, Dependency is not met # -2 Unknown dependency kind - status uncertain switch ($Dependency.SchemaInfo.Name) { '_Bios' { Write-Debug "$('- ' * $DebugIndent)[ Got: $($CachedHardwareTable['_Bios']) ]" foreach ($entry in $Dependency.Level) { if ($CachedHardwareTable['_Bios'] -like "$entry*") { return 0 } } return -1 } '_CPUAddressWidth' { Write-Debug "$('- ' * $DebugIndent)[ Got: $($CachedHardwareTable['_CPUAddressWidth']), Expected: $($dependency.AddressWidth) ]" if ($CachedHardwareTable['_CPUAddressWidth'] -like "$($Dependency.AddressWidth)*") { return 0 } else { return -1 } } '_Driver' { [array]$SupportedDriverNodes = 'HardwareID', 'Version', 'Date', 'File' [array]$DriverChildNodes = $Dependency.ChildNodes.SchemaInfo.Name if (-not (Compare-Array $DriverChildNodes -in $SupportedDriverNodes)) { Write-Debug "$('- ' * $DebugIndent)_Driver node contained unknown element - skipping checks" return -2 } if ($DriverChildNodes -contains 'HardwareID') { [bool]$HardwareFound = $false foreach ($HardwareInMachine in $CachedHardwareTable['_PnPID'].HardwareID) { foreach ($HardwareID in $Dependency.HardwareID.'#cdata-section') { # Lenovo HardwareIDs can contain wildcards (*) so we have to compare with "-like" if ($HardwareInMachine -like "*$HardwareID*") { Write-Debug "$('- ' * $DebugIndent)Matched device '$HardwareInMachine' with required '$HardwareID'" $HardwareFound = $true $HardwareIDFound = $HardwareInMachine } } } if ($HardwareFound) { [array]$DevicesWithHardwareID = $CachedHardwareTable['_PnPID'].Where{ $_.HardwareID -eq "$HardwareIDFound" } if ($DevicesWithHardwareID.Count -ne 1) { Write-Debug "$('- ' * $DebugIndent)$($DevicesWithHardwareID.Count) devices with HardwareID '$HardwareIDFound'" } $Device = $DevicesWithHardwareID[0] # First, check if there is a driver installed for the device at all before proceeding (issue#24) if ($Device.Problem -eq 'CM_PROB_FAILED_INSTALL') { [string]$HexDeviceProblemStatus = '0x{0:X8}' -f ($Device | Get-PnpDeviceProperty -KeyName 'DEVPKEY_Device_ProblemStatus').Data Write-Debug "$('- ' * $DebugIndent)Device '$HardwareIDFound' does not have any driver (ProblemStatus: $HexDeviceProblemStatus)" return -1 } $DriverVersion = ($Device | Get-PnpDeviceProperty -KeyName 'DEVPKEY_Device_DriverVersion').Data $DriverDate = ($Device | Get-PnpDeviceProperty -KeyName 'DEVPKEY_Device_DriverDate').Data.Date # Documentation for this: https://docs.microsoft.com/en-us/windows-hardware/drivers/install/identifier-score--windows-vista-and-later- [byte]$DriverMatchTypeScore = (Get-PnpDeviceProperty -InputObject $Device -KeyName 'DEVPKEY_Device_DriverRank').Data -shr 12 -band 0xF if ($DriverMatchTypeScore -ge 2) { Write-Verbose "Device '$($Device.Name)' may be using a generic or inbox driver, which could lead to wrong results for this package." } if ($DriverChildNodes -contains 'Date') { Write-Debug "$('- ' * $DebugIndent)Trying to match driver based on Date" $LenovoDate = [DateTime]::new(0) if ( [DateTime]::TryParseExact($Dependency.Date, 'yyyy-MM-dd', [CultureInfo]::InvariantCulture, 'None', [ref]$LenovoDate) ) { Write-Debug "$('- ' * $DebugIndent)[Got: $DriverDate, Expected: $LenovoDate]" if ($DriverDate -eq $LenovoDate) { return 0 # SUCCESS } } else { Write-Verbose "Got unsupported date format from Lenovo: '$($Dependency.Date)' (expected yyyy-MM-dd)" } } if ($DriverChildNodes -contains 'Version') { Write-Debug "$('- ' * $DebugIndent)Trying to match driver based on Version" # Not all drivers tell us their versions via the OS API. I think later I can try to parse the INIs as an alternative, but it would get tricky if ($DriverVersion) { Write-Debug "$('- ' * $DebugIndent)Testing installed driver version: $DriverVersion against required $($Dependency.Version)" return (Compare-VersionStrings -LenovoString $Dependency.Version -SystemString $DriverVersion) } else { Write-Verbose "Device '$HardwareIDFound' does not report its driver version. Returning unsupported (-2)" return -2 } } } else { Write-Debug "$('- ' * $DebugIndent)No installed device matched the driver check" } } if (Compare-Array @('File', 'Version') -in $DriverChildNodes) { # This may not be 100% yet as Lenovo sometimes uses some non-system environment variables in their file paths [string]$Path = Resolve-CmdVariable -String $Dependency.File -ExtraVariables @{'WINDOWS' = $env:SystemRoot} if (Test-Path -LiteralPath $Path -PathType Leaf) { $filProductVersion = (Get-Item -LiteralPath $Path).VersionInfo.ProductVersion $FileVersionCompare = Compare-VersionStrings -LenovoString $Dependency.Version -SystemString $filProductVersion if ($FileVersionCompare -eq -2) { Write-Debug "$('- ' * $DebugIndent)Got unsupported with ProductVersion, trying comparison with FileVersion" $filFileVersion = (Get-Item -LiteralPath $Path).VersionInfo.FileVersion return (Compare-VersionStrings -LenovoString $Dependency.Version -SystemString $filFileVersion) } else { return $FileVersionCompare } } else { Write-Debug "$('- ' * $DebugIndent)The file '$Path' was not found." return -1 } } return -1 } '_EmbeddedControllerVersion' { if ($CachedHardwareTable['_EmbeddedControllerVersion']) { return (Compare-VersionStrings -LenovoString $Dependency.Version -SystemString $CachedHardwareTable['_EmbeddedControllerVersion']) } return -1 } '_ExternalDetection' { $externalDetection = Invoke-PackageCommand -Command $Dependency.'#text' -Path $env:TEMP Write-Debug "$('- ' * $DebugIndent)[ Got ExitCode: $($externalDetection.ExitCode), Expected: $($Dependency.rc) ]" if ($externalDetection -and $externalDetection.ExitCode -in ($Dependency.rc -split ',')) { return 0 } else { return -1 } } '_FileExists' { # This may not be 100% yet as Lenovo sometimes uses some non-system environment variables in their file paths [string]$Path = Resolve-CmdVariable -String $Dependency -ExtraVariables @{'WINDOWS' = $env:SystemRoot} return (Test-Path -LiteralPath $Path -PathType Leaf) } '_FileVersion' { # This may not be 100% yet as Lenovo sometimes uses some non-system environment variables in their file paths [string]$Path = Resolve-CmdVariable -String $Dependency.File -ExtraVariables @{'WINDOWS' = $env:SystemRoot} if (Test-Path -LiteralPath $Path -PathType Leaf) { $filProductVersion = (Get-Item -LiteralPath $Path).VersionInfo.ProductVersion $FileVersionCompare = Compare-VersionStrings -LenovoString $Dependency.Version -SystemString $filProductVersion if ($FileVersionCompare -eq -2) { Write-Debug "$('- ' * $DebugIndent)Got unsupported with ProductVersion, trying comparison with FileVersion" $filFileVersion = (Get-Item -LiteralPath $Path).VersionInfo.FileVersion return (Compare-VersionStrings -LenovoString $Dependency.Version -SystemString $filFileVersion) } else { return $FileVersionCompare } } else { Write-Debug "$('- ' * $DebugIndent)The file '$Path' was not found." return -1 } } '_OS' { foreach ($entry in $Dependency.OS) { if ("$entry" -like "${CachedHardwareTable['_OS']}*") { return 0 } } return -1 } '_OSLang' { if ($Dependency.Lang -eq [CultureInfo]::CurrentUICulture.ThreeLetterWindowsLanguageName) { return 0 } else { return -1 } } '_PnPID' { foreach ($HardwareID in $CachedHardwareTable['_PnPID'].HardwareID) { if ($HardwareID -like "*$($Dependency.'#cdata-section')*") { return 0 } } return -1 } '_RegistryKey' { if ($Dependency.Key) { if (Test-Path -LiteralPath ('Microsoft.PowerShell.Core\Registry::{0}' -f $Dependency.Key) -PathType Container) { return 0 } } return -1 } '_RegistryKeyValue' { if ($Dependency.type -ne 'REG_SZ') { return -2 } if (Test-Path -LiteralPath ('Microsoft.PowerShell.Core\Registry::{0}' -f $Dependency.Key) -PathType Container) { try { $regVersion = Get-ItemPropertyValue -LiteralPath ('Microsoft.PowerShell.Core\Registry::{0}' -f $Dependency.Key) -Name $Dependency.KeyName -ErrorAction Stop } catch { return -1 } [string]$DependencyVersion = if ($Dependency.KeyValue) { $Dependency.KeyValue } elseif ($Dependency.Version) { $Dependency.Version } else { Write-Verbose "Could not get LenovoString from _RegistryKeyValue dependency node" return -2 } return (Compare-VersionStrings -LenovoString $DependencyVersion -SystemString $regVersion) } else { return -1 } } default { Write-Verbose "Unsupported dependency encountered: $_" return -2 } } return -2 } |