Classic VB Corner

Mining the Registry for Structures

Need to extract binary data from the registry? Here's a quick primer on reading and interpreting structures stored there.

Classic VB has always offered a couple of really crude wrappers for reading and writing to the Windows registry. They were hobbled from the beginning by being restricted to a single subkey under the CurrentUser (HKCU) hive. So most folks have found or written wrappers to read and write string and dword keys. But many of the published ones don't offer nice, easy, binary operations, so I thought I'd share the method I use.

I was playing around with reproducing a little utility that determines -- estimates, really -- how long Windows has been running. One of the methods to do this is to read the "ShutdownTime" value stored at HKLM\System\CurrentControlSet\Control\Windows. Well, right away, that rules out using native VB methods, as it's in the LocalMachine hive. Worse still, from this perspective, it's stored as a FILETIME structure in binary format. Definitely no native support for that.

The FILETIME structure is simply a convenient way to pack a 64-bit value into manageable 32-bit chunks, and represents the number of nanoseconds since Jan. 1, 1601 (UTC, of course). You can represent FILETIME in ClassicVB as such:

Public Type FILETIME
   dwLowDateTime As Long
   dwHighDateTime As Long
End Type
So the problem comes down to being able to grab some arbitrary number of bytes from a binary registry entry, and then stuff them into a predefined structure. The answer was to write a generic registry reading routine that would return a dynamic byte array. First the code, then the clues:
Public Function RegGetBinaryValue(ByVal RootKey As Long, _
   ByVal Key As String, ByVal ValueName As String, _
   TheData() As Byte) As Boolean
   
   Dim nRet As Long
   Dim hKey As Long
   Dim nType As Long
   Dim nBytes As Long
   Dim DWord As Long

   ' Open key
   nRet = apiRegOpenKeyEx(RootKey, Key, 0&, KEY_READ, hKey)
   If nRet = ERROR_SUCCESS Then
      ' If NULL, the default value will be read.
      If ValueName = "*" Then ValueName = vbNullString
      
      ' Determine how large the buffer needs to be
      nRet = apiRegQueryValueEx(hKey, ValueName, 0&, nType, _
                                ByVal 0&, nBytes)
      If nRet = ERROR_SUCCESS Then
         If (nType = REG_BINARY) Then
            ' Resize buffer and request data at this key, ...
            ReDim TheData(0 To nBytes - 1) As Byte
            nRet = apiRegQueryValueEx(hKey, ValueName, 0&, _
                                      nType, TheData(0), nBytes)
            If nRet = ERROR_SUCCESS Then
               ' ... and return success.
               RegGetBinaryValue = True
            End If
         End If
      End If
      Call apiRegCloseKey(hKey)
   End If
End Function
The registry functions are very well-named. So much so, in fact, that I ended up recycling some of them as function names in my standard registry wrappers, so I needed to use aliases in all my declares. You can see the standard I settled on was preceding the API function names with the "api" prefix, while my own function names took the more conversational names.

The RegGetBinaryValue function accepts root key (typically HKCU or HKLM), key and value name parameters to point to what data is to be retrieved. Special handling is given when the value name of "*" is used -- that returns the default value for the key. The function also accepts a pointer to a dynamic array, which is resized to fit and filled with the available data from the requested location.

Calling RegGetBinaryValue can be done in this manner:

Public Function LastShutdown() As Date
   Dim b() As Byte
   Dim ft As FILETIME
   Const HKEY_LOCAL_MACHINE = &H80000002
   Const Key As String = _
      "System\CurrentControlSet\Control\Windows"
   Const Value As String = "ShutdownTime"
   
   If RegGetBinaryValue(HKEY_LOCAL_MACHINE, Key, Value, b) Then
      If UBound(b) = 7 Then
         Call CopyMemory(ft, b(0), 8&)
         LastShutdown = FileTimeToDouble(ft, True)
      End If
   End If
End Function

In this case, I'm expecting a specific structure to be stored in the binary value, one composed of exactly eight bytes. So I pass an empty dynamic array to RegGetBinaryValue, and then test the size of the array upon successful return. If I was handed eight bytes, a quick call to CopyMemory slings them over into a FILETIME structure. That's all there is to it.

What's that FileTimeToDouble call in there, you say? That's a little routine I wrote to convert API date/time values into more VB-friendly values. It exercises a couple more APIs to return a value our code can understand intrinsically:

Private Function FileTimeToDouble(ftUTC As FILETIME, _
   Optional ByVal Localize As Boolean = False) As Double
   Dim ft As FILETIME
   Dim st As SYSTEMTIME
   Dim d As Double
   Dim t As Double
   
   ' Convert to local filetime, if necessary.
   If Localize Then
      Call FileTimeToLocalFileTime(ftUTC, ft)
   Else
      ft = ftUTC
   End If
   
   ' Convert to system time structure.
   Call FileTimeToSystemTime(ft, st)
   
   ' Convert to VB-style date (double).
   FileTimeToDouble = DateSerial(st.wYear, st.wMonth, st.wDay) + _
                      TimeSerial(st.wHour, st.wMinute, st.wSecond)
End Function

Windows typically stores all time values in UTC, and then converts them to the local time zone upon demand. So the first task when converting FILETIME structures is often to pass them to FileTimeToLocalFileTime which does just as its name suggests, including accounting for daylight-saving time. The next step is to break the encoded low and high values of the FILETIME structure into a much more readable SYSTEMTIME structure using the FileTimeToSystemTime API.

SYSTEMTIME structures neatly map into exactly the values we need to provide DateSerial and TimeSerial, such that we end up with the native data format used by ClassicVB for dates and times. Yes, a Date variable is simply a Double in drag.

The Uptime sample on my site contains this code, and much more which I'll be touching on in forthcoming columns. Next, we'll look at why this registry value may not be the best indicator of system uptime, and what's a better way to estimate that elusive value.

About the Author

Karl E. Peterson wrote Q&A, Programming Techniques, and various other columns for VBPJ and VSM from 1995 onward, until Classic VB columns were dropped entirely in favor of other languages. Similarly, Karl was a Microsoft BASIC MVP from 1994 through 2005, until such community contributions were no longer deemed valuable. He is the author of VisualStudioMagazine.com's new Classic VB Corner column. You can contact him through his Web site if you'd like to suggest future topics for this column.

comments powered by Disqus

Featured

  • Mastering Blazor Authentication and Authorization

    At the Visual Studio Live! @ Microsoft HQ developer conference set for August, Rockford Lhotka will explain the ins and outs of authentication across Blazor Server, WebAssembly, and .NET MAUI Hybrid apps, and show how to use identity and claims to customize application behavior through fine-grained authorization.

  • Linear Support Vector Regression from Scratch Using C# with Evolutionary Training

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the linear support vector regression (linear SVR) technique, where the goal is to predict a single numeric value. A linear SVR model uses an unusual error/loss function and cannot be trained using standard simple techniques, and so evolutionary optimization training is used.

  • Low-Code Report Says AI Will Enhance, Not Replace DIY Dev Tools

    Along with replacing software developers and possibly killing humanity, advanced AI is seen by many as a death knell for the do-it-yourself, low-code/no-code tooling industry, but a new report belies that notion.

  • Vibe Coding with Latest Visual Studio Preview

    Microsoft's latest Visual Studio preview facilitates "vibe coding," where developers mainly use GitHub Copilot AI to do all the programming in accordance with spoken or typed instructions.

  • Steve Sanderson Previews AI App Dev: Small Models, Agents and a Blazor Voice Assistant

    Blazor creator Steve Sanderson presented a keynote at the recent NDC London 2025 conference where he previewed the future of .NET application development with smaller AI models and autonomous agents, along with showcasing a new Blazor voice assistant project demonstrating cutting-edge functionality.

Subscribe on YouTube