Skip to content

Commit

Permalink
(GH-739) Get-UninstallRegistryKey / update templates
Browse files Browse the repository at this point in the history
Add Get-UninstallRegistryKey function to provide a means of getting
uninstaller registry keys that does not fail with incorrectly
encoded keys and values.

Update the code documentation to link update the function with other
similar functions and generate updated documentation

Add the function to the uninstall template and nuspec with
instructions to use the chocolatey-uninstall.extension package when
supporting 0.9.9.x and below as the new function will only be
available in 0.9.10 and above.

The helper includes a trycatch for invalid registry keys. Using the
catch is a requirement as blindly enumerating will fail every time
if even one invalid key exists.
  • Loading branch information
dtgm authored and ferventcoder committed Jun 12, 2016
1 parent 9d5cf08 commit d607d65
Show file tree
Hide file tree
Showing 14 changed files with 284 additions and 9 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ If you were using any of the functions in a non-recommended way or not compliant
* Mark packages pending until install completes successfully - see [#198](https://github.com/chocolatey/choco/issues/198)
* Resolve sources by name - see [#356](https://github.com/chocolatey/choco/issues/356)
* Uninstall-ChocolateyEnvironmentVariable function - see [#772](https://github.com/chocolatey/choco/issues/772)
* Get-UninstallRegistryKey function - see [#739](https://github.com/chocolatey/choco/issues/739)
* Pro/Business - Ubiquitous Install Directory Switch - see [#258](https://github.com/chocolatey/choco/issues/258)
* Pro/Business - Runtime Virus Scanning - see [virus scanning](https://chocolatey.org/docs/features-virus-check)
* Pro/Business - Private CDN cache for downloads - see [private CDN cache](https://chocolatey.org/docs/features-private-cdn)
Expand Down Expand Up @@ -127,7 +128,8 @@ If you were using any of the functions in a non-recommended way or not compliant
* Fix - Logging of upgrade messages - placement of some messages is incorrect - see [#557](https://github.com/chocolatey/choco/issues/557)
* Fix - Get-WebFile fails with - The term '//continue' is not recognized as the name of a cmdlet - see [#789](https://github.com/chocolatey/choco/issues/789)
* Fix - Unable to read registry snapshot file - see [#487](https://github.com/chocolatey/choco/issues/487)

* Fix - Uninstall template fails when encountering "bad" registry keys - see [#739](https://github.com/chocolatey/choco/issues/739)

### IMPROVEMENTS

* AutoUninstaller is on by default - see [#308](https://github.com/chocolatey/choco/issues/308)
Expand Down
119 changes: 119 additions & 0 deletions docs/generated/HelpersGetUninstallRegistryKey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Get-UninstallRegistryKey

Retrieve registry key(s) for system-installed applications from an
exact or wildcard search.

## Syntax

~~~powershell
Get-UninstallRegistryKey `
-SoftwareName <String> `
[-IgnoredArguments <Object[]>] [<CommonParameters>]
~~~

## Description

This function will attempt to retrieve a matching registry key for an
already installed application, usually to be used with a
chocolateyUninstall.ps1 automation script.

The function also prevents `Get-ItemProperty` from failing when
handling wrongly encoded registry keys.

## Notes

Available in 0.9.10+. If you need to maintain compatibility with pre
0.9.10, please add the following to your nuspec:

~~~xml
<dependencies>
<dependency id="chocolatey-uninstall.extension" />
</dependencies>
~~~

## Aliases

`Get-InstallRegistryKey`


## Examples

**EXAMPLE 1**

~~~powershell
# Software name in Programs and Features is "Gpg4Win (2.3.0)"
[array]$key = Get-UninstallRegistryKey -SoftwareName "Gpg4win*"
$key.DisplayName
~~~

**EXAMPLE 2**

~~~powershell
# Software name is "Launchy 2.5"
[array]$key = Get-UninstallRegistryKey -SoftwareName "Launchy*"
$key.UninstallString
~~~

**EXAMPLE 3**

~~~powershell
# Software name is "Mozilla Firefox"
[array]$key = Get-UninstallRegistryKey -SoftwareName "Mozilla Firefox"
$key.UninstallString
~~~

## Inputs

None

## Outputs

None

## Parameters

### -SoftwareName &lt;String&gt;
Part or all of the Display Name as you see it in Programs and Features.
It should be enough to be unique.

If the display name contains a version number, such as "Launchy 2.5",
it is recommended you use a fuzzy search "Launchy*" (the wildcard '*')
as if the version is upgraded or autoupgraded, suddenly the uninstall
script will stop working and it may not be clear as to what went wrong
at first.

Property | Value
---------------------- | --------------
Aliases |
Required? | true
Position? | 1
Default Value |
Accept Pipeline Input? | true (ByValue)

### -IgnoredArguments [&lt;Object[]&gt;]
Property | Value
---------------------- | -----
Aliases |
Required? | false
Position? | named
Default Value |
Accept Pipeline Input? | false

### &lt;CommonParameters&gt;

This cmdlet supports the common parameters: -Verbose, -Debug, -ErrorAction, -ErrorVariable, -OutBuffer, and -OutVariable. For more information, see `about_CommonParameters` http://go.microsoft.com/fwlink/p/?LinkID=113216 .


## Links

* [[Install-ChocolateyPackage|HelpersInstallChocolateyPackage]]
* [[Install-ChocolateyInstallPackage|HelpersInstallChocolateyInstallPackage]]
* [[Uninstall-ChocolateyPackage|HelpersUninstallChocolateyPackage]]


[[Function Reference|HelpersReference]]

***NOTE:*** This documentation has been automatically generated from `Import-Module "$env:ChocolateyInstall\helpers\chocolateyInstaller.psm1" -Force; Get-Help Get-UninstallRegistryKey -Full`.
1 change: 1 addition & 0 deletions docs/generated/HelpersInstallChocolateyInstallPackage.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ This cmdlet supports the common parameters: -Verbose, -Debug, -ErrorAction, -Err

* [[Install-ChocolateyPackage|HelpersInstallChocolateyPackage]]
* [[Uninstall-ChocolateyPackage|HelpersUninstallChocolateyPackage]]
* [[Get-UninstallRegistryKey|HelpersGetUninstallRegistryKey]]
* [[Start-ChocolateyProcessAsAdmin|HelpersStartChocolateyProcessAsAdmin]]


Expand Down
1 change: 1 addition & 0 deletions docs/generated/HelpersInstallChocolateyPackage.md
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ This cmdlet supports the common parameters: -Verbose, -Debug, -ErrorAction, -Err

* [[Get-ChocolateyWebFile|HelpersGetChocolateyWebFile]]
* [[Install-ChocolateyInstallPackage|HelpersInstallChocolateyInstallPackage]]
* [[Get-UninstallRegistryKey|HelpersGetUninstallRegistryKey]]
* [[Install-ChocolateyZipPackage|HelpersInstallChocolateyZipPackage]]


Expand Down
1 change: 1 addition & 0 deletions docs/generated/HelpersReference.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ These are the functions from above as one list.
* [[Get-OSArchitectureWidth|HelpersGetOSArchitectureWidth]]
* [[Get-ToolsLocation|HelpersGetToolsLocation]]
* [[Get-UACEnabled|HelpersGetUACEnabled]]
* [[Get-UninstallRegistryKey|HelpersGetUninstallRegistryKey]]
* [[Get-VirusCheckValid|HelpersGetVirusCheckValid]]
* [[Get-WebFile|HelpersGetWebFile]]
* [[Get-WebFileName|HelpersGetWebFileName]]
Expand Down
1 change: 1 addition & 0 deletions docs/generated/HelpersUninstallChocolateyPackage.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ This cmdlet supports the common parameters: -Verbose, -Debug, -ErrorAction, -Err
* [[Install-ChocolateyPackage|HelpersInstallChocolateyPackage]]
* [[Install-ChocolateyInstallPackage|HelpersInstallChocolateyInstallPackage]]
* [[Uninstall-ChocolateyZipPackage|HelpersUninstallChocolateyZipPackage]]
* [[Get-UninstallRegistryKey|HelpersGetUninstallRegistryKey]]


[[Function Reference|HelpersReference]]
Expand Down
4 changes: 3 additions & 1 deletion nuget/chocolatey/chocolatey.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ If you were using any of the functions in a non-recommended way or not compliant
* Mark packages pending until install completes successfully - see [#198](https://github.com/chocolatey/choco/issues/198)
* Resolve sources by name - see [#356](https://github.com/chocolatey/choco/issues/356)
* Uninstall-ChocolateyEnvironmentVariable function - see [#772](https://github.com/chocolatey/choco/issues/772)
* Get-UninstallRegistryKey function - see [#739](https://github.com/chocolatey/choco/issues/739)
* Pro/Business - Ubiquitous Install Directory Switch - see [#258](https://github.com/chocolatey/choco/issues/258)
* Pro/Business - Runtime Virus Scanning - see [virus scanning](https://chocolatey.org/docs/features-virus-check)
* Pro/Business - Private CDN cache for downloads - see [private CDN cache](https://chocolatey.org/docs/features-private-cdn)
Expand Down Expand Up @@ -182,7 +183,8 @@ If you were using any of the functions in a non-recommended way or not compliant
* Fix - Logging of upgrade messages - placement of some messages is incorrect - see [#557](https://github.com/chocolatey/choco/issues/557)
* Fix - Get-WebFile fails with - The term '//continue' is not recognized as the name of a cmdlet - see [#789](https://github.com/chocolatey/choco/issues/789)
* Fix - Unable to read registry snapshot file - see [#487](https://github.com/chocolatey/choco/issues/487)

* Fix - Uninstall template fails when encountering "bad" registry keys - see [#739](https://github.com/chocolatey/choco/issues/739)

### IMPROVEMENTS

* AutoUninstaller is on by default - see [#308](https://github.com/chocolatey/choco/issues/308)
Expand Down
3 changes: 3 additions & 0 deletions src/chocolatey.resources/chocolatey.resources.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@
<ItemGroup>
<EmbeddedResource Include="tools\checksum.exe.config" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="helpers\functions\Get-UninstallRegistryKey.ps1" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# Copyright © 2011 - Present RealDimensions Software, LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
#
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

function Get-UninstallRegistryKey {
<#
.SYNOPSIS
Retrieve registry key(s) for system-installed applications from an
exact or wildcard search.
.DESCRIPTION
This function will attempt to retrieve a matching registry key for an
already installed application, usually to be used with a
chocolateyUninstall.ps1 automation script.
The function also prevents `Get-ItemProperty` from failing when
handling wrongly encoded registry keys.
.NOTES
Available in 0.9.10+. If you need to maintain compatibility with pre
0.9.10, please add the following to your nuspec:
~~~xml
<dependencies>
<dependency id="chocolatey-uninstall.extension" />
</dependencies>
~~~
.INPUTS
String
.OUTPUTS
This function searches registry objects and returns PSCustomObject of
the matched key's properties.
Retrieve properties with dot notation, for example:
`$key.UninstallString`
.PARAMETER SoftwareName
Part or all of the Display Name as you see it in Programs and Features.
It should be enough to be unique.
If the display name contains a version number, such as "Launchy 2.5",
it is recommended you use a fuzzy search "Launchy*" (the wildcard '*')
as if the version is upgraded or autoupgraded, suddenly the uninstall
script will stop working and it may not be clear as to what went wrong
at first.
.EXAMPLE
>
# Software name in Programs and Features is "Gpg4Win (2.3.0)"
[array]$key = Get-UninstallRegistryKey -SoftwareName "Gpg4win*"
$key.DisplayName
.EXAMPLE
>
# Software name is "Launchy 2.5"
[array]$key = Get-UninstallRegistryKey -SoftwareName "Launchy*"
$key.UninstallString
.EXAMPLE
>
# Software name is "Mozilla Firefox"
[array]$key = Get-UninstallRegistryKey -SoftwareName "Mozilla Firefox"
$key.UninstallString
.LINK
Install-ChocolateyPackage
.LINK
Install-ChocolateyInstallPackage
.LINK
Uninstall-ChocolateyPackage
#>
[CmdletBinding()]
param(
[parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)]
[string] $softwareName,
[parameter(ValueFromRemainingArguments = $true)][Object[]] $ignoredArguments
)
Write-Debug "Running 'Get-UninstallRegistryKey' for `'$env:ChocolateyPackageName`' with SoftwareName:`'$softwareName`'";

if ($softwareName -eq $null -or $softwareName -eq '') {
throw "$SoftwareName cannot be empty for Get-UninstallRegistryKey"
}

This comment has been minimized.

Copy link
@dtgm

dtgm Jun 19, 2016

Author Contributor

@ferventcoder I don't see the throw is giving a better
message than parameter validation would....what is the reason for replacing [ValidateNotNullOrEmpty()] with ^^^?

This comment has been minimized.

Copy link
@ferventcoder

ferventcoder Jun 20, 2016

Member

ValidateNoNullOrEmpty has issues in some versions of PowerShell.

This comment has been minimized.

Copy link
@dtgm

dtgm Jun 20, 2016

Author Contributor

@ferventcoder did some searching and I could only find one bug with validatenotnullorempty detailed best http://blog.whatsupduck.net/2012/02/powershell-validatenotnullorempty-bug.html
That error doesn't occur if param is specified with [string] (tested with posh 2)
I know it is basically the same, but it is easier to read inline.


$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 up to 10 times 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
}

Set-Alias Get-InstallRegistryKey Get-UninstallRegistryKey
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ Install-ChocolateyPackage
.LINK
Uninstall-ChocolateyPackage
.LINK
Get-UninstallRegistryKey
.LINK
Start-ChocolateyProcessAsAdmin
#>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,9 @@ Get-ChocolateyWebFile
.LINK
Install-ChocolateyInstallPackage
.LINK
Get-UninstallRegistryKey
.LINK
Install-ChocolateyZipPackage
#>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ Install-ChocolateyInstallPackage
.LINK
Uninstall-ChocolateyZipPackage
.LINK
Get-UninstallRegistryKey
#>
param(
[parameter(Mandatory=$true, Position=0)][string] $packageName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,12 @@ public class ChocolateyUninstallTemplate
}
$uninstalled = $false
$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\*'
[array]$key = Get-ItemProperty -Path @($machine_key6432,$machine_key, $local_key) `
-ErrorAction SilentlyContinue `
| ? { $_.DisplayName -like ""$softwareName"" }
# Get-UninstallRegistryKey is new to 0.9.10, if supporting 0.9.9.x and below,
# take a dependency on ""chocolatey-uninstall.extension"" in your nuspec file.
# This is only a fuzzy search if $softwareName includes '*'. Otherwise it is
# exact. In the case of versions in key names, we recommend removing the version
# and using '*'.
[array]$key = Get-UninstallRegistryKey -SoftwareName $softwareName
if ($key.Count -eq 1) {
$key | % {
Expand Down
2 changes: 2 additions & 0 deletions src/chocolatey/infrastructure.app/templates/NuspecTemplate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ public class NuspecTemplate
<dependency id="""" version=""[_MIN_VERSION_INCLUSIVE, MAX_VERSION_INCLUSIVE]"" />
<dependency id="""" version=""[_MIN_VERSION_INCLUSIVE, MAX_VERSION_EXCLUSIVE)"" />
<dependency id="""" />
<!-- If supporting 0.9.9.x (or below) and including a chocolateyUninstall.ps1 file to uninstall an EXE/MSI, you probably want to uncomment the below lines. Please verify whether you are using a helper function from that package. -->
<dependency id=""chocolatey-uninstall.extension"" />
</dependencies>-->
<!--<provides>NOT YET IMPLEMENTED</provides>-->
Expand Down

0 comments on commit d607d65

Please sign in to comment.