-
Notifications
You must be signed in to change notification settings - Fork 383
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
(extension) Added Uninstall Extension Package
- Loading branch information
Showing
2 changed files
with
118 additions
and
0 deletions.
There are no files selected for viewing
31 changes: 31 additions & 0 deletions
31
extensions/chocolatey-uninstall.extension/chocolatey-uninstall.extension.nuspec
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<!-- Do not remove this test for UTF-8: if “Ω” doesn’t appear as greek uppercase omega letter enclosed in quotation marks, you should use an editor that supports UTF-8, not this one. --> | ||
<package xmlns="http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd"> | ||
<metadata> | ||
<id>chocolatey-uninstall.extension</id> | ||
<version>0.1.0</version> | ||
<packageSourceUrl>https://github.com/chocolatey/chocolatey-coreteampackages</packageSourceUrl> | ||
<owners>chocolatey</owners> | ||
<title>chocolatey-uninstall.extension (Install)</title> | ||
<authors>dtgm ferventcoder</authors> | ||
<copyright>© 2016-Present Chocolatey Core Team Package Contributors</copyright> | ||
<projectUrl>https://github.com/chocolatey/chocolatey-coreteampackages</projectUrl> | ||
<licenseUrl>https://github.com/chocolatey/choco/blob/master/LICENSE</licenseUrl> | ||
<requireLicenseAcceptance>false</requireLicenseAcceptance> | ||
<projectSourceUrl>https://github.com/chocolatey/chocolatey-coreteampackages</projectSourceUrl> | ||
<docsUrl>https://github.com/chocolatey/chocolatey-coreteampackages/wiki</docsUrl> | ||
<mailingListUrl>https://groups.google.com/forum/#!forum/chocolatey</mailingListUrl> | ||
<bugTrackerUrl>https://github.com/chocolatey/chocolatey-coreteampackages/issues</bugTrackerUrl> | ||
<tags>uninstall extension admin</tags> | ||
<summary>Helper Function for retrieving registry keys during uninstall of packages</summary> | ||
<description> | ||
Provides a helper function to help with retrieving registry keys during uninstall of Chocolatey packages. | ||
</description> | ||
<releaseNotes> | ||
* 0.1.0 - Initial release of extension package | ||
</releaseNotes> | ||
</metadata> | ||
<files> | ||
<file src="extensions\**" target="extensions" /> | ||
</files> | ||
</package> |
87 changes: 87 additions & 0 deletions
87
extensions/chocolatey-uninstall.extension/extensions/Get-UninstallRegistryKey.psm1
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
function Get-UninstallRegistryKey { | ||
<# | ||
.SYNOPSIS | ||
Retrieve registry uninstall key(s) | ||
.DESCRIPTION | ||
This function will attempt to retrieve a matching registry key to be used | ||
within a chocolateyUninstall.ps1 script. | ||
.PARAMETER SoftwareName | ||
Part or all of the Display Name as you see it in Programs and Features. | ||
It should be enough to be unique. | ||
.EXAMPLE | ||
[array]$key = Get-UninstallRegistryKey -SoftwareName "Gpg4win (2.3.0)" | ||
[array]$key = Get-UninstallRegistryKey -SoftwareName "Launchy 2.5" | ||
[array]$key = Get-UninstallRegistryKey -SoftwareName "Mozilla Firefox*" | ||
$key.UninstallString | ||
.INPUTS | ||
Accepts [string] | ||
.OUTPUTS | ||
This script searches registry objects and returns PSCustomObject of the | ||
matched key's properties. | ||
Retrieve properties with dot notation, for example: $key.UninstallString | ||
.NOTES | ||
This helper reduces the number of lines one would have to write to | ||
retrieve registry keys to 1 line. It also prevents Get-ItemProperty from | ||
failing when handling wrongly encoded registry keys. | ||
Using this function in a package requires adding the extension as a | ||
dependency. Add the following the nuspec: | ||
<dependencies> | ||
<dependency id="chocolatey-uninstall.extension" /> | ||
</dependencies> | ||
.LINK | ||
Uninstall-ChocolateyPackage | ||
#> | ||
[CmdletBinding()] | ||
param( | ||
[Parameter(Mandatory=$True, | ||
ValueFromPipeline=$True)] | ||
[ValidateNotNullOrEmpty()] | ||
[string] $softwareName | ||
) | ||
Write-Debug "Running 'Get-UninstallRegistryKey' for `'$env:ChocolateyPackageName`' with SoftwareName:`'$softwareName`'"; | ||
|
||
$ErrorActionPreference = 'Stop' | ||
$local_key = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*' | ||
$machine_key = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*' | ||
$machine_key6432 = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*' | ||
|
||
Write-Verbose "Retrieving all uninstall registry keys" | ||
[array]$keys = Get-ChildItem -Path @($machine_key6432, $machine_key, $local_key) ` | ||
-ErrorAction SilentlyContinue | ||
|
||
Write-Debug "Error handling check: Get-ItemProperty will fail if a registry key is written incorrectly." | ||
Write-Debug "If such a key is found, loop to try to bypass all badKeys" | ||
[int]$maxAttempts = 10 | ||
for ([int]$attempt = 1; $attempt -le $maxAttempts; $attempt++) { | ||
[bool]$success = $FALSE | ||
|
||
try { | ||
[array]$foundKey = Get-ItemProperty -Path $keys.PsPath ` | ||
-ErrorAction SilentlyContinue ` | ||
| Where-Object {$_.DisplayName -like $softwareName} | ||
$success = $TRUE | ||
} catch { | ||
Write-Debug "Found bad key." | ||
foreach ($key in $keys){try{Get-ItemProperty $key.PsPath > $null}catch{$badKey = $key.PsPath}} | ||
Write-Verbose "Skipping bad key: $($key.PsPath)" | ||
[array]$keys = Get-ChildItem -Path @($machine_key6432, $machine_key, $local_key) ` | ||
-ErrorAction SilentlyContinue ` | ||
| Where-Object {$badKey -NotContains $_.PsPath} | ||
} | ||
|
||
if ($success) {break;} | ||
} | ||
|
||
return $foundKey | ||
} |
f7fe281
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dtgm The file name - path too long -
extensions/chocolatey-uninstall.extension/extensions/Get-UninstallRegistryKey.psm1
just needs to beextensions/Get-UninstallRegistryKey.psm1
f7fe281
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ferventcoder I think you mistook the filename for github path.
first part
extensions/chocolatey-uninstall.extension
is GH pathsecond part
extensions/Get-UninstallRegistryKey.psm1
is package pathf7fe281
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OH, yeah. Whooopsy. Nothing to see here, carry on. :)
👍
f7fe281
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
:) so you want to proceed with this as the fix as is? Any changes?
f7fe281
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to say the PowerShell is super clean. Good job!
f7fe281
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dtgm - from having written something very similar in the past, I am fairly certain this code will miss 64bit Uninstall key when run under a 32-bit process on a 64-bit system. (management system like SCCM, Altiris sometimes stick with a 32-bit agent for compat. Or do really weird stuff like SCCM 2012 "packages" run 32-bit and "application objects" run 64-bit).
I have code that works around this if you are interested.
f7fe281
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fairly certain you are right. Do you think it is an edge case that we should support?
f7fe281
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only time this would come into play is when a 32-bit process, like when you are running a config manager in 32 bit mode (Chef/Puppet) - less so for Puppet since it has had a 64-bit agent for 2 years now. Chef is just getting theirs out cc @mwrock.
So likely it would be a good idea to address this.
f7fe281
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@DarwinJS Of course I'm interested :) I know the code could be optimised to work more directly with registry objects than relying on Get-ItemProperty, but not sure that is related to the issue you are describing. Can you clarify further why this code will miss 64-bit uninstall keys under 32-bit processes? It is looking at posh registry objects, that as far as I am aware, are not differentiated with bit-depth. I'm not seeing where the issue could be; why would a 32-bit process not be able to enumerate the WOW path and find the key?
I could see that maybe handling an uninstall binary found by the key with this script that is 64-bit under a 32-bit process failing, but that wouldn't have anything to do with this code.
f7fe281
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, it looks like @ferventcoder clarified in #222 why it would fail
f7fe281
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ferventcoder - I feel strongly it is not edge case. For instance, many sysinternals tools like psexec are 32-bit - and many other "callers" could be 32-bit.
@dtgm - when you are in a 32-bit process 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall*' is mapped to the real location 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall*' - it is a part of the implementation of WOW64. So you can never build a reference to the 64-bit location.
@ferventcoder - this can be done in powershell without the possibly unpredictable results of playing with the process level redirection with the below code.
The below code also handles:
I would interested in some type of attribution for contributing this code (unfortunately I don't have time to work it into the Chocolatey codebase or I would do that instead of this brain dump)
f7fe281
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@DarwinJS As I noted in #222 (comment) the keys aren't listed as affected to redirection. Not sure if the technet article is accurate or not though.
f7fe281
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@DarwinJS Also I edited your comment so the code would show correctly. (use 3 backticks to surround a multi-line codeblock)
f7fe281
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dtgm - that page is trying to say that it is affected:
Learned it inside out while writing this: https://www.amazon.com/Deploying-Supporting-Applications-64-bit-Windows-ebook/dp/B0098P9Z22
f7fe281
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, any key that you find under HKLM\Software\WOW6432node you know is redirected for sure - wow6432node is a 32-bit processes view of the keyname hierarchically above it. [except when you see it twice in a row, that is someone hardcoding it and then their code runs under 32-bit and creates a doubling of the key]
And when it get's to COM it's even more fun. ProdIDs aren't redirected, but CLSID (which progids point to) is.
D.
f7fe281
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@DarwinJS Correct, my bad. I saw that but missed that subkeys inherit from that as your screenshot also shows.
f7fe281
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So you just call another powershell natively? That's an interesting way around the problem. Not sure if I like it though. At least not yet
f7fe281
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It could be the least worst bad way of accessing the registry.
f7fe281
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the disabling of WOW64 redirection could be pretty bad - I am thinking it also disables the file system redirection at the same time - so your 32-bit proc will be looking at 64-bit binaries in it's "system32" folder.
The code ran on over 10,000 machines without incident and the piece that dynamically grabs object output was a pattern I stole from somewhere reputable - can't remember where though!
Very cool that PowerShell can pass the objects from proc to proc.
Starting a new 64-bit proc is the cleanest way to bridge the two (if avoiding the wow64 redirection disablement).
You can also instruct the WMI registry provider on which registry bitness view to connect to - I believe even when operating locally - but then you'd be looking at a whole different type of data coming back.
f7fe281
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahhh - here's the original - you can even see some of my 3 year old comments against the article.
http://powershell.com/cs/blogs/tips/archive/2013/05/30/running-portions-of-code-in-32-bit-or-64-bit.aspx
Aleksandar Nikolić was telling me at a conference that he puts powershell.com snippets through some pretty rigorous testing before publishing.
f7fe281
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re: disabling
It does not have to disable both. Since I already have quite a bit of experience doing this in Ruby, I'm positive that you can do it with a flag being sent to registry in a 32 bit process and not disable SysWOW64 entirely, which we both agree is bad.
f7fe281
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But I'm not fully sure about .NET yet (and back to v2, since we may need to support if running from Posh), which is why I said maybe this is not that bad of an option. :)
f7fe281
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just realized that the code I submitted above (and most code on the net) assumes that if you are on 64-bit Windows, that WOW64 is installed and available.
Given that it is optional on Server and ServerCore and non-existent on Nano - this assumption should not be made.
Testing for "%systemroot%\syswow64" (from a 64-bit process) and "%systemroot%\sysnative" (from a 32-bit on 64-bit OS process) would confirm both [1] I am on a 64-bit os, [2] WOW64 is installed.
f7fe281
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@DarwinJS are you in a position to submit a PR to correct this? Thanks!
f7fe281
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gep13 - I was reporting it for the code in this thread which is not implemented yet.
Do you mean that you'd like a PR for the existing code for the same problem?
f7fe281
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@DarwinJS sorry, you lost me. I thought you were saying that there was an issue with the new uninstall extension which was added with this commit. Is this not the case?
f7fe281
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gep13 the code provided by DarwinJS is an example of how he tackles the issue. Most of the code appears to be outside the scope for this function since we are only interested in retrieving the key. I think the only pertinent portion is we would want to add something like:
A possibly better, but more difficult solution, would require rewriting the cmdlets we are using in this function (Get-Item/Property) that are handling the Registry Objects so we can directly specify redirection in a powershell 32-bit process running in a WoW environment so the keys are read properly.
f7fe281
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this function has also been migrated to an internal helper, so any fixes here should also be applied over there https://github.com/chocolatey/choco/commits/master/src/chocolatey.resources/helpers/functions/Get-UninstallRegistryKey.ps1
f7fe281
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dtgm - I am not sure what code substitutes for "#existing code to enumerate Wow6432Node".
Is the -ErrorAction SilentlyContinue in this code:
Intentionally covering the following cases:
Non-existence of 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall' in two cases - [1] you are running on 32-bit or [2] on 64-bit with no 32-bit subsystem installed?
[3] Non-existence of 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall' ?
f7fe281
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I only anticipated the location may not exist which I can't imagine the user caring about. But testing for locations would allow to only retrieve keys from locations that exist so ErrorAction could be set to Continue.
When I say
#existing code to enumerate WoW6432Node keys
I am meaning the code already present in the script that tries to get redirected keys that would fail otherwise.-Command
may have been misleading but it accepts a script-block.f7fe281
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dtgm:
#2 - yes - that is why I chimed into this thread. On server Core (2012, 2016) WOW64 is optional. On Nano Server it is not supported.
It might be more robust if your code for skipping bad keys was looking for a specific known set of exceptions - to my eye it seems to assume all errors will be of the type that the code is intended to filter out. (BTW - great piece of code to effectively handle bad registry keys)
If you were to be selective about which keys you checked, then you'd need a little more logic to not include the WOW6432NODE when running pure 32-bit or on 64-bit where WOW64 is not installed. If you decide to be more selective, keep in mind that it is not reliable to test for "WOW6432NODE" registry key because many bad software programs and installers accidentally create one while in 32-bit mode - so it is quite typical to see it present on pure 32-bit and to see HKLM\Software\wow6432node\wow6432node" on 64-bit machines.