Uninstall all but most recent two versions

For an application that installs new versions alongside existing versions, we want to remove all but the two most recent. I have this bit of relevance to get the version and name pairings:

q: ((value “DisplayVersion” of it as string, Name of it as string) of keys whose ((value “DisplayName” of it as string as lowercase starts with “Tableau” as lowercase) AND (value “UninstallString” of it as string as lowercase contains “msiexec”)) of keys “HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall” of (x32 registries; x64 registries))
A: 19.3.510, {10A385DC-BC76-40C0-AA57-E9B78A7E7C6B}
A: 20.4.1116, {6D73F31A-5428-4FA9-AF4F-465CDA0B0A44}
A: 20.2.25203, {76106BC3-8B70-441F-98CE-D448FCEAFD18}
A: 18.3.419, {E3D06A5A-E251-49CB-9988-E07259F978AD}

I want to sort these by the first column, throw out the greatest two lines, and return the second column of whatever remains. I think it’s a matter of making a set and then walking the array but… how?

This one turned out to be a bit tricky. I’ve had cases where we need to retrieve the “highest version among registry keys” and “all but the highest version”, but not a need to retrieve all but the top 2 versions.

If we have a set of strings, we can build a set that excludes the highest value (after casting to a version):

q: ((maxima of (elements of it as version) as string)  ) of  set of ("19.3.510";"20.4.1116";"20.2.25203";"18.3.419")
A: 20.4.1116
T: 0.090 ms
I: plural string

And we can remove that one maxima value from the set through a set subtraction:

q: (elements of (it - set of (maxima of (elements of it as version) as string)  )) of  set of ("19.3.510";"20.4.1116";"20.2.25203";"18.3.419")
A: 18.3.419
A: 19.3.510
A: 20.2.25203
T: 0.117 ms
I: plural string

We can repeat that to remove the top two elements from the set:

q: elements of (it - set of (maxima of (elements of it as version) as string)  )  of (it - set of (maxima of (elements of it as version) as string)  ) of  set of ("19.3.510";"20.4.1116";"20.2.25203";"18.3.419")
A: 18.3.419
A: 19.3.510
T: 0.141 ms
I: plural string

I built a set of fake registry keys on my machine to retrieve these values from the Registry.

q: ((value "DisplayVersion" of it as string, Name of it as string) of keys whose ((value "DisplayName" of it as string as lowercase starts with "Tableau" as lowercase) AND (value "UninstallString" of it as string as lowercase contains "msiexec")) of keys "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" of (x32 registries; x64 registries))
A: 19.3.510, tableau1
A: 20.4.1116, tableau2
A: 20.2.25203, tableau3
A: 18.3.419, tableau4
T: 8.207 ms
I: plural ( string, string )

I’m indenting this relevant for a little bit more readability. Here I add the maxima and set subtraction logic to get the registry keys for all but the highest two versions:

items 1 of (
   it
   , keys whose (
        value "DisplayName" of it as string as lowercase starts with "Tableau" as lowercase 
        AND value "UninstallString" of it as string as lowercase contains "msiexec"
         )  of keys "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" of (x32 registries; x64 registries)
   ) whose (value "DisplayVersion" of item 1 of it as string is contained by item 0 of it)  of (
    it - set of (maxima of (elements of it as version) as string)
    )  of (
    it - set of (maxima of (elements of it as version) as string)  
    )  of 
    set of (values "DisplayVersion" of it as string) of keys whose (
        value "DisplayName" of it as string as lowercase starts with "Tableau" as lowercase
        AND value "UninstallString" of it as string as lowercase contains "msiexec"
        )  of keys "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" of (x32 registries; x64 registries)

A: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\tableau1
A: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\tableau4
4 Likes

Nice! Thank you!

I was thinking about “count the number of items, identify N and N-1, subtract them, extract the remaining items”. I didn’t even think of just getting the maximum and chucking it twice. :smiley:

It should be trivial to wrap this in an if{N>2} statement.

-Andrew

1 Like

Actually I need the Name of the key, not the key itself. But I think I can suss that out.

names of items 1 of ..

1 Like

If you know the old versions, this may help with uninstalling.

parameter "exists1"="{names of keys whose (value "DisplayName" of it as string contains "Tableau" and value "DisplayVersion" of it as string contains "18.3") of keys "HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall" of (x32 registries; x64 registries)}"

if {exists keys whose (value "DisplayName" of it as string contains "Tableau" and value "DisplayVersion" of it as string contains "18.3") of keys "HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall" of (x32 registries; x64 registries)}
waithidden msiexec.exe /X{parameter "exists1" & " /qn"}
endif

Well, I guess this is turning into a Tableau thread. :smiley:

We’ve been maintaining Tableau install and uninstall templates for some time:

Relevance:

exist keys whose( (exists values "DisplayName" whose(it as string as lowercase starts with "REPLACE" as lowercase) of it) AND (exists values "QuietUninstallString" of it) ) of keys "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" of ( x64 registries; x32 registries )

Actionscript:

parameter "theDisplayName" = "REPLACE"
waithidden {values "QuietUninstallString" of keys whose( (exists values "DisplayName" whose(it as string as lowercase starts with (parameter "theDisplayName") as lowercase) of it) AND (exists values "QuietUninstallString" of it) ) of keys "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" of ( x64 registries; x32 registries )}

We swap in Tableau’s full DisplayName value in place of the REPLACE strings.

Here’s the finished fixlet:

Relevance:

(windows of operating system) AND ((it > 2) of (number of (keys whose ( (exists values "DisplayName" whose(it as string as lowercase starts with "Tableau" as lowercase) of it) AND (exists values "QuietUninstallString" of it) ) of keys "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" of (x32 registries; x64 registries))))

Action script:

if { (it > 2) of (number of (keys whose ( (exists values "DisplayName" whose(it as string as lowercase starts with "Tableau" as lowercase) of it) AND (exists values "QuietUninstallString" of it) )  of keys "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" of (x32 registries; x64 registries))) }
	delete __createfile
	createfile until -30-
{concatenation "%0a" of ((value "QuietUninstallString" of it as string) of (items 1 of (it, keys whose ( (exists values "DisplayName" whose(it as string as lowercase starts with "Tableau" as lowercase) of it) AND (exists values "QuietUninstallString" of it)  ) of keys "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" of (x32 registries; x64 registries)) whose (value "DisplayVersion" of item 1 of it as string is contained by item 0 of it)  of (it - set of (maxima of (elements of it as version) as string))  of (it - set of (maxima of (elements of it as version) as string)  )  of set of (values "DisplayVersion" of it as string) of keys whose ( (exists values "DisplayName" whose(it as string as lowercase starts with "Tableau" as lowercase) of it) AND (exists values "QuietUninstallString" of it) )  of keys "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" of (x32 registries; x64 registries)))}
	-30-
	delete sc_delete.bat
	copy __createfile sc_delete.bat
 	waithidden cmd /c sc_delete.bat
endif
2 Likes

Modified the relevance using the tuple item string set technique to only pull the reg keys once:

q: (elements of it, elements of (it - set of (maxima of (elements of it as version) as string)) of (it - set of (maxima of (elements of it as version) as string)) of set of tuple string items 1 of elements of it) whose (tuple string item 1 of item 0 of it = item 1 of it) of set of (it as string) of (values "DisplayName" of it as string, values "DisplayVersion" of it as string, values "UninstallString" of it as string) of keys whose (value "DisplayName" of it as string as lowercase starts with "Tableau" as lowercase AND value "UninstallString" of it as string as lowercase contains "msiexec")  of keys "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" of (x32 registries; x64 registries)
A: ( Tableau1, 1.1.0.0, msiexec.exe /x something1 ), 1.1.0.0
A: ( Tableau2, 1.2.0.0, msiexec.exe /x something2 ), 1.2.0.0
T: 16.439 ms
I: plural ( string, string )

q:  tuple string items 2 of items 0 of (elements of it, elements of (it - set of (maxima of (elements of it as version) as string)) of (it - set of (maxima of (elements of it as version) as string)) of set of tuple string items 1 of elements of it) whose (tuple string item 1 of item 0 of it = item 1 of it) of set of (it as string) of (values "DisplayName" of it as string, values "DisplayVersion" of it as string, values "UninstallString" of it as string) of keys whose (value "DisplayName" of it as string as lowercase starts with "Tableau" as lowercase AND value "UninstallString" of it as string as lowercase contains "msiexec")  of keys "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" of (x32 registries; x64 registries)
A: msiexec.exe /x something1
A: msiexec.exe /x something2
T: 8.074 ms
3 Likes