Kevin Denham
Kevin Denham

Reputation: 519

Why does nonexistent Registry value come back as "1" instead of null?

I hate to admit I wasted a large amount of time trying to tweak a VB script that returns a list of installed programs and I'm trying to understand where I went wrong.

The problem I was running into is that every "List Programs" type script I found online populates a text file with every key that has a "DisplayName" field. Despite a ton of looking I couldn't find one that returns JUST the listings that populate add/remove or appwiz.cpl. This is an example of the "Grab Everything" type scripts I found:

Const HKLM = &H80000002 'HKEY_LOCAL_MACHINE
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set oShell = CreateObject( "WScript.Shell" )
temp=oShell.ExpandEnvironmentStrings("%temp%")
Set objTextFile = objFSO.CreateTextFile(temp & "\software.txt", True)
strComputer = "."
strKey = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\"
strKey2 = "SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\"
strEntry1a = "DisplayName"
strEntry1b = "QuietDisplayName"
Set objReg = GetObject("winmgmts://" & strComputer & _
 "/root/default:StdRegProv")
objReg.EnumKey HKLM, strKey, arrSubkeys
For Each strSubkey In arrSubkeys
  intRet1 = objReg.GetStringValue(HKLM, strKey & strSubkey, _
   strEntry1a, strValue1)
  If intRet1 <> 0 Then
    objReg.GetStringValue HKLM, strKey & strSubkey, _
     strEntry1b, strValue1
  End If
  If strValue1 <> "" Then
  objTextFile.WriteLine strValue1
  End If
Next
objtextfile.close
Set objTextFile = objFSO.CreateTextFile(temp & "\software.txt", True)
objReg.EnumKey HKLM, strKey2, arrSubkeys
For Each strSubkey In arrSubkeys
  intRet1 = objReg.GetStringValue(HKLM, strKey2 & strSubkey, _
   strEntry1a, strValue1)
  If intRet1 <> 0 Then
    objReg.GetStringValue HKLM, strKey & strSubkey, _
     strEntry1b, strValue1
  End If
  If strValue1 <> "" Then
  objTextFile.WriteLine strValue1
  End If
Next
objtextfile.close

Since I couldn't find it I decided to make it myself. Finding the fields which Windows uses to exclude entries was pretty easy. All the keys listed in the Uninstall keys in the registry are excluded if they contain the fields "ParentDisplayName" or "SystemComponent". All I had to do was to introduce a way to screen the keys containing the above fields so they are not added to the ObjTextFile.WriteLine.

My question is would values that should have been null be returned as "1"? I'd really like to know what went wrong so I avoid wasting so much time in the future.

The script I finally got working is here:

(Notice the submissions to the text file must have both a "SystemComponent" and "ParentDisplayName" of "1" in order to not be excluded. But I have no idea why it is "1" and not Null.

Const HKLM = &H80000002 'HKEY_LOCAL_MACHINE
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set oShell = CreateObject( "WScript.Shell" )       
System=oShell.ExpandEnvironmentStrings("%systemroot%")    
Set objTextFile = objFSO.CreateTextFile("installed.txt", True)
strComputer = "."
strKey = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\"
strKey2 = "SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\"
strEntry1a = "DisplayName"
strEntry1b = "QuietDisplayName"
strEntry1c = "SystemComponent"
strEntry1d = "ParentDisplayName"
Set objReg = GetObject("winmgmts://" & strComputer & _
 "/root/default:StdRegProv")
objReg.EnumKey HKLM, strKey, arrSubkeys
For Each strSubkey In arrSubkeys
  Check1 = objReg.GetDWORDValue(HKLM, strKey & strSubkey, _
   strEntry1c)
  Check2 = objReg.GetStringValue(HKLM, strKey & strSubkey, _
   strEntry1d)
  intRet1 = objReg.GetStringValue(HKLM, strKey & strSubkey, _
   strEntry1a, strValue1)
  If intRet1 <> 0 Then
    objReg.GetStringValue HKLM, strKey & strSubkey, _
     strEntry1b, strValue1
  End If
  If strValue1 <> "" and check1 = 1 and check2 = 1 Then
  objTextFile.WriteLine strValue1
  End If
Next

If objfso.folderexists (system & "\syswow64\") then
objReg.EnumKey HKLM, strKey2, arrSubkeys
For Each strSubkey In arrSubkeys
  Check1 = objReg.GetDWORDValue(HKLM, strKey2 & strSubkey, _
   strEntry1c)
  Check2 = objReg.GetStringValue(HKLM, strKey2 & strSubkey, _
   strEntry1d)
  intRet1 = objReg.GetStringValue(HKLM, strKey2 & strSubkey, _
   strEntry1a, strValue1)
  If intRet1 <> 0 Then
    objReg.GetStringValue HKLM, strKey & strSubkey, _
     strEntry1b, strValue1
  End If
  If strValue1 <> "" and check1 = 1 and check2 = 1 then
  objTextFile.WriteLine strValue1
  End If
Next
objtextfile.close
else
end if

This is where I got caught up; trying endless variations of:

  strEntry1c = "SystemComponent"
    Check = objReg.GetDWORDValue(HKLM, strKey & strSubkey, _
       strEntry1c)
    IF isNull(Check) Then
      intRet1 = objReg.GetStringValue(HKLM, strKey & strSubkey, _
       strEntry1a, strValue1)

Upvotes: 0

Views: 1639

Answers (2)

Ekkehard.Horner
Ekkehard.Horner

Reputation: 38745

Because

  1. @Ansgar gave such a good explanation (+1)
  2. user1691832 still experiments with Null checks
  3. The Docs tend to avoid speaking about return values and possible errors

some code which shows that you should check the numerical return value and how to do it:

Option Explicit

Const HKLM = &H80000002 ' HKEY_LOCAL_MACHINE
Const BAD  = &H80000010 ' BAD HIVE

Dim oReg   : Set oReg = GetObject("winmgmts://./root/default:StdRegProv")
Dim aParms : aParms   = Array( _
      Array(HKLM, "SYSTEM\CurrentControlSet\Control\CrashControl", "AutoReboot") _
    , Array(HKLM, "SYSTEM\CurrentControlSet\Control\CrashControl", "YYYYYYYYYY") _
    , Array(HKLM, "SYSTEM\CurrentControlSet\Control\XXXXXXXXXXXX", "AutoReboot") _
    , Array(HKLM, "SYSTEM\CurrentControlSet\Control\XXXXXXXXXXXX", "YYYYYYYYYY") _
    , Array(HKLM, "SYSTEM\CurrentControlSet\Control\CrashControl", "AutoReboot") _
    , Array(BAD , "SYSTEM\CurrentControlSet\Control\CrashControl", "AutoReboot") _
)
Dim lValue ' 'dwValue' is type prefix fraud
Dim aParm
For Each aParm In aParms
    WScript.Echo "try to get long Value from:", Join(aParm, "\")
    Dim iRet : iRet = oReg.GetDWORDValue(aParm(0), aParm(1), aParm(2), lValue)
    If 0 = iRet Then
       WScript.Echo "       got:", iRet, TypeName(lValue), lValue
    Else
       WScript.Echo "       got:", iRet, "won't try to display", TypeName(lValue)
    End If
Next

output:

cscript 14794473.vbs
try to get long Value from: -2147483646\SYSTEM\CurrentControlSet\Control\CrashControl\AutoReboot
       got: 0 Long 1
try to get long Value from: -2147483646\SYSTEM\CurrentControlSet\Control\CrashControl\YYYYYYYYYY
       got: 1 won't try to display Null
try to get long Value from: -2147483646\SYSTEM\CurrentControlSet\Control\XXXXXXXXXXXX\AutoReboot
       got: 2 won't try to display Null
try to get long Value from: -2147483646\SYSTEM\CurrentControlSet\Control\XXXXXXXXXXXX\YYYYYYYYYY
       got: 2 won't try to display Null
try to get long Value from: -2147483646\SYSTEM\CurrentControlSet\Control\CrashControl\AutoReboot
       got: 0 Long 1
try to get long Value from: -2147483632\SYSTEM\CurrentControlSet\Control\CrashControl\AutoReboot
       got: 6 won't try to display Null

Upvotes: 0

Ansgar Wiechers
Ansgar Wiechers

Reputation: 200233

It's not quite clear to me what your question is here. Which value do you expect to be Null (or Not Null)? The value read from the registry or the return value of the function you use to read the registry value? Those two values have entirely different meanings.

Besides, the function call in your last code snippet isn't quite right (although it will work).

strEntry1c = "SystemComponent"
Check = objReg.GetDWORDValue(HKLM, strKey & strSubkey, strEntry1c)

You're calling the function GetDWORDValue with the parameters hDefKey, sSubkeyName and sValueName, but omit the out parameter for the actual value (uValue).The return value of the function, however, is not the data of the registry value SystemComponent, but an integer status code indicating if the operation was successful or not. The data read from the registry value would normally be stored in the function's 4th parameter. Since this parameter is missing, it defaults to Null, i.e. the runtime environment expands the function call to this:

Check = objReg.GetDWORDValue(HKLM, strKey & strSubkey, strEntry1c, Null)

Depending on whether strKey & strSubkey and/or strEntry1c do or don't exist, the value of Check will be either 0 (value successfully read), 1 (value doesn't exist), or 2 (key doesn't exist). Whatever is read from the registry will be dropped, though, because there's no variable to hold the data (4th parameter is Null). Because of this the condition IsNull(Check) always evaluates to False and GetStringValue is never called.

If you add an actual variable as the 4th parameter to the function call:

Check = objReg.GetDWORDValue(HKLM, strKey & strSubkey, strEntry1c, val)

the value of that variable will be either the value read from the registry (Check = 0) or Null if the value can't be read (Check <> 0).

You also have to change the condition from IsNull(Check) to either Check <> 0 or IsNull(val) if you want to call GetStringValue when the DWORD value was not present.

Upvotes: 2

Related Questions