public
Last active

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

  • Download Gist
uninstall_string_getter.ps1
PowerShell
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
$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
}

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.