vroedie
(Fresh Scripter)
2006-10-10 02:46 PM
enumkey in array

Hi,

I'm trying to build a script that will read all values from a registry key and put them in an array. So that I can reuse this array and enumerate all registry keys from below those registry keys.

I have created the following so far, but I can't seem to get it into an array:

Code:


Dim $Indx, $Keyname, $KeyArr
$Indx = 0

While @ERROR = 0
$Keyname = ENUMKEY("HKCU\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles\", $Indx)
If $Keyname <> ""
? "Name found: $Keyname"
$KeyArr[$Indx] = $Keyname
$Indx = $Indx + 1
Endif
Loop




I took most of this code from the manual, but it was used in an "If, Endif, goto" construction and I'd rather not use goto's in my script.

regards,

jeroen


Witto
(MM club member)
2006-10-10 04:01 PM
Re: enumkey in array

Code:

If NOT @LOGONMODE
Break On
Else
Break Off
EndIf
Dim $RC
$RC = SetOption("Explicit","On")
$RC = SetOption("NoVarsInStrings","On")
$RC = SetOption("NoMacrosInStrings","On")
$RC = SetOption("WrapAtEOL","On")

Dim $Indx, $Keyname, $KeyArr[]
$Indx = 0

While @ERROR = 0
$Keyname = EnumKey( "HKCU\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles\", $Indx)
If $Keyname <> ""
? "Name found: "+$Keyname
ReDim Preserve $KeyArr[$Indx]
$KeyArr[$Indx] = $Keyname
$Indx = $Indx + 1
EndIf
Loop
? "Number of keys found: "+(Ubound($KeyArr)+1)
For Each $Keyname In $KeyArr
? "- "+$Keyname
Next



Richard H.Administrator
(KiX Supporter)
2006-10-10 04:46 PM
Re: enumkey in array

The loop will be better constructed like this, to avoid false positives on the @ERROR test:
Code:
Dim $sKey
$sKey="HKCU\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles\"

$Keyname = EnumKey( $sKey, $Indx)
While @ERROR = 0
If $Keyname <> ""
? "Name found: "+$Keyname
ReDim Preserve $KeyArr[$Indx]
$KeyArr[$Indx] = $Keyname
$Indx = $Indx + 1
EndIf
$Keyname = EnumKey( $sKey, $Indx)
Loop



Witto
(MM club member)
2006-10-10 06:44 PM
Re: enumkey in array

Richard,
I do not think there will be false positives, because when there is no key anymore, $Keyname will be "" and an error will be raised. The last loop will skip the If/EndIf because of the statement:
Code:

If $Keyname <> ""


Don't you think so?


NTDOCAdministrator
(KiX Master)
2006-10-10 07:05 PM
Re: enumkey in array

There are some pre-scripted UDFs for this.

ARRAYENUMKEY() - Creates an array of names of the subkeys contained in a registry key
http://www.kixtart.org/ubbthreads/showflat.php?Cat=0&Number=81940

ArrayEnumValue() - Creates an array of names of the registry entries contained in a registry key or subkey
http://www.kixtart.org/ubbthreads/showflat.php?Cat=0&Number=81943


vroedie
(Fresh Scripter)
2006-10-11 09:40 AM
Re: enumkey in array

Hi,

Thanks! Because I'm still learning I would like to know what you did and how you did it. I've got some questions about it, but this is how I interpret your code:

Code:
  

If NOT @LOGONMODE
Break On
Else
Break Off
EndIf

;I do not know the Break option, so I don't know why this is done

Dim $RC
$RC = SetOption("Explicit","On")
$RC = SetOption("NoVarsInStrings","On")
$RC = SetOption("NoMacrosInStrings","On")
$RC = SetOption("WrapAtEOL","On")

Dim $Indx, $Keyname, $KeyArr[] ;the brackets are for the declaration of an array , that's what I missed.

$Indx = 0

While @ERROR = 0
$Keyname = EnumKey( "HKCU\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles\", $Indx)

If $Keyname <> ""
? "Name found: "+$Keyname
ReDim Preserve $KeyArr[$Indx] ; Here is stated that the values in the array need to be saved
$KeyArr[$Indx] = $Keyname
$Indx = $Indx + 1
EndIf
Loop
? "Number of keys found: "+(Ubound($KeyArr)+1) ;what is the Ubound for? the plus 1 is because the array starts with 0.
For Each $Keyname In $KeyArr ? "- "+$Keyname ; this I can use to rerun the code for any subkeys.
Next



regards,

jeroen


vroedie
(Fresh Scripter)
2006-10-11 10:16 AM
Re: enumkey in array

And now, if I wanted to create a function out of this (or use an UDF) and I want to reuse the array so I can do the same to the subkeys of the array I just created.

This is what I've got so far (I added the prints so I could see what values the variables got, but It wasn't much):

Code:
 
$MainKey = "HKCU\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles\"
? $MainKey

$Step1[] = ReadRegArr($MainKey)
? $Step1
For Each $Key in $Step1
$Ind2 = 0
Redim Preserve $Step2[]
$Step2[$Ind2] = ReadRegArr($MainKey + "\" + $Key)
? $Step2
? $Key
$Ind2 = $Ind2 + 1
? $Ind2
Next


Function ReadRegArr($RegKey)

Dim $Indx, $CurrentKeyname, $KeyArr[], $Regkey
$Indx = 0

While @ERROR = 0
$Keyname = ENUMKEY($RegKey, $Indx)
If $Keyname <> ""
? "Name found: $Keyname"
Redim Preserve $KeyArr[$Indx]
$KeyArr[$Indx] = $Keyname
$Indx = $Indx + 1
Endif
Loop
? "Number of keys found: "+(Ubound($KeyArr)+1)
For Each $Keyname In $KeyArr
? "- "+$Keyname
Next

EndFunction



regards,

jeroen


Richard H.Administrator
(KiX Supporter)
2006-10-11 10:34 AM
Re: enumkey in array

Quote:

Richard,
I do not think there will be false positives, because when there is no key anymore, $Keyname will be "" and an error will be raised. The last loop will skip the If/EndIf because of the statement:
Code:
--------------------------------------------------------------------------------

If $Keyname <> ""

--------------------------------------------------------------------------------

Don't you think so?




Yes...and no

While that would work in this specific case there are two problems. The first is that the loop executes one more time that necessary. More importantly is that if the coder adds extra commands then there is a danger of causing a false positive on the error check.

Here is a very simple example. All that I've done is read an entry "FOO" from each of the subkeys. Assume that the entry will exist on some keys but not on others.

Here is the code:
Code:
Break ON
$Key = "HKCU\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles\"

"Method 1 (fails)"+@CRLF
"----------------"+@CRLF
$Indx=0
While @ERROR = 0
$Keyname = EnumKey($Key, $Indx)
If $Keyname <> ""
"Name found: "+$Keyname+@CRLF
ReDim Preserve $KeyArr[$Indx] ; Here is stated that the values in the array need to be saved
$KeyArr[$Indx] = $Keyname
"Value of 'FOO' in key is: "+ReadValue($Key+"\"+$Keyname,"FOO")+@CRLF
$Indx=$Indx+1
EndIf
Loop

@CRLF
"Method 2 (works)"+@CRLF
"----------------"+@CRLF
$Indx=0
$Keyname = EnumKey($Key, $Indx)
While Not @ERROR
"Name found: "+$Keyname+@CRLF
ReDim Preserve $KeyArr[$Indx] ; Here is stated that the values in the array need to be saved
$KeyArr[$Indx] = $Keyname
"Value of 'FOO' in key is: "+ReadValue($Key+"\"+$Keyname,"FOO")+@CRLF
$Indx=$Indx+1
$Keyname = EnumKey($Key, $Indx)
Loop



And here is the output:
Code:
Method 1 (fails)
----------------
Name found: BACKUP OF Outlook
Value of 'FOO' in key is:

Method 2 (works)
----------------
Name found: BACKUP OF Outlook
Value of 'FOO' in key is:
Name found: Outlook
Value of 'FOO' in key is:
Name found: RH
Value of 'FOO' in key is:



As you can see, the error caused by the missing entry actually causes the loop to exit early in method 1. Method 2 does not suffer from this problem, and in addition does not iterate the loop for the error pass.

For this reason method 2 is recommended, especially for starters. Once you have the habit of coding loops this way you will avoid some of the confusing and hard to find errors that the alternative methods allow.


vroedie
(Fresh Scripter)
2006-10-11 01:56 PM
Re: enumkey in array

Tnx,

but how can I use the values of an array in an new array? (See my previous example)

regards,

jeroen


Richard H.Administrator
(KiX Supporter)
2006-10-11 02:43 PM
Re: enumkey in array

Like this:
Code:
Break ON
$=SetOption("Explicit","ON")

Dim $sRootKey
$sRootKey="HKCU\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles\"

Dim $sMain,$sSub,$asMain,$asSub

$asMain=ReadRegArr($sRootKey)
For Each $sMain in $asMain
"Main key: "+$sMain+@CRLF
$asSub=ReadRegArr($sRootKey+"\"+$sMain)
For Each $sSub in $asSub
" Sub key: "+$sSub+@CRLF
Next
Next
Exit 0

Function ReadRegArr($sKey)
Dim $i,$sSubKey

Redim $ReadRegArr[0]

$i=0
$sSubKey=EnumKey($sKey,$i)
While Not @ERROR
ReDim Preserve $ReadRegArr[$i]
$ReadRegArr[$i]=$sSubKey
$i=$i+1
$sSubKey=EnumKey($sKey,$i)
Loop
If @ERROR=259 Exit 0 Else Exit @ERROR EndIf
EndFunction



You could also create a recursive function to load the entire tree, but that is a little more complex and may not fit your needs.


Witto
(MM club member)
2006-10-11 04:36 PM
Re: enumkey in array

"Break" is documented in the kix2010.doc. If a KiX script is broken (p.e. via task manager and use of the "End Task" button on the "Applications" tab), the user will be logged of. The Break is Off.
This behaviour of KiXtart is good during logon scripts, but unwanted in (most of the) other cases.
Normally, we want the Break Off during logon scripts and we want the Break On in any other case. So we could write:
Code:

If NOT @LOGONMODE
Break On
EndIf


But maybe, one day, the KiX community and Ruud will decide that Break should be On per default. To be prepared for that switch, I write
Code:

If NOT @LOGONMODE
Break On
Else
Break Off
EndIf



Witto
(MM club member)
2006-10-11 04:50 PM
Re: enumkey in array

UBound is also documented in kix2010.doc. It counts the number of elements in an array, starting from 0.
The next code will return 2 for the UBound Function.
Code:

If NOT @LOGONMODE
Break On
Else
Break Off
EndIf
Dim $RC
$RC = SetOption("Explicit","On")
$RC = SetOption("NoVarsInStrings","On")
$RC = SetOption("NoMacrosInStrings","On")
$RC = SetOption("WrapAtEOL","On")

Dim $arrElements
$arrElements = "One","Two","Three"
? Ubound($arrElements)
For $rc = 0 to Ubound($arrElements)
? "- "+$RC+": "+$arrElements[$RC]
Next



vroedie
(Fresh Scripter)
2006-10-11 04:58 PM
Re: enumkey in array

Hi,

Great this was exectly what I was looking for, thanks!

I've adjusted the code and function so it can read multiple registry entries, their sub entries, their value's and their value data.

Code:

Break ON
$=SetOption("Explicit","ON")

If RedirectOutput("test.txt") = 0
EndIf

Dim $sRootKey
$sRootKey="HKCU\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles\"

Dim $sMain,$sSub,$asMain,$asSub,$sSub2,$asSub2,$Val

$asMain=ReadRegArr($sRootKey)
For Each $sMain in $asMain
;"Main key: "+$sMain+@CRLF
$asSub=ReadRegArr($sRootKey+"\"+$sMain)
For Each $sSub in $asSub
;" Sub key: "+$sSub+@CRLF
$asSub2=ReadRegValArr($sRootKey+"\"+$sMain+"\"+$sSub)
For Each $sSub2 in $asSub2
;" sub - Sub key: "+$sSub2+@CRLF
$Val = ReadValue($sRootKey+"\"+$sMain+"\"+$sSub, $sSub2)
If INSTR($Val,".pst")
? "Value data: " + $Val+@CRLF
EndIf
Next
Next
Next
Exit 0

Function ReadRegArr($sKey)
Dim $i,$sSubKey

Redim $ReadRegArr[0]

$i=0
$sSubKey=EnumKey($sKey,$i)
While Not @ERROR
ReDim Preserve $ReadRegArr[$i]
$ReadRegArr[$i]=$sSubKey
$i=$i+1
$sSubKey=EnumKey($sKey,$i)
Loop
If @ERROR=259 Exit 0 Else Exit @ERROR EndIf
EndFunction

Function ReadRegValArr($sVKey)
Dim $iV,$sVSubKey

Redim $ReadRegValArr[0]

$iV=0
$sVSubKey=EnumValue($sVKey,$iV)
While Not @ERROR
ReDim Preserve $ReadRegValArr[$iV]
$ReadRegValArr[$iV]=$sVSubKey
$iV=$iV+1
$sVSubKey=EnumValue($sVKey,$iV)
Loop
If @ERROR=259 Exit 0 Else Exit @ERROR EndIf
EndFunction

Function ReadRegValArr2($sV2Key,$sV2Value)
Dim $iV2,$sV2SubKey

Redim $ReadRegValArr2[0]

$iV2=0
$sV2SubKey=ReadValue($sV2Key,$iV2)
While Not @ERROR
ReDim Preserve $ReadRegValArr2[$iV2]
$ReadRegValArr2[$iV2]=$sV2SubKey
$iV2=$iV2+1
$sV2SubKey=ReadValue($sV2Key,$iV2)
Loop
If @ERROR=259 Exit 0 Else Exit @ERROR EndIf
EndFunction



So if someone can use this, it works!

regards,

jeroen