The Data

I have gathered data starting with Dynamo 0.6.1 (the oldest version available at Dynamo Builds), and gathered data on all the stable releases and some of the daily releases (though I will only include stable releases here) through 2.0.3 (the latest version available). I have included my screen grabs of each key for reference.
It was through this data gathering that I finally realized the scope of the problem, Dynamo really isn’t one program with updates, at least from an uninstall perspective. There are multiple permutations, with different ways to identify Dynamo in the registry, different ways to uninstall, and different detritus left behind to clean up. So I’ll walk you through that data and all the pertinent and changing information.
I have yet to grok exactly how the separation of Dynamo Core from the hosted Dynamo part affects everything. So yeah, there will be a followup post at some point.

The early Dynamo’s are found in HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall, which makes sense given that the contemporary versions of Revit included 2013 & 2014, before the shift to 64 bit only. Dynamo 0.9.# only installed on Revit 2015-2017, and 2015 marked the beginning of our current x64 only Revit world, but Autodesk waited till Dynamo 1.0.0 to move to HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall. In any case, on to the data.

Dynamo 0.6.#

Dynamo 0.6.# all share a common GUID, but not a standard GUID only key name. The InstallLocation is not in Program Files, with its specialized permissions, so security is reduced simply by having Dynamo installed. The Publisher property is Autodesk, Inc., so we can’t use this alone to identify a Dynamo key, but we do have a URLInfoAbout property that will help. There is a normal UninstallString property, which is what enables the manual uninstall process from Add & Remove Programs, but conveniently there is also a QuietUninstallString property so we don’t have to go chasing down the silent uninstall argument(s). Another oddity with the uninstall; we get an uninstall EXE, much like with Notepad++, but the actual name of this file seems to change. The first Dynamo in the 0.6.# series that you install will have an unins000.exe uninstaller. But installing a new build over the top may, or may not, rename this file to unins001.exe. It doesn’t seem to happen every time, and only 000 and 001 seem to be used, but it still means we need to verify exactly which name is currently applicable, on a machine by machine basis. This is what triggers the need for more extensive PowerShell code to address the early Dynamo uninstalls.
It is also worth noting here that Microsoft best practice is NOT to include version numbers in the DisplayName property, that’s what the DisplayVersion property is for. Early Dynamo ignores this convention.

Pertinent information for our purposes:

  • Primary key = HKLM\…\WOW6432Node\…\Uninstall\{12A2BEA3-7641-4AEC-B344-9B49C8DDFF1A}_is1
    • DisplayName = Dynamo 0.6.#
    • DisplayVersion = 0.6.#
    • InstallLocation = C:\Autodesk\Dynamo\Core
    • Publisher = Autodesk, Inc.
    • QuietUninstallString = “C:\Autodesk\Dynamo\Core\Uninstall\unins00#.exe”*/SILENT
    • URLInfoAbout = dynamobim.com

* Uninstall EXE name changes between unins000.exe & unins001.exe.

Reference Registry images

| |
Dynamo 0.7.#

Dynamo 0.7.0 looks much like 0.6.#, with only a change in URLInfoAbout. Dynamo 0.7.1 then changes Publisher and InstallLocation, the latter finally moving to Program Files where it belongs. Uninstall continues to be handled by an uninstaller EXE that changes names as new builds are installed. The 0.7 series shares a GUID, but continues to not use a standard GUID only key name. It’s worth noting that the change to Dynamo as the Publisher means we can use that to identify a Dynamo Primary Key, as the property only exists in the Primary key, not the Secondary key. This means that from here on out (as long as the Publisher doesn’t change back!) we no longer need to be concerned with the URLInfoAbout property.

Pertinent information:

  • Primary key = HKLM\…\WOW6432Node\…\Uninstall\{6B5FA6CA-9D69-46CF-B517-1F90C64F7C0B}_is1
    • DisplayName = Dynamo 0.7.#
    • DisplayVersion = 0.7.#
    • InstallLocation = C:\Autodesk\Dynamo07\Core (0.7.0) C:\Program Files\Dynamo 0.7 (0.7.1+)
    • Publisher = Dynamo (0.7.1+)
    • QuietUninstallString = “…\unins00#.exe”*/SILENT
    • URLInfoAbout = dynamobim.org (0.7.0+)

* Uninstall EXE name changes between unins000.exe & unins001.exe.

| | | | |
Dynamo 0.8.#

Dynamo 0.8.# at first looks a lot like 0.7.#, but now we have a new Secondary key to be aware of, and oddly it’s found in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall rather than in WOW6432Node like the Primary key. The 0.8 series again shares a GUID, but continues to not use a standard GUID only key name. Note that the secondary key gives us a Version property that identifies the actual build number. This is the first time we have registry access to that information.
One interesting note with regards to that Secondary key. It includes an UninstallString property, just like the Primary key. And new UninstallParam property, with all new arguments. This combination works, but since so does the information in the Primary key, we CAN ignore it. Ido wonder however, at that /UPDATE flag. Update WHILE uninstalling? Um, OK Autodesk. 😉

Pertinent information:

  • Primary key = HKLM\…\WOW6432Node\…\Uninstall\{24113174-8C13-4FA5-8010-772C9144AC41}_is1
    • DisplayName = Dynamo 0.8.#
    • DisplayVersion = 0.8.#
    • InstallLocation = C:\Program Files\Dynamo 0.8
    • Publisher = Dynamo
    • QuietUninstallString = “…\unins00#.exe”1 /SILENT
  • Secondary key = HKLM\SOFTWARE\…\Uninstall\Dynamo 0.8

* Uninstall EXE name changes between unins000.exe & unins001.exe.

, | , | ,
Dynamo 0.9.#

Dynamo 0.9.# continues with the 0.8.# pattern, with a new GUID (but still a non standard key name) and changing the InstallLocation in a predictable manner. The uninstall EXE name continues to be variable.

Pertinent information:

  • Primary key = {86B8C99A-85CE-45e1-8149-0A22AAAB3792}_is1
    • DisplayName = Dynamo 0.9.#
    • DisplayVersion = 0.9.#
    • InstallLocation = C:\Program Files\Dynamo 0.9
    • Publisher = Dynamo
    • QuietUninstallString = “…\unins00#.exe”*/SILENT
  • Secondary key = HKLM\SOFTWARE\…\Uninstall\Dynamo 0.9

* Uninstall EXE name changes between unins000.exe & unins001.exe.

, | , | ,
Dynamo 1.#.#

Dynamo 1.0.0 changes… EVERYTHING! First off, Dynamo Core and Dynamo Revit are now split, with independent uninstalls. The Primary and Secondary keys have moved to HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall suggesting Dynamo is a fully 64 bit program as of 1.0.0. But Dynamo Revit now gets a Tertiary key, and it still lives in HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall, for unknown reasons.
On the bright side, both Primary keys are now normal GUID named keys, which simplifies the uninstall, since all we need to do is use the GUID as an argument for msiexec.exe. On the dark side again, Autodesk has decided that every build, both Dynamo Core and Dynamo Revit need a new GUID. Even daily builds get new GUIDs it seems. This is… madness, and utterly contrary to Microso0ft best practice, or indeed any logic I can come up with. Of course, since it’s the GUID we need for the uninstall, this complicates things, a lot more than moving to GUID based uninstalls simplified things.
Another, lesser, positive is that DisplayName now shows only the main version (e.g. Dynamo 1.0.0), while DisplayVersion now properly provides the full build number (e.g. 1.0.0.1180). For those dealing with daily builds this is super helpful.
One last tidbit. Notice in the Primary Keys below, how Dynamo Core has a NoRemove property, with a value of 1, and is missing the UninstallString property completely. Compare this to Dynamo Revit, which has NoModify & NoRepair properties (both with values of 1) and doesn’t have a NoRemove property while having a normal UninstallString property.
Those three “No” properties in conjunctions with the associated XXXString properties control what is available in the manual Add & Remove Programs tool. If you have a NoRepair property = 1 or the RepairSting property is missing, you get no Repair button in ARP. NoRepair = 0 or missing, and a valid RepairSting and you get a working Repair button. The same is true for Modify and Remove via their respective properties. So, this difference in the two Primary keys explains this difference in ARP, where Dynamo Revit CAN be uninstalled, and Dynamo Core CAN’T.

Dynamo 1.0.0 manual uninstalls

WHY Autodesk chose to do this I have no clue. It’s extra work to break perfectly normal and expected behavior, and they have maintained this behavior all the way through to the current 2.0.3 install. It’s a bit nuts. That said, it’s also not going to get in our way, because this ONLY affects manual uninstalls. Our PowerShell script is gonna tear that @%#$ing Dynamo Core right out like a weed. OK, maybe that was a bit much. But it will uninstall it. 🙂


, , , ,
, , , ,
, , , ,
, , , ,
, , , ,
, , , ,
, , , ,
, , , ,
, , , ,

Dynamo 2.#.#

Dynamo 2.#.# continues as with 1.#.#, new GUIDs for everything, crippled manual uninstall of Dynamo Core, Primary, Secondary and for Revit misplaced Tertiary keys.

, , , ,
, , , ,
, , , ,
, , , ,

The Code

Gathering Uninstall Keys

To start with, we know we have two registry keys to look in, and we can build an array of all the keys in those two keys using Get-ChildItem. Then we can loop through those keys, and verify we have the expected data.

$uninstall32 = 'HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstall64 = 'HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstallKeys = (Get-ChildItem "Registry::$uninstall64") +
(Get-ChildItem "Registry::$uninstall32")

foreach ($uninstallKey in $uninstallKeys) {
Write-Host $uninstallKey
Write-Host $uninstallKey.name
}

Identifying Dynamo Keys

$uninstall32 = 'HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstall64 = 'HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstallKeys = (Get-ChildItem "Registry::$uninstall64") +
(Get-ChildItem "Registry::$uninstall32")

foreach ($uninstallKey in $uninstallKeys) {
$keyPath = "Registry::$($uninstallKey.name)"
$publisher = (Get-ItemProperty -path:$keyPath -name:'Publisher' -errorAction:silentlyContinue).Publisher
$URLInfo = (Get-ItemProperty -path:$keyPath -name:'URLInfoAbout' -errorAction:silentlyContinue).URLInfoAbout

if (($publisher -eq 'Dynamo') -or ($URLInfo -like '*dynamobim*')) {
$displayname = (Get-ItemProperty -path:$keyPath -name:'DisplayName' -errorAction:silentlyContinue).DisplayName
Write-Host "$displayname"
}
}

Gathering Uninstall Data

$uninstall32 = 'HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstall64 = 'HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstallKeys = (Get-ChildItem "Registry::$uninstall64") +
(Get-ChildItem "Registry::$uninstall32")

foreach ($uninstallKey in $uninstallKeys) {
$keyPath = "Registry::$($uninstallKey.name)"
$publisher = (Get-ItemProperty -path:$keyPath -name:'Publisher' -errorAction:silentlyContinue).Publisher
$URLInfo = (Get-ItemProperty -path:$keyPath -name:'URLInfoAbout' -errorAction:silentlyContinue).URLInfoAbout

if (($publisher -eq 'Dynamo') -or ($URLInfo -like '*dynamobim*')) {
$uData = @{
dName = $null
pKey = $null
sKey = $null
tKey = $null
uExecutable = $null
uArguments = $null
iLocation = $null
}
$uData.dName = (Get-ItemProperty -path:$keyPath -name:'DisplayName' -errorAction:silentlyContinue).DisplayName
$uData.pKey = $keyPath
if (Test-Path "Registry::$uninstall64\$($uData.dName.subString(0, $uData.dName.length-2))") {
$uData.skey = "Registry::$uninstall64\$($uData.dName.subString(0, $uData.dName.length-2))"
}
if (Test-Path "Registry::$uninstall32\$($uData.dName)") {
$uData.tkey = "Registry::$uninstall32\$($uData.dName)"
}
$uData.iLocation = (Get-ItemProperty -path:$uData.pKey -name:'InstallLocation' -errorAction:silentlyContinue).InstallLocation

$quietUninstallString = (Get-ItemProperty -path:$uData.pKey -name:'QuietUninstallString' -errorAction:silentlyContinue).QuietUninstallString
if ($uData.pKey -match '(?<guid>\{[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}\})$') {
$uData.uExecutable = 'MsiExec.exe'
$uData.uArguments = "/x $($matches['guid']) /qb"
} elseif ($quietUninstallString -match '"(?<executable>[^"]+)"\s+(?<arguments>.+)') {
$uData.uExecutable = $matches['executable']
$uData.uArguments = $matches['arguments']
}

Write-Host "$($uData.dName)"
Write-Host "  p:$($uData.pKey)"
Write-Host "  s:$($uData.sKey)"
Write-Host "  t:$($uData.tKey)"
Write-Host "  l:$($uData.iLocation)"
Write-Host "  e:$($uData.uExecutable) $($uData.uArguments)"
}
}

Dynamo Uninstalls

Now that we have our data, we can do the actual uninstalls, which I will detail below, since that is where the new code occurs.

$uninstall32 = 'HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstall64 = 'HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstallKeys = (Get-ChildItem "Registry::$uninstall64") +
(Get-ChildItem "Registry::$uninstall32")

foreach ($uninstallKey in $uninstallKeys) {
$keyPath = "Registry::$($uninstallKey.name)"
$publisher = (Get-ItemProperty -path:$keyPath -name:'Publisher' -errorAction:silentlyContinue).Publisher
$URLInfo = (Get-ItemProperty -path:$keyPath -name:'URLInfoAbout' -errorAction:silentlyContinue).URLInfoAbout

if (($publisher -eq 'Dynamo') -or ($URLInfo -like '*dynamobim*')) {
$uData = @{
dName = $null
pKey = $null
sKey = $null
tKey = $null
uExecutable = $null
uArguments = $null
iLocation = $null
}
$uData.dName = (Get-ItemProperty -path:$keyPath -name:'DisplayName' -errorAction:silentlyContinue).DisplayName
$uData.pKey = $keyPath
if (Test-Path "Registry::$uninstall64\$($uData.dName.subString(0, $uData.dName.length-2))") {
$uData.skey = "Registry::$uninstall64\$($uData.dName.subString(0, $uData.dName.length-2))"
}
if (Test-Path "Registry::$uninstall32\$($uData.dName)") {
$uData.tkey = "Registry::$uninstall32\$($uData.dName)"
}
$uData.iLocation = (Get-ItemProperty -path:$uData.pKey -name:'InstallLocation' -errorAction:silentlyContinue).InstallLocation

$quietUninstallString = (Get-ItemProperty -path:$uData.pKey -name:'QuietUninstallString' -errorAction:silentlyContinue).QuietUninstallString
if ($uData.pKey -match '(?<guid>\{[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}\})$') {
$uData.uExecutable = 'MsiExec.exe'
$uData.uArguments = "/x $($matches['guid']) /qb"
} elseif ($quietUninstallString -match '"(?<executable>[^"]+)"\s+(?<arguments>.+)') {
$uData.uExecutable = $matches['executable']
$uData.uArguments = $matches['arguments']
}

if ($uData.uExecutable -and $uData.uArguments) {
$process = (Start-Process -filePath:$uData.uExecutable -argumentList:$uData.uArguments -passThru -wait)
if ($process.exitCode -eq 0) {
Write-Host "Uninstalled $($uData.dName)"
} else {
Write-Host "Failed to uninstall $($uData.dName) with exit code $($process.exitCode)"
}
}
}
}

To handle the actual installs we have an if to ensure we only attempt an uninstall if we have both

The whole Shebang

1$uninstall32 = 'HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
2$uninstall64 = 'HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall'
3$uninstallKeys = (Get-ChildItem "Registry::$uninstall64") +
4(Get-ChildItem "Registry::$uninstall32")
5$specialFolders = New-Object collections.arrayList
6foreach ($specialFolderName in [enum]::GetNames([Environment+SpecialFolder])) {
7if ((($specialFolderPath = [environment]::getfolderpath($specialFolderName)) -ne '') -and ($specialFolders -NotContains $specialFolderPath)) {
8[void]$specialFolders.Add($specialFolderPath)
9}
10}
11
12foreach ($uninstallKey in $uninstallKeys) {
13$keyPath = "Registry::$($uninstallKey.name)"
14$publisher = (Get-ItemProperty -path:$keyPath -name:'Publisher' -errorAction:silentlyContinue).Publisher
15$URLInfo = (Get-ItemProperty -path:$keyPath -name:'URLInfoAbout' -errorAction:silentlyContinue).URLInfoAbout
16
17if (($publisher -eq 'Dynamo') -or ($URLInfo -like '*dynamobim*')) {
18$uData = @{
19dName = $null
20pKey = $null
21sKey = $null
22tKey = $null
23uExecutable = $null
24uArguments = $null
25iLocation = $null
26}
27$uData.dName = (Get-ItemProperty -path:$keyPath -name:'DisplayName' -errorAction:silentlyContinue).DisplayName
28$uData.pKey = $keyPath
29if (Test-Path "Registry::$uninstall64\$($uData.dName.subString(0, $uData.dName.length-2))") {
30$uData.skey = "Registry::$uninstall64\$($uData.dName.subString(0, $uData.dName.length-2))"
31}
32if (Test-Path "Registry::$uninstall32\$($uData.dName)") {
33$uData.tkey = "Registry::$uninstall32\$($uData.dName)"
34}
35$uData.iLocation = (Get-ItemProperty -path:$uData.pKey -name:'InstallLocation' -errorAction:silentlyContinue).InstallLocation
36
37$quietUninstallString = (Get-ItemProperty -path:$uData.pKey -name:'QuietUninstallString' -errorAction:silentlyContinue).QuietUninstallString
38if ($uData.pKey -match '(?<guid>\{[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}\})$') {
39$uData.uExecutable = 'MsiExec.exe'
40$uData.uArguments = "/x $($matches['guid']) /qb"
41} elseif ($quietUninstallString -match '"(?<executable>[^"]+)"\s+(?<arguments>.+)') {
42$uData.uExecutable = $matches['executable']
43$uData.uArguments = $matches['arguments']
44}
45
46if ($uData.uExecutable -and $uData.uArguments) {
47$process = (Start-Process -filePath:$uData.uExecutable -argumentList:$uData.uArguments -passThru -wait)
48if ($process.exitCode -eq 0) {
49Write-Host "Uninstalled $($uData.dName)"
50
51if ($uData.pKey -and (Test-Path $uData.pKey)) {
52try {
53Remove-Item $uData.pKey -force -errorAction:stop
54Write-Host "  Deleted Primary Key: $($uData.pKey)"
55} catch {
56Write-Host "  Delete key failed: $($_.Exception.Message)"
57}
58}
59if ($uData.sKey -and (Test-Path $uData.sKey)) {
60try {
61Remove-Item $uData.sKey -force -errorAction:stop
62Write-Host "  Deleted Secondary Key: $($uData.sKey)"
63} catch {
64Write-Host "  Delete key failed: $($_.Exception.Message)"
65}
66}
67if ($uData.tKey -and (Test-Path $uData.tKey)) {
68try {
69Remove-Item $uData.tKey -force -errorAction:stop
70Write-Host "  Deleted Tertiary Key: $($uData.tKey)"
71} catch {
72Write-Host "  Delete key failed: $($_.Exception.Message)"
73}
74}
75if ($uData.iLocation -and (Test-Path $uData.iLocation)) {
76try {
77Remove-Item "$($uData.iLocation)\*" -recurse -force -errorAction:stop
78Write-Host "  Deleted Install Location contents: $($uData.iLocation)"
79} catch {
80Write-Host "  Delete folder contents failed: $($_.Exception.Message)"
81}
82$candidateFolderToDelete = $uData.iLocation
83:deleteEmptyFolders while ((Get-ChildItem -path:$candidateFolderToDelete -force).count -eq 0) {
84try {
85Remove-Item $candidateFolderToDelete -recurse -force -errorAction:stop
86Write-Host "  Deleted: $candidateFolderToDelete"
87$candidateFolderToDelete = Split-Path $candidateFolderToDelete -parent
88if ($SpecialFolders -contains $candidateFolderToDelete) {
89break :deleteEmptyFolders
90}
91} catch {
92break :deleteEmptyFolders
93}
94}
95}
96} else {
97Write-Host "Failed to uninstall $($uData.dName) with exit code $($process.exitCode)"
98}
99}
100}
101}
102
103# Orphans
104$orphans = @('C:\Program Files\Dynamo', 'C:\Program Files\Dynamo 0.9')
105$orphanHeader = "`n Deleted orphan(s)"
106foreach ($orphan in $orphans) {
107if (Test-Path $orphan) {
108try {
109Remove-Item $orphan -force -recurse -errorAction:stop
110if ($orphanHeader) {
111Write-Host $orphanHeader
112$orphanHeader = $null
113}
114Write-Host "    Deleted $orphan"
115} catch {
116if ($orphanHeader) {
117Write-Host $orphanHeader
118$orphanHeader = $null
119}
120Write-Host "  Delete folder failed: $($_.Exception.Message)"
121}
122}
123}
 


1.0
$uninstall32 = 'HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstall64 = 'HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstallKeys = (Get-ChildItem "Registry::$uninstall64") +
(Get-ChildItem "Registry::$uninstall32")

foreach ($uninstallKey in $uninstallKeys) {
Write-Host "$uninstallKey"
Write-Host "$($uninstallKey.name)"
}
 

1.1
$uninstall32 = 'HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstall64 = 'HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstallKeys = (Get-ChildItem "Registry::$uninstall64") +
(Get-ChildItem "Registry::$uninstall32")

foreach ($uninstallKey in $uninstallKeys) {
$keyPath = "Registry::$($uninstallKey.name)"
$publisher = (Get-ItemProperty -path:$keyPath -name:'Publisher' -errorAction:silentlyContinue).Publisher
$URLInfo = (Get-ItemProperty -path:$keyPath -name:'URLInfoAbout' -errorAction:silentlyContinue).URLInfoAbout
if (($publisher -eq 'Dynamo') -or ($URLInfo -like '*dynamobim*')) {
$displayname = (Get-ItemProperty -path:"Registry::$keyPath" -name:'DisplayName' -errorAction:silentlyContinue).DisplayName
Write-Host "$displayname"
}
}
 
1.2
$uninstall32 = 'HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstall64 = 'HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstallKeys = (Get-ChildItem "Registry::$uninstall64") +
(Get-ChildItem "Registry::$uninstall32")

foreach ($uninstallKey in $uninstallKeys) {
$keyPath = "Registry::$($uninstallKey.name)"
$publisher = (Get-ItemProperty -path:$keyPath -name:'Publisher' -errorAction:silentlyContinue).Publisher
$URLInfo = (Get-ItemProperty -path:$keyPath -name:'URLInfoAbout' -errorAction:silentlyContinue).URLInfoAbout
if (($publisher -eq 'Dynamo') -or ($URLInfo -like '*dynamobim*')) {
$uData = @{
dName = $null
pKey = $null
sKey = $null
tKey = $null
uExecutable = $null
uArguments = $null
iLocation = $null
}

$uData.dName = (Get-ItemProperty -path:$keyPath -name:'DisplayName' -errorAction:silentlyContinue).DisplayName
$uData.pKey = $keyPath
$uData.sKey = "Registry::$uninstall64\$($uData.dName.subString(0, $uData.dName.length-2))"
if (Test-Path "Registry::$uninstall32\$($uData.dName)") {
$uData.tkey = "Registry::$($uData.dName)"
}
$uData.iLocation = (Get-ItemProperty -path:$uData.pKey -name:'InstallLocation' -errorAction:silentlyContinue).InstallLocation

$quietUninstallString = (Get-ItemProperty -path:$uData.pKey -name:'QuietUninstallString' -errorAction:silentlyContinue).QuietUninstallString

if ($uData.pKey -match '(?<guid>\{[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}\})$') {
$uData.uExecutable = 'MsiExec.exe'
$uData.uArguments = "/x $($matches['guid']) /qb"
} elseif ($quietUninstallString -match '"(?<executable>[^"]+)"\s+(?<arguments>.+)') {
$uData.uExecutable = $matches['executable']
$uData.uArguments = $matches['arguments']
}

Write-Host "$($uData.dName)"
Write-Host "   p:$($uData.pKey)"
Write-Host "   s:$($uData.sKey)"
Write-Host "   t:$($uData.tKey)"
Write-Host "   l:$($uData.iLocation)"
Write-Host "   e:$($uData.uExecutable) $($uData.uArguments)"
}
}
 
1.3
$uninstall32 = 'HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstall64 = 'HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstallKeys = (Get-ChildItem "Registry::$uninstall64") +
(Get-ChildItem "Registry::$uninstall32")

foreach ($uninstallKey in $uninstallKeys) {
$keyPath = "Registry::$($uninstallKey.name)"
$publisher = (Get-ItemProperty -path:$keyPath -name:'Publisher' -errorAction:silentlyContinue).Publisher
$URLInfo = (Get-ItemProperty -path:$keyPath -name:'URLInfoAbout' -errorAction:silentlyContinue).URLInfoAbout
if (($publisher -eq 'Dynamo') -or ($URLInfo -like '*dynamobim*')) {
$uData = @{
dName = $null
pKey = $null
sKey = $null
tKey = $null
uExecutable = $null
uArguments = $null
iLocation = $null
}

$uData.dName = (Get-ItemProperty -path:$keyPath -name:'DisplayName' -errorAction:silentlyContinue).DisplayName
$uData.pKey = $keyPath
$uData.sKey = "Registry::$uninstall64\$($uData.dName.subString(0, $uData.dName.length-2))"
if (Test-Path "Registry::$uninstall32\$($uData.dName)") {
$uData.tkey = "Registry::$($uData.dName)"
}
$uData.iLocation = (Get-ItemProperty -path:$uData.pKey -name:'InstallLocation' -errorAction:silentlyContinue).InstallLocation

$quietUninstallString = (Get-ItemProperty -path:$uData.pKey -name:'QuietUninstallString' -errorAction:silentlyContinue).QuietUninstallString

if ($uData.pKey -match '(?<guid>\{[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}\})$') {
$uData.uExecutable = 'MsiExec.exe'
$uData.uArguments = "/x $($matches['guid']) /qb"
} elseif ($quietUninstallString -match '"(?<executable>[^"]+)"\s+(?<arguments>.+)') {
$uData.uExecutable = $matches['executable']
$uData.uArguments = $matches['arguments']
}

if ($uData.uExecutable -and $uData.uArguments) {
$process = (Start-Process -filePath:$uData.uExecutable -argumentList:$uData.uArguments -passThru -wait)
if ($process.exitCode -eq 0) {
Write-Host "Uninstalled $($uData.dName)"
} else {
Write-Host "Failed to uninstall $($uData.dName) with exit code $($process.exitCode)"
}
}
}
}
 
1.4
$uninstall32 = 'HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstall64 = 'HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstallKeys = (Get-ChildItem "Registry::$uninstall64") +
(Get-ChildItem "Registry::$uninstall32")

$specialFolders = New-Object collections.arrayList
foreach ($specialFolderName in GetNames([Environment+SpecialFolder])) {
if ((($specialFolderPath = [environment]::getfolderpath($specialFolderName)) -ne '') -and ($specialFolders -NotContains $specialFolderPath)) {
[void]$specialFolders.Add($specialFolderPath)
}
}

foreach ($uninstallKey in $uninstallKeys) {
$keyPath = "Registry::$($uninstallKey.name)"
$publisher = (Get-ItemProperty -path:$keyPath -name:'Publisher' -errorAction:silentlyContinue).Publisher
$URLInfo = (Get-ItemProperty -path:$keyPath -name:'URLInfoAbout' -errorAction:silentlyContinue).URLInfoAbout
if (($publisher -eq 'Dynamo') -or ($URLInfo -like '*dynamobim*')) {
$uData = @{
dName = $null
pKey = $null
sKey = $null
tKey = $null
uExecutable = $null
uArguments = $null
iLocation = $null
}

$uData.dName = (Get-ItemProperty -path:$keyPath -name:'DisplayName' -errorAction:silentlyContinue).DisplayName
$uData.pKey = $keyPath
$uData.sKey = "Registry::$uninstall64\$($uData.dName.subString(0, $uData.dName.length-2))"
if (Test-Path "Registry::$uninstall32\$($uData.dName)") {
$uData.tkey = "Registry::$($uData.dName)"
}
$uData.iLocation = (Get-ItemProperty -path:$uData.pKey -name:'InstallLocation' -errorAction:silentlyContinue).InstallLocation

$quietUninstallString = (Get-ItemProperty -path:$uData.pKey -name:'QuietUninstallString' -errorAction:silentlyContinue).QuietUninstallString

if ($uData.pKey -match '(?<guid>\{[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}\})$') {
$uData.uExecutable = 'MsiExec.exe'
$uData.uArguments = "/x $($matches['guid']) /qb"
} elseif ($quietUninstallString -match '"(?<executable>[^"]+)"\s+(?<arguments>.+)') {
$uData.uExecutable = $matches['executable']
$uData.uArguments = $matches['arguments']
}

if ($uData.uExecutable -and $uData.uArguments) {
$process = (Start-Process -filePath:$uData.uExecutable -argumentList:$uData.uArguments -passThru -wait)

if ($process.exitCode -eq 0) {
Write-Host "Uninstalled $($uData.dName)"

pKey

if ($uData.pKey -and (Test-Path $uData.pKey)) {
try {
Remove-Item $uData.pKey -force -errorAction:stop
Write-Host "    Deleted Primary Key: $($uData.pKey)"
} catch {
Write-Host "    Delete key failed: $($_.Exception.Message)"
}
}

sKey

if ($uData.sKey -and (Test-Path $uData.sKey)) {
try {
Remove-Item $uData.sKey -force -errorAction:stop
Write-Host "    Deleted Secondary Key: $($uData.sKey)"
} catch {
Write-Host "    Delete key failed: $($_.Exception.Message)"
}
}

tKey

if ($uData.tKey -and (Test-Path $uData.tKey)) {
try {
Remove-Item $uData.tKey -force -errorAction:stop
Write-Host "    Deleted Tertiary Key: $($uData.tKey)"
} catch {
Write-Host "    Delete key failed: $($_.Exception.Message)"
}
}

if ($uData.iLocation -and (Test-Path $uData.iLocation)) {
try {
Remove-Item "$($uData.iLocation)\*" -recurse -force -errorAction:stop
Write-Host "    Deleted Install Location contents: $($uData.iLocation)"
} catch {
Write-Host "    Delete folder contents failed: $($_.Exception.Message)"
}
$candidateFolderToDelete = $uData.iLocation
:deleteEmptyFolders while ((Get-ChildItem -path:$candidateFolderToDelete -force).count -eq 0) {
try {
Remove-Item $candidateFolderToDelete -recurse -force -errorAction:stop
Write-Host "    Deleted: $candidateFolderToDelete"
$candidateFolderToDelete = Split-Path $candidateFolderToDelete -parent
if ($SpecialFolders -contains $candidateFolderToDelete) {
break :deleteEmptyFolders
}
} catch {
break :deleteEmptyFolders
}
}
}
} else {
Write-Host "Failed to uninstall $($uData.dName) with exit code $($process.exitCode)"
}
}
}
}


# Orphans
$orphans = @('C:\Program Files\Dynamo', 'C:\Program Files\Dynamo 0.9')
$orphanHeader = "`n Deleted orphan(s)"
foreach ($orphan in $orphans) {
if (Test-Path $orphan) {
try {
Remove-Item $orphan -force -recurse -errorAction:stop
if ($orphanHeader) {
Write-Host $orphanHeader
$orphanHeader = $null
}
Write-Host "    Deleted $orphan"
} catch {
if ($orphanHeader) {
Write-Host $orphanHeader
$orphanHeader = $null
}
Write-Host "    Delete folder failed: $($_.Exception.Message)"
}
}
}

3}
23}
123}

3}
23}
123}


 
 
WIDTH TEST
$uninstallString -name:'QuietUninstallString' -errorAction:silentlyContinue).QuietUninstallString

$QuietUninstallString = (Get-ItemProperty -path:"Registry::$uData.pKey -name:'QuietUninstallString' -errorAction:silentlyContinue).QuietUninstallString



Some code
Some more code
Even more code

Some code
Some more code
Even more code

STACK EXCHANGE CAN IGNORE EVERYTHING BELOW HERE

Some code
Some more code
Even more code


Some code
Some more code
Even more code


$uninstall32 = 'HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstall64 = 'HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstallKeys = (Get-ChildItem "Registry::$uninstall64") +
(Get-ChildItem "Registry::$uninstall32")
foreach ($uninstallKey in $uninstallKeys) {
Write-Host "$uninstallKey"
}

New test
$uninstall32 = 'HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstall64 = 'HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstallKeys = (Get-ChildItem "Registry::$uninstall64") +
(Get-ChildItem "Registry::$uninstall32")
foreach ($uninstallKey in $uninstallKeys) {
Write-Host "$uninstallKey"
}

$uninstall32 = 'HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstall64 = 'HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstallKeys = (Get-ChildItem "Registry::$uninstall64") +
(Get-ChildItem "Registry::$uninstall32")

foreach ($uninstallKey in $uninstallKeys) {
Write-Host "$uninstallKey"
}

THREE
$uninstall32 = 'HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstall64 = 'HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstallKeys = (Get-ChildItem "Registry::$uninstall64") +
(Get-ChildItem "Registry::$uninstall32")
foreach ($uninstallKey in $uninstallKeys) {
Write-Host "$uninstallKey"
}



$uninstall32 = 'HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstall64 = 'HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstallKeys = (Get-ChildItem "Registry::$uninstall64") +
(Get-ChildItem "Registry::$uninstall32")

foreach ($uninstallKey in $uninstallKeys) {
Write-Host "$uninstallKey"
}


$uninstall32 = 'HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstall64 = 'HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstallKeys = (Get-ChildItem "Registry::$uninstall64") +
(Get-ChildItem "Registry::$uninstall32")

foreach ($uninstallKey in $uninstallKeys) {
Write-Host "$uninstallKey"
}

TESTING

$uninstall32 = 'HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstall64 = 'HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstallKeys = (Get-ChildItem "Registry::$uninstall64") +
(Get-ChildItem "Registry::$uninstall32")

foreach ($uninstallKey in $uninstallKeys) {
Write-Host "$uninstallKey"
}

TESTING


$uninstall32 = 'HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstall64 = 'HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstallKeys = (Get-ChildItem "Registry::$uninstall64") +
(Get-ChildItem "Registry::$uninstall32")
foreach ($uninstallKey in $uninstallKeys) {

$keyPath = $uninstallKey.name
$publisher = (Get-ItemProperty -path:"Registry::$keyPath" -name:'Publisher' -errorAction:silentlyContinue).Publisher
$URLInfo = (Get-ItemProperty -path:"Registry::$keyPath" -name:'URLInfoAbout' -errorAction:silentlyContinue).URLInfoAbout

if (($publisher -eq 'Dynamo') -or ($URLInfo -like '*dynamobim*')) {
$displayname = (Get-ItemProperty -path:"Registry::$keyPath" -name:'DisplayName' -errorAction:silentlyContinue).DisplayName
Write-Host "$displayname"
}

}


foreach ($uninstallKey in $uninstallKeys) {
Write-Host "$uninstallKey"
}
Write-Host "$displayname"

Get-AppXProvisionedPackage -online | Where-Object {$_.PackageName -like "Microsoft.XboxApp*"} | Get-AppxProvisionedPackage -online
Get-AppXProvisionedPackage -online | Where-Object {$_.PackageName -like "Microsoft.XboxApp*"} | Get-AppxProvisionedPackage -online
Get-AppXProvisionedPackage -online | Where-Object {$_.PackageName -like "Microsoft.XboxApp*"} | Get-AppxProvisionedPackage -online
Get-AppXProvisionedPackage -online | Where-Object {$_.PackageName -like "Microsoft.XboxApp*"} | Get-AppxProvisionedPackage -online

<Sets>
<Set id="NWM_2018">Apply Set for Rollout testing
<Rollout>NWM_2018</Rollout>Rollout Package reference
</Set>
<Set id="NWM_2018-X">Apply Set for Remove testing
<Remove>NWM_2018</Remove>Remove package reference
</Set>
</Sets>

<Sets>
<Set id="NWM_2018">
<Rollout>NWM_2018</Rollout>Rollout Package reference
</Set>
<Set id="NWM_2018-X">Apply Set for Remove testing
<Remove>NWM_2018</Remove>Remove package reference
</Set>
</Sets>

Bluebeam Revu 2019

Bluebeam Revu 2019

Bluebeam Revu 2019

Bluebeam Revu 2019