2

I tried asking this once, but I think there was some confusion so I'm going to try this fresh. Here is the code:

set regVar=HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
set excluded=Microsoft MDOP Dell 
set count=0
for /f "tokens=2,*" %%a in ('Reg Query %regVar% /S^|find " DisplayName" ^| findstr /v "%excluded%"') do (
    set /a count+=1
    setlocal EnableDelayedExpansion
    for %%n in (!count!) do (
        endlocal
        set product[%%n]=%%b
        echo  %%n.%tab%%%b
        echo  %%a
    )   
)

What this does is go through all uninstall registries and then adds each DisplayName key into an array called product[] and then prints the result for the current number it's on i the loop to the screen for a menu. What you can then do is type in the number, and it returns the Display name in that array:

So this as it stands filters out everything microsoft, mdop, and dell

What I want it to do is ALSO look in that key for that DisplayName and return another key called UninstallString and assign that to the array uninstallprod[] with the same associated index. The issue I'm running into is that I cant figure out how to either query using a statement like you can in sql

Select UninstallString from REGISTRY where DisplayName="Program name"

with program name being the string returned from the DisplayName you selected, or to have a SINGLE query that will allow me to both filter out the DisplayNames like you see above and also include the UninstallString value in the same statement. The problem with trying it any other way is I can't filter and do both arrays without it ending up with indexes completely wrong (it'll skip a display name but still assign the uninstallstring because its not letting me filter both)

UPDATE:

At this point I have it pulling back both a DisplayName and Uninstall string, but it's not advancing the counter correctly. In it's current state, it assigns 2 variables, the name of the key (%%a) to use for assigning and comparing what the key is, and the value of the key (%%c) which is either the name of the program or the uninstall path (string) to use for uninstallation later in the script. So what ends up happening is that I end up with a list of skipped numbers because the counter is getting advanced even when there is no displayname being printed to the list. Here is the code I am now working with.

@echo off
set tab=    
set regVar=HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
set excluded=Microsoft MDOP Dell 
set count=1
for /f "tokens=1,2,*" %%a in ('Reg Query %regVar% /S^|findstr "DisplayName UninstallString" ^| findstr /v "%excluded%"') do (
    setlocal EnableDelayedExpansion
    for %%n in (!count!) do (
        endlocal
        if "%%a"=="DisplayName" set product[%%n]=%%c&echo %%n.%tab%%%c
        if "%%a"=="UninstallString" if defined product[%%n] set uninstall[%%n]==%%c&set/a count+=1
    )
)

Just as an example, I have Sublime Text 2.0.1 and 64 bit HP CIO Components Installer on my list. What ends up happening is I get a list like this:

1.  Sublime Text 2.0.1
3.  64 bit HP CIO Components Installer

So for some reason the counter is getting advanced past 2 even though no product[2] is being defined. And in the 32 bit version I have 5 entries showing up, one of them is blank, which prints like this:

1.  Adobe Flash Player 11 ActiveX
2.  Google Chrome
3.  TeamViewer 8
4.  
4.  Google Update Helper.
Flynn
  • 193
  • 2
  • 5
  • 18
  • A number of problems - `some` of which I've tackled with my update yesterday. It's possible for the DisplayName to appear after the UninstallString (maybe) - There may be multiple Uninstall strings (perhaps able to filter-out `REG_EXPAND_SZ` ?) and Quietuninstallstring also passes through teh filter. Since you don't `SETLOCAL` at the start of your batch, subsequent runs are dealing with a dirty environment - values set that you are assuming are clear. Note also my `exclusions` are different - `microsoft` often occurs in uninstall paths and hence is illegally excluded. – Magoo Apr 18 '13 at 17:47

1 Answers1

2

You'd need to let both "UninstallString" and "Displayname" through the initial gate, so change

for /f "tokens=2,*" %%a in ('Reg Query %regVar% /S^|find " DisplayName" "...

to

for /f "tokens=1,2,*" %%a in ('Reg Query %regVar% /S^|FINDSTR "Displayname UninstallString"...

Which will set %%a to Displayname or UninstallString and %%c to the data value required.

THEN you have to deal with the sequence

Displayname x
Displayname y
Uninstall y

or

Displayname y
Uninstall y
Uninstall z

Which actually isn't particularly hard.

You are already setting Product[n] - you need to do this for every %%a==Displayname. Repeat for Uninstall[n] - do this for every %%a==UninstallString BUT only if Product[n] is defined.

IF "%%a"=="UninstallString" if defined Product[%%n] set uninstall[%%n]=%%c&set/a count+=1

take the count-increment before the SETLOCAL out, as it needs to be done only if you have a matching pair of PRODUCT and UNINSTALL; change your initial value to 1 (unless you like counting from zero)

and pray that you don't get a sequence

Displayname y
Uninstall z

Now, if you DO get this sequence, simply find a convenient string to ALSO let through the gate - like, perhaps "HKEY_" and if %%a begins "HKEY_" then set "Product[%%n]=" to clear any Displayname appearing without an uninstallstring.

You may even want to change that around a little.

If you set

  • Product against the first Displayname
  • uninstall against the first uninstall string (ie. only if not defined unistall[%%n])
  • When a HKEY_ appears, increment the counter if both product[%%n] and uninstall[%%n] are defined, else set product[%%n] and uninstall[%%n] to [nothing]

then the sequence of uninstall and displayname is not relevant, although you'd need to repeat the HKEY_ test after the loop to take care of the very last instance...


Addendum for solving some problems discussed

@ECHO OFF
setlocal
set regVar=HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
set excluded=/c:" Microsoft" /c:"MDOP" /c:"Dell" 
set count=1
for /f "tokens=1,2,*" %%a in ('Reg Query %regVar% /S^|findstr "DisplayName HKEY_ UninstallString" ^| findstr /v %excluded%') do (
    setlocal EnableDelayedExpansion
    for %%n in (!count!) do (
        ENDLOCAL
        SET HKEY=Y
        IF "%%a"=="DisplayName" SET "HKEY="&set product[%%n]=%%c
        IF "%%a"=="UninstallString" SET "HKEY="&IF NOT DEFINED uninstall[%%n] set uninstall[%%n]=%%c
        IF "%%a"=="QuietUninstallString" SET "HKEY="&IF NOT DEFINED uninstall[%%n] set uninstall[%%n]=%%c
    IF DEFINED hkey IF DEFINED product[%%n] IF defined uninstall[%%n] SET /a count+=1&SET "hkey="
    IF DEFINED hkey set "product[%%n]="&SET "uninstall[%%n]="
    )   
)

::
:: Last entry may not be complete
::
IF NOT DEFINED product[%count%] SET "uninstall[%count%]="&SET /a count-=1
IF NOT DEFINED uninstall[%count%] SET "product[%count%]="&SET /a count-=1

ECHO %count% entries found
SET prod
SET unins

Note that since HKEY is now allowed through the gate, but it's a pain to test line beginning HKEY, if it's NOT one of the strings "Displayname", "UninstallString" or "QuietUninstallString" then it's presumed to be HKEY. IF DEFINED works on the RUN-TIME presence/absence of a variable which permits easy - er, koff - programming.


Display data from an "array"

FOR /l %%i IN (1,1,%count%) DO (
CALL ECHO %%i. %%product[%%i]%%
)

Additional data 20130419-1756Z

@echo OFF
setlocal
set tab=    
set regVar=HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
set excluded=Microsoft MDOP Dell 
FOR /f "delims=" %%i IN ('Reg Query %regVar% /S^|find "HKEY_"') DO (
 FOR %%s IN (dn us qu pu dn ut) DO SET "%%s="
 FOR /f "tokens=1,2,*delims= " %%s IN ('Reg Query "%%i" /S') DO (
  if /i "%%s"=="Displayname" SET /a dn+=1&SET dn=%%u
  if /i "%%s"=="Uninstallstring" SET /a us+=1&SET ut=%%u
  if /i "%%s"=="quietUninstallstring" SET /a qu+=1
  if /i "%%s"=="publisher" SET pu=%%u
 )
 FOR %%s IN (dn us qu) DO SET /a %%s+=0
 CALL :report
)

GOTO :eof

:report
SET /a tus=us+qu
ECHO %dn%   %us%   %qu%   %tus%   %pu% %dn% %ut%
GOTO :eof

This procedure should show interesting details.

The report columns are

  1. Number of 'displayname's found
  2. Number of 'Uninstallstring's found
  3. number of 'quiteuninstallstring's found
  4. total of previous 2 columns
  5. publisher string - if found
  6. displayname - if found
  7. uninstall text - if found

I'd suggest you use this data to do your filtering and assignment to your array - the inner loop is dealing with a single uninstall item. Select the required parts and add them or don't to your arrays.

Magoo
  • 77,302
  • 8
  • 62
  • 84
  • YES! that's what I was wanting to do, I'll try that as soon as I get back to that network. Question though, those exclusion that are in work on the DisplayName, will that still exclude those? or will it exclude all ocurrances of those words from both uninstallstring and displayname? – Flynn Apr 16 '13 at 12:58
  • So I added UninstallString, now its actually advancing the counter between DisplayName and UninstallString. So it'll say 1.DisplayName 2.UninstallString instead of me being able to call both in that lower for loop at once. SO how it's structured now is that there is a Name, Type, and Data in the reg. It returns %%a = DisplayName Type, and %%b = DisplayName Data. I was hoping that calling both would give me %%c = UninstallString Type, and %%D = UninstallString Data. But instead it advances and alternates between %%a/%%b pairs for DisplayName then another %%a/%%b pair for UninstallString. – Flynn Apr 16 '13 at 13:15
  • And another thing that's happening is that it's now returning ALL uninstall strings regardless of if a DisplayName is being returned. – Flynn Apr 16 '13 at 13:18
  • ok, it's not letting me edit grrr. here is the output: `%a=REG_SZ` `%b=1.Program 1` | `%a=REG_SZ` `%b=2.c:\program1\uninstal` | `%a=REG_SZ` `%b=3.c:\program1\uninstal /silent` | `%a=REG_SZ` `%b=4.Program2` | `%a=REG_EXPAND_SZ` `%b=5.MsiExec.exe /I{program2key}` maybe that will help show where the issue is. I wanted to set it so they were grouped together, not sure if I'm making sense there or not? Sorry, I'm used to Linux and the way windows works with this stuff is absolutely confusing, I'm used to just asking they system for exactly what I want and getting an answer lol – Flynn Apr 16 '13 at 13:33
  • Ok, so I was completely overlooking the fact that I was reading token 2 and 3 ONLY, after I realised that, it all fell into place, I'll post exactly how I fixed it tomorrow. Thanks again Peter Wright for your insight, your solution works perfectly as far as I can tell. – Flynn Apr 16 '13 at 17:42
  • Hmm - well, running this on my machine came up with a few hiccoughs. One problem was that the 'HKLM' line was being excluded because the **excluded** list excluded "Microsoft" - which occurs in the HLKM line... Another was that gating "UninstallString" also allowed through "QuiteUninstallString". Good to see you got it working. I'll post my solution for comparison... – Magoo Apr 16 '13 at 18:48
  • So it turns out that it's not working correctly :( the counter in `if "%%a"=="UninstallString" if defined product[%%n] set uninstall[%%n]==%%c&set/a count+=1` is advancing even when product[] is not defined. I turned echo on, and cannot see when it is happening, but for some reason it advances on a non-DesplayName variable even though that %%n hasn't been applied to a product. it then echos that uninstall string and advances the counter to display the next displayname as the next number. (skips a number because of the uninstall string) – Flynn Apr 18 '13 at 15:28
  • btw, if I toss `if defined product[%%n] echo "YES!"` it will echo YES! no matter what, which tells me that the if defined product[] statement isn't working as we intended – Flynn Apr 18 '13 at 15:29
  • Suggest you edit your original question and add your latest-version-of-code to the end. – Magoo Apr 18 '13 at 15:32
  • sweet, so here is a question, it looks like it's pulling back BOTH 64 and 32 bit programs, how? the reg it's querying is the 64bit, but on my system it's returning 32 bit as well – Flynn Apr 18 '13 at 17:53
  • No idea - I'm not a registry expert. Any way to tell? – Magoo Apr 18 '13 at 17:58
  • The other thing I can't figure out is why is it echoing product[3]= and uninstall[#]= for each product when the only echo is count? EDIT: looks like its the set prod and set unins at the end. If I remove those to make the menu a bit more pretty (IE 1. Product) how can I loop through the product array and write it to the screen with my formatting? – Flynn Apr 18 '13 at 18:01
  • I think something was just cached somewhere, so the only thing I still need to do is print off `1. product` rather than `product[1]=product` – Flynn Apr 18 '13 at 18:25
  • nevermind, i got it lol, i just readded my original &echo %%n.%tab%%%c – Flynn Apr 18 '13 at 18:30
  • ok, everything works perfectly EXCEPT one thing, everytime the menu is printed again, it carries the count to the next number even though count is set to 1 at the beginning. (IE if the last item is 2, then the next time this script run it starts at 3 instead of 1 until I close the window and open a new one) – Flynn Apr 18 '13 at 18:41
  • This is because you aren't executing a SETLOCAL at the start of the routine, so the variables are assigned when you start. you could execute `for /f "delims==" %%i in ('set prod') do set "%%i="` to clear the appropriate variables if you like. Repeat with `uninst`, too. – Magoo Apr 18 '13 at 19:40
  • nice that fixed it, one last question, I still get that empty return, I tried adding a null exclusion but that doesnt work, and I tried adding a /c:"" to the exclusions too, I also tried adding a if %%c defined to the `if "%%a"=="DisplayName" set "HKEY="&set product[%%n]=%%c&echo %%n.%tab%%%c` but it broke, what am I doing wrong to exclude display names that are empty? I also tried moving my echo for the menu to the `if defined hkey if defined product[%%n] if defined uninstall[%%n] set /a count+=1&set "hkey="&echo %%n.%tab%%%c` like that, but it still prints an empty displayname – Flynn Apr 18 '13 at 19:55
  • I'm getting an error "Environment variable prod not defined" the first time I run the script because of that reset line, I added `if defined %prod% for /f "delims==" %%i in ('set prod') do set "%%i="` to try to get rid of that error and only use it if prod is defined, but that causes the line to never be used... – Flynn Apr 19 '13 at 13:23