question

johnweiss-2130 avatar image
0 Votes"
johnweiss-2130 asked Viorel-1 commented

RegEnumValue Skips Some Registry Items. How to fix?

My registry key contains 8 entries:

 HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Devices

 Microsoft Print to PDF     :    winspool,Ne03:
 Fax    :   winspool,Ne06:
 Send To OneNote 16     :    winspool,nul:
 Hyland Software Virtual Printer   :  winspool,Ne04:
 Faxolution  :  winspool,Ne05:
 Microsoft XPS Document Writer  :  winspool,Ne02:
 PrimoPDF  :  winspool,Ne01:
 Webex Document Loader  :  winspool,Ne00:

but RegEnumValue returns only 5 items:

 Microsoft Print to PDF     :    winspool,Ne03:
 Fax    :   winspool,Ne06:
 Send To OneNote 16     :    winspool,nul:
 Hyland Software Virtual Printer   :  winspool,Ne04:
 Faxolution  :  winspool,Ne05:


Any fix? Here's my VBA code:

' HIVES
Public Enum RegHive
HKEY_LOCAL_MACHINE = &H80000002
HKEY_CURRENT_USER = &H80000001
End Enum


' COMMON KEY-PATHS
Public Const REGPATH_ODBC = "SOFTWARE\ODBC\ODBC.INI\ODBC Data Sources"
Public Const REGPATH_PRINTERS = "Software\Microsoft\Windows NT\CurrentVersion\Devices"


' DATA-TYPES
Public Enum RegType
REG_NONE = &H0
REG_SZ = &H1 'Nul terminated string -
REG_EXPAND_SZ = &H2 'Nul terminated string (with environment variable references) -
REG_BINARY = &H3 'Free form binary -
REG_DWORD = &H4 '32-bit number -
REG_DWORD_BIG_ENDIAN = &H5 '32-bit number. In big-endian format, the most significant byte of a word is the low-order byte -
REG_LINK = &H6 'Symbolic Link (unicode) -
REG_MULTI_SZ = &H7 'Multiple strings -
REG_RESOURCE_LIST = &H8 'Resource list in the resource map -
End Enum
' https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/25cce700-7fcf-4bb6-a2f3-0f6d08430a55


' CONSTANTS
Public Const REG_KEY_QUERY_VALUE = &H1 ' 1
Public Const REG_KEY_WOW64_64KEY = &H100 ' 256
Public Const REG_ERROR_NO_MORE_ITEMS = &H103 ' 259
Public Const REG_ERROR_MORE_DATA = &HEA ' 234
Public Const REG_KEY_ENUMERATE_SUB_KEYS = &H8
Public Const REG_KEY_READ = &H20019
Public Const REG_KEY_ALL_ACCESS = &HF003F


' DECLARATIONS

' open key
Public Declare PtrSafe Function RegOpenKeyEx Lib "advapi32.dll" Alias
"RegOpenKeyExA" (ByVal hKey As LongPtr, ByVal lpSubKey As String,

ByVal ulOptions As Long, ByVal samDesired As Long, phkResult As LongPtr) As Long

' info about all values in key
Declare PtrSafe Function RegQueryInfoKey Lib "advapi32.dll" Alias "RegQueryInfoKeyA"
(ByVal hKey As LongPtr, ByVal lpClass As String, lpcbClass As Long, ByVal lpReserved As LongPtr,

lpcSubKeys As Long, lpcbMaxSubKeyLen As Long, lpcbMaxClassLen As Long, lpcValues As Long,
lpcbMaxValueNameLen As Long, lpcbMaxValueLen As Long, lpcbSecurityDescriptor As Long,

lpftLastWriteTime As FILETIME) As Long

' get one value.
Declare PtrSafe Function RegQueryValueEx Lib "advapi32.dll" Alias "RegQueryValueExA"
(ByVal hKey As LongPtr, ByVal lpValueName As String, ByVal lpReserved As LongPtr, lpType As Long,

lpData As Any, lpcbData As Long) As Long ' Note that if you declare the lpData parameter as String, you must pass it By Value.

' enumerate values
Public Declare PtrSafe Function RegEnumValue Lib "advapi32.dll" Alias
"RegEnumValueA" (ByVal hKey As LongPtr, ByVal dwIndex As Long,

ByVal lpValueName As String, lpcbValueName As Long, ByVal lpReserved As LongPtr, _
lpType As Long, lpData As Byte, lpcbData As Long) As Long

' WRITE STRING DATA
Public Declare PtrSafe Function RegSetKeyValueString Lib "advapi32.dll" Alias "RegSetKeyValueA"
(ByVal hKey As LongPtr, ByVal lpSubKey As String, ByVal lpValueName As String,

ByVal dwType As Long, ByVal lpData As String, ByVal cbData As Long) As Long

' WRITE NUMERIC DATA
Public Declare PtrSafe Function RegSetKeyValueLong Lib "advapi32.dll" Alias "RegSetKeyValueA"
(ByVal hKey As Long, ByVal lpSubKey As String, ByVal lpValueName As String,

ByVal dwType As Long, lpData As Long, ByVal cbData As Long) As Long

' Delete Value
Declare PtrSafe Function RegDeleteValue Lib "advapi32.dll" Alias "RegDeleteValueA" _
(ByVal hKey As LongPtr, ByVal lpValueName As String) As Long

' close key
Public Declare PtrSafe Function Reg_CloseKey Lib "advapi32.dll" Alias "RegCloseKey" _
(ByVal hKey As LongPtr) As Long


 Sub  Reg_Enumerate_Values()
           ' open the key whose values to enumerate
           Dim hKey As LongPtr  ' registry key handle
           RegOpenKeyEx HKEY_CURRENT_USER, "Software\Microsoft\Windows NT\CurrentVersion\Devices", 0&, REG_KEY_ALL_ACCESS, hKey
              
           ' loop until all values have been enumerated
           Dim lValueIndex As Long          ' index for RegEnumValue
           Dim sValueName As String  ' name of each value in the key
           Dim lNameLen As Long    ' length of sValueName
           Dim lType As RegType        ' registry value data type
           Dim aData() As Byte    ' byte array of registry value value
           Dim lDataSize As Long
                        
           Do
                     ' prep vars to rec'v data
                     sValueName = String$(300, vbNullChar)
                     lNameLen = 299
                     ReDim aData(0 To 999)
                     sData = ""
                        
                     ' get the next item
                     lResult = RegEnumValue(hKey, lValueIndex, sValueName, lNameLen, 0&, lType, aData(0), lDataSize)
    
                     ' inspect variables here
                     Stop
                        
                     ' tell RegEnumValue to get the next registry value
                     lValueIndex = lValueIndex + 1
    
           Loop Until lResult = REG_ERROR_NO_MORE_ITEMS
              
           Reg_CloseKey hKey
    
 End Function



windows-api
· 8
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.


To reproduce the problem, show the definition of external functions and constants.


1 Vote 1 ·

Declarations added to the question, above.
thx

0 Votes 0 ·

More info:
Unexpectedly, RegQueryValueEx is able to query the missing items by name.

This isn't a solution, however, because my code has no way of knowing what items are in the registry -- that why i need to enumerate.

This simply demonstrates an inconsistency between RegEnumValue and RegQueryValueEx.

 Function Reg_GetValue()
 ' Not working yet
           Dim hKey As LongPtr  ' registry key handle
           RegOpenKeyEx HKEY_CURRENT_USER, "Software\Microsoft\Windows NT\CurrentVersion\Devices", 0&, REG_KEY_ALL_ACCESS, hKey
              
           Dim lResult As LongPtr, lType, lSize As Long
           Dim aData(0 To 255) As Byte
           lSize = 255
           lResult = RegQueryValueEx(hKey, "Webex Document Loader", 0, lType, aData(0), lSize)
              
           lResult = Reg_CloseKey(hKey)
              
           ' Inspect return variables here
           Stop
 End Function


0 Votes 0 ·

Is there a new Win 10 way to query the registry?

0 Votes 0 ·

It might be due to missing null-terminators on some of the items, or they exceed max length. But, if an item is visible in regedit, i should be able to enumerate it.

0 Votes 0 ·

According to MSDN:

If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type, the string may not have been stored with the proper null-terminating characters. Therefore, even if the function returns ERROR_SUCCESS, the application should ensure that the string is properly terminated before using it; otherwise, it may overwrite a buffer. (Note that REG_MULTI_SZ strings should have two null-terminating characters.)
To determine the maximum size of the name and data buffers, use the RegQueryInfoKey function.

So have you used RegQueryInfoKey to check whether the lpcValues returned by the registry is correct, and to determine the name and data buffer size.


1 Vote 1 ·

it seems another possible issue might be using the 8-bit functions for 16-bit value-names

RegQueryValueExW : 16-bit (wide).

LPCWSTR lpValueName,

RegQueryValueExA : 8-bit (Ansi?)

LPCSTR lpValueName,

0 Votes 0 ·

Wow. simply changing

 RegEnumValueA

to

 RegEnumValueW

returns all the items! This suggests the issue is to do with bit-size. But some are missing the data, and none have the full name. I'm guessing this may have to do with how i parse the result. Hopefully, i can use one parse-method to get all values, instead of two different methods.

 P     :    
 M     :    
 M     :    winspool,Ne03:
 F     :    winspool,Ne06:
 S     :    winspool,nul:
 H     :    winspool,Ne04:
 F     :    winspool,Ne05:
 W     :    


0 Votes 0 ·

1 Answer

Viorel-1 avatar image
0 Votes"
Viorel-1 answered Viorel-1 commented

It seems that you did not initialise the lDataSize variable. Try executing the next lines before each RegEnumValue:

 ReDim aData(0 To 999)
 lDataSize = 1000


· 2
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Woops. You're right. That's the problem.

Question: Is there any advantage to using the Wide command on Windows 10?
RegQueryValueExW

Thx!

0 Votes 0 ·

To support non-Latin letters, the xxxW functions should be preferred if you know how to call them from VBA.

1 Vote 1 ·