Last active
March 30, 2020 16:50
-
-
Save mbrownnycnyc/6511499 to your computer and use it in GitHub Desktop.
Still needs to be polished to do less work (proc arch conditional): for use with chocolatey packaging and to be included as a help within a warmUp template for use with chocolateyUnininstall.ps1... GetValueFromRegistryThruWMI modified version of http://gallery.technet.microsoft.com/scriptcenter/6062bbfc-53bf-4f92-994d-08f18c8324c0
This file contains hidden or 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
$global:debug = $false | |
$global:debugverbose = $false | |
Function GetUninstallString([string]$computername, $displayname) | |
{ | |
try { | |
#first, we'll grab a collection of key names below HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall for 64 bit | |
#we'll search for the DisplayName... | |
#if found, we'll query for the UninstallString and we'll stop looking | |
write-host "Getting UninstallString. This may take a moment..." -backgroundcolor "DarkYellow" | |
write-host "Enumerating 64-bit HKLM keys (or 32-bit keys if the host process is 32-bit)..." -backgroundcolor "DarkYellow" | |
$keynames = GetValueFromRegistryThruWMI "enumkey" "64" "localhost" "HKLM" "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" "" | |
foreach ( $key in $keynames) { | |
$dname = GetValueFromRegistryThruWMI "GetStringValue" "64" "localhost" "HKLM" "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$key" "DisplayName" | |
if ( $dname -ne $false -and $dname.contains($displayname) ) { | |
$uninstallstring = GetValueFromRegistryThruWMI "GetStringValue" "64" "localhost" "HKLM" "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$key" "UninstallString" | |
write-host "Got it... $uninstallstring" -backgroundcolor "darkgreen" | |
write-host "Passing it to the chocolatey gods for sacrifice..." -backgroundcolor "darkgreen" | |
return $uninstallstring | |
} | |
} | |
#then, we'll grab a collection of key names below HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall for 32 bit | |
#we'll search for the DisplayName... if found, we'll query for the UninstallString and we'll stop looking | |
write-host "Enumerating 32-bit HKLM keys..." -backgroundcolor "DarkYellow" | |
write-host "Don't you just wish you had installed that 64-bit version now? C'mon! Quantum computing is almost here!" -backgroundcolor "DarkYellow" | |
$keynames = GetValueFromRegistryThruWMI "enumkey" "32" "localhost" "HKLM" "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" "" | |
foreach ( $key in $keynames) { | |
$dname = GetValueFromRegistryThruWMI "GetStringValue" "32" "localhost" "HKLM" "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$key" "DisplayName" | |
if ( $dname -ne $false -and $dname.contains($displayname) ) { | |
$uninstallstring = GetValueFromRegistryThruWMI "GetStringValue" "32" "localhost" "HKLM" "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$key" "UninstallString" | |
write-host "Got it... $uninstallstring" -backgroundcolor "darkgreen" | |
write-host "Passing it to the chocolatey gods for sacrifice..." -backgroundcolor "darkgreen" | |
return $uninstallstring | |
} | |
} | |
#then, we'll grab a collection of key names below HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall for 64 bit | |
#we'll search for the DisplayName... if found, we'll query for the UninstallString and we'll stop looking | |
write-host "Enumerating 64-bit HKCU keys (or 32-bit keys if the host process is 32-bit)" -backgroundcolor "DarkYellow" | |
write-host "What great times we're having, huh?" -backgroundcolor "DarkYellow" | |
$keynames = GetValueFromRegistryThruWMI "enumkey" "64" "localhost" "HKCU" "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" "" | |
foreach ( $key in $keynames) { | |
$dname = GetValueFromRegistryThruWMI "GetStringValue" "64" "localhost" "HKCU" "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$key" "DisplayName" | |
if ( $dname -ne $false -and $dname.contains($displayname) ) { | |
$uninstallstring = GetValueFromRegistryThruWMI "GetStringValue" "64" "localhost" "HKcU" "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$key" "UninstallString" | |
write-host "Got it... $uninstallstring" -backgroundcolor "darkgreen" | |
write-host "Passing it to the chocolatey gods for sacrifice..." -backgroundcolor "darkgreen" | |
return $uninstallstring | |
} | |
} | |
#then, we'll grab a collection of key names below HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall for 32 bit | |
#we'll search for the DisplayName... if found, we'll query for the UninstallString and we'll stop looking | |
write-host "Enumerating 32-bit HKCU keys " -backgroundcolor "DarkYellow" | |
write-host "The pain is almost over." -backgroundcolor "DarkYellow" | |
$keynames = GetValueFromRegistryThruWMI "enumkey" "32" "localhost" "HKCU" "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" "" | |
foreach ( $key in $keynames) { | |
$dname = GetValueFromRegistryThruWMI "GetStringValue" "32" "localhost" "HKLM" "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$key" "DisplayName" | |
if ( $dname -ne $false -and $dname.contains($displayname) ) { | |
$uninstallstring = GetValueFromRegistryThruWMI "GetStringValue" "32" "localhost" "HKLM" "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$key" "UninstallString" | |
write-host "Got it... $uninstallstring" -backgroundcolor "darkgreen" | |
write-host "Passing it to the chocolatey gods for sacrifice..." -backgroundcolor "darkgreen" | |
return $uninstallstring | |
} | |
} | |
throw "Couldn't find $displayname as a DIsplayName or couldn't find the DisplayName's UninstallString below Uninstall." | |
} | |
catch { | |
return $error[0] | |
} | |
} | |
Function GetValueFromRegistryThruWMI([string]$stdregprovmethod, $providerarchtoget, [string]$computername, $regtree, $regkey, $value) | |
{ | |
try{ | |
$stdregprovmethod = $stdregprovmethod.tostring().tolower() | |
#these are all the available methods for stdregprov: http://msdn.microsoft.com/en-us/library/aa393664%28v=vs.85%29.aspx | |
$supportedstdregprovmethods = 'enumkey','enumvalues','getstringvalue' | |
#$allstdregprovmethods = 'checkaccess','createkey','deletekey','deletevalue','enumkey','enumvalues','getbinaryvalue','getdwordvalue','getexpandedstringvalue','getmultistringvalue','getstringvalue','setbinaryvalue','setdwordvalue','setexpandedstringvalue','setmultistringvalue','setstringvalue','#these are not supported in windows version 5.x.x (<=2003/xp)','getqwordvalue','getsecuritydescriptor','setqwordvalue','setsecuritydescriptor' | |
if ( $supportedstdregprovmethods -notcontains $stdregprovmethod ) { | |
write-host You have provided a StrRegProv Method that is not supported yet. | |
write-host enumkey will be used instead. | |
$stdregprovmethod = 'enumkey' | |
} | |
else { | |
} | |
switch ($regtree) | |
{ | |
#constant uints for registry trees | |
HKCR { $regtree = "&h80000000" } | |
HKCU { $regtree = "&h80000001" } | |
HKLM { $regtree = "&h80000002" } | |
HKU { $regtree = "&h80000003" } | |
#more obscure trees: http://msdn.microsoft.com/en-us/library/windows/desktop/ms724836%28v=vs.85%29.aspx | |
HKPerformanceData { $regtree = "&h80000004" } # http://msdn.microsoft.com/en-us/library/windows/desktop/aa373219%28v=vs.85%29.aspx | |
HKCurrentConfig { $regtree = "&h80000005" } | |
HKCULocalSettings { $regtree = "&h80000007" } | |
default { $regtree = "&h80000002" } | |
} | |
$objNamedValueSet = New-Object -COM "WbemScripting.SWbemNamedValueSet" | |
if ( $providerarchtoget -eq 32 ) { | |
if ( [IntPtr]::size -eq 8 -and (test-path env:\PROCESSOR_ARCHITEW6432) ) { | |
if ($debugverbose) { write-host process arch is 64-bit, querying 32-bit provider as requested. } | |
$objNamedValueSet.Add("__ProviderArchitecture", 32) | Out-Null | |
} | |
elseif ( [IntPtr]::size -eq 4 ) { | |
if ($debugverbose) { write-host process arch is 32-bit, querying 32-bit provider. } | |
$objNamedValueSet.Add("__ProviderArchitecture", 32) | Out-Null | |
} | |
} | |
elseif ( $providerarchtoget -eq 64 ) { | |
if ( [IntPtr]::size -eq 8 -and (test-path env:\PROCESSOR_ARCHITEW6432) ) { | |
if ($debugverbose) { write-host process arch is 64-bit, querying 64-bit provider as requested. } | |
$objNamedValueSet.Add("__ProviderArchitecture", 64) | Out-Null | |
} | |
elseif ( [IntPtr]::size -eq 4 ) { | |
if ($debugverbose) { write-host process arch is 32-bit, querying 64-bit provider. } | |
$objNamedValueSet.Add("__ProviderArchitecture", 64) | Out-Null | |
} | |
} | |
else { | |
if ($debug) { write-host You have requested an architecture that is not handled. Please provide "32" or "64" as your first parameter. } | |
return | |
} | |
$objLocator = New-Object -COM "Wbemscripting.SWbemLocator" | |
$objServices = $objLocator.ConnectServer($computername,"root\default","","","","","",$objNamedValueSet) | |
$objStdRegProv = $objServices.Get("StdRegProv") | |
$Inparams = ($objStdRegProv.Methods_ | where {$_.name -eq $stdregprovmethod}).InParameters.SpawnInstance_() | |
if ($stdregprovmethod -eq "getstringvalue") { | |
if ($debugverbose) { write-host getting string value $regtree $regkey : $value } | |
($Inparams.Properties_ | where {$_.name -eq "Hdefkey"}).Value = $regtree | |
($Inparams.Properties_ | where {$_.name -eq "Ssubkeyname"}).Value = $regkey | |
($Inparams.Properties_ | where {$_.name -eq "Svaluename"}).Value = $value | |
$Outparams = $objStdRegProv.ExecMethod_("GetStringValue", $Inparams, "", $objNamedValueSet) | |
if (($Outparams.Properties_ | where {$_.name -eq "ReturnValue"}).Value -eq 0) { | |
$result = ($Outparams.Properties_ | where {$_.name -eq "sValue"}).Value | |
if ($debugverbose) { write-host '$result of GetStringValue: ' $result } | |
return $result | |
} | |
else{ | |
if ($debugverbose) { write-host '$result of GetStringValue: ' $false } | |
return $false | |
} | |
} | |
elseif ($stdregprovmethod -eq "enumkey") { | |
if ($debug) { write-host enuming keys below $regtree : $regkey } | |
($Inparams.Properties_ | where {$_.name -eq "Hdefkey"}).Value = $regtree | |
($Inparams.Properties_ | where {$_.name -eq "Ssubkeyname"}).Value = $regkey | |
$Outparams = $objStdRegProv.ExecMethod_("EnumKey", $Inparams, "", $objNamedValueSet) | |
if (($Outparams.Properties_ | where {$_.name -eq "ReturnValue"}).Value -eq 0) { | |
$result = ($Outparams.Properties_ | where {$_.name -eq "sNames"}).Value | |
if ($debugverbose) { write-host '$result of EnumKey: ' $result } | |
return $result | |
} | |
else{ | |
if ($debugverbose) { write-host '$result of EnumKey: ' $false } | |
return $false | |
} | |
} | |
} | |
catch { | |
$error[0] | |
} | |
} | |
$packageName = 'desktoprestore' | |
$fileType = 'MSI' | |
$silentArgs = '/x','/quiet', '/passive' #in this case, this must be an array instead of a string. We will expand during execution. | |
#not ready as of September 9th, 2013: | |
#Uninstall-ChocolateyPackage $packageName $fileType $silentArgs $filePath | |
$filePath = GetUninstallString localhost "Desktop Restore" | |
#We must parse the returned UninstallString and modify it, consider $fileType and $silentArgs | |
#let's take the example: | |
#$filePath = "MsiExec.exe /I{228CEA74-6DD1-40B9-B95F-77273F4316B5}" | |
#or this | |
#$filePath = "MsiExec.exe /xribbit11-.3adf.msi" | |
if ( $fileType -eq "MSI" ) { | |
#With MSIs, some UninstallString REG_SZs will contain the `/I{Product GUID}` which brings up the installer configuration screen for the installed product. | |
#There are three scenarios for an UninstallString that we are concerned with that will trigger an uninstall: | |
# `/x[Product GUID]` | |
# `/x[product.msi]` | |
# any arbitrary string that doesn't contain /i or /x (could be an exe) | |
#our primary step will be to isolate the Product GUID: | |
# (by the way, we get the product guid by querying the registry, but why would we perform more costly work again... | |
# We already queried the registry to deal with the condition that we aren't dealing with an MSI.) | |
# We will find the Product GUID by searching for the [0-9][a-z][a-Z] between '{' and '}' which is empirically stable. | |
if ( $filePath -match '{.*}' ) { $productGUID = $matches[0].tolower() } | |
#even if this $productGUID winds up being "", we can still search for other things: | |
#If the UninstallString contains '/x$productGUID' | |
# or if it contains | |
# '/x', followed by any character, followed by '.msi', followed by an end of line character, followed by whitespace, followed by any character. | |
# and | |
# any character, followed by any invalid character, followed by any character | |
# or if it doesn't contain '/x' or '/i' | |
#http://msdn.microsoft.com/en-us/library/aa365247.aspx | |
if ( $filePath.tolower() -match "/x$productGUID" -or ( | |
$filePath.tolower() -match "/x.*.msi[$\s]*" -and | |
$filePath.tolower() -notmatch ".*[" + [regex]::Escape([System.IO.Path]::InvalidPathChars -join "") + "].*" | |
) -or ( | |
-not $filePath.tolower() -match "/x" -or | |
-not $filePath.tolower() -match "/i" | |
) | |
) { | |
#then we can just run it as is. It contains the one of the uninstall commands ("/x$productGUID" or "/xproduct.msi") or it could be an EXE or something else [see pdfcreator for an example of this instance]. | |
if ($debug) { write-host running "$filePath"} | |
(Start-Process -FilePath "$filePath" -Wait -Passthru).ExitCode | |
} | |
else { | |
#We'll gather up the switches, then we'll verify each as okay (only allow $silentArgs, except "/x" which requires more things) | |
# a switch is defined as any string that starts with a '/' that is followed by '[a-zA-Z]' then followed by parameters | |
# this excludes the /log switch as it can contain a parameter... maybe later... | |
# we can exclude the `/x` switch from this check as it was already handled in this conditional | |
$filePath -split "/" | foreach-object { | |
if ( $_ -ne "x") { | |
if ($silentArgs -match [regex]::Escape( $_.trim() ) ) { | |
[string[]]$matchedsilentargs += $_ | |
} | |
} | |
} | |
foreach ($silentArg in $silentArgs) { | |
if ( $silentArg -ne "/x") { | |
[string[]]$matchedsilentargs += $silentArg | |
} | |
} | |
#Now, let's add /x$productGUID to $silentArgs. It wasn't already there, or the original condition (above) would have covered it. | |
#we might still only be dealing with "/x" $silentArgs | |
$silentArgs = "/x$productGUID " + ($matchedsilentargs) | |
#now we can execute it with the $silentArgs | |
if ($debug) { write-host running "msiexec.exe " $silentArgs } | |
(Start-Process -FilePath "msiexec.exe" -ArgumentList "$silentArgs" -Wait -Passthru).ExitCode | |
} | |
} | |
else { | |
#file type isn't MSI | |
#We'll gather up the switches, then we'll verify each as okay (only allow $silentArgs) | |
# a switch is defined as any string that starts with a '/' that is followed by '[a-zA-Z]' then followed by parameters | |
# this excludes any switch that takes in a parameter... maybe later... | |
$filePath -split "/" | foreach-object { | |
if ( $silentArgs -match [regex]::Escape( $_.trim() ) ) { | |
if ($debug) { write-host "adding to the array " $_ } | |
[string[]]$matchedsilentargs += $_ | |
} | |
} | |
$silentArgs = ([string[]]$matchedsilentargs -join " /") | |
write-host running "$filePath" $silentArgs | |
(Start-Process -FilePath "msiexec.exe" -ArgumentList "$silentArgs" -Wait -Passthru).ExitCode | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment