Enumerate Windows Registry (WinAPI) - Delphi

DarkCoderSc personal avatar
DarkCoderSc

Jean-Pierre LESUEUR

// ...

uses
  System.SysUtils, Winapi.Windows, Generics.Collections;

// ...

const RRF_RT_ANY = $0000FFFF;

// ...

function RegGetValueW(
  hkey        : HKEY;
  lpSubKey    : LPCWSTR;
  lpValue     : LPCWSTR;
  dwFlags     : DWORD;
  var dwType  : DWORD;
  pvData      : PVOID;
  var pcbData : DWORD
) : LONG; stdcall; external advapi32 name 'RegGetValueW';

// ...

procedure EnumerateRegistryKeys(const ARegistryHive : HKEY; const ARegistryPath: String; var _outKeys : TList<String>; var _outValues : TDictionary<String, String>);
begin
  var hOpenedKey : HKEY;
  var ACloseKey := False;

  if not Assigned(_outKeys) then
    _outKeys := TList<String>.Create()
  else
    _outKeys.Clear();

  if not Assigned(_outValues) then
    _outValues := TDictionary<String, String>.Create()
  else
    _outValues.Clear();

  if not String.IsNullOrWhiteSpace(ARegistryPath) then begin
    var ARet := RegOpenKeyEx(
      ARegistryHive,
      PWideChar(ARegistryPath),
      0,
      KEY_READ,
      hOpenedKey
    );

    if ARet <> ERROR_SUCCESS then
      raise EWindowsException.Create('RegOpenKeyEx');

    ACloseKey := True;
  end else
    hOpenedKey := ARegistryHive;
  try
    var ASubKeysCount : DWORD;
    var AMaxKeyNameLength : DWORD;

    var AValuesCount : DWORD;
    var AMaxValueNameLength : DWORD;

    var ARet := RegQueryInfoKeyW(
        hOpenedKey,
        nil,
        nil,
        nil,
        @ASubKeysCount,
        @AMaxKeyNameLength,
        nil,
        @AValuesCount,
        @AMaxValueNameLength,
        nil,
        nil,
        nil
    );

    if ARet <> ERROR_SUCCESS then
      raise EWindowsException.Create('RegQueryInfoKeyW');

    if (ASubKeysCount = 0) and (AValuesCount = 0) then
      raise Exception.Create('No keys or values');

    // Include terminating NULL characters
    Inc(AMaxKeyNameLength);
    Inc(AMaxValueNameLength);

    var ABufferSize := 0;
    if AMaxKeyNameLength > AMaxValueNameLength then
      ABufferSize := AMaxKeyNameLength * SizeOf(WideChar)
    else
      ABufferSize := AMaxValueNameLength * SizeOf(WideChar);

    var pNameBuffer : PWideChar := nil;
    GetMem(pNameBuffer, ABufferSize);
    try
      // Enumerate SubKeys
      if ASubKeysCount > 0 then begin
        for var I := 0 to ASubKeysCount -1 do begin
          ZeroMemory(pNameBuffer, AMaxKeyNameLength * SizeOf(WideChar));

          var AKeyLength := AMaxKeyNameLength;

          ARet := RegEnumKeyExW(
            hOpenedKey,
            I,
            pNameBuffer,
            AKeyLength,
            nil,
            nil,
            nil,
            nil
          );
          if ARet <> ERROR_SUCCESS then
            continue;

          ///
          _outKeys.Add(String(pNameBuffer));
        end;
      end;

      // Enumerate Key-Values
      if AValuesCount > 0 then begin
        for var  I := 0 to AValuesCount -1 do begin
          ZeroMemory(pNameBuffer, AMaxKeyNameLength * SizeOf(WideChar));

          var AValueNameLength := AMaxValueNameLength;

          ARet := RegEnumValueW(
            hOpenedKey,
            I,
            pNameBuffer,
            AValueNameLength,
            nil,
            nil,
            nil,
            nil
          );
          if ARet <> ERROR_SUCCESS then
            continue;

          ///
          var AValueName := String(pNameBuffer);
          var AValueType : DWORD;
          var AValueDataSize : DWORD;
          var AData : String;

          ARet := RegGetValueW(
            hOpenedKey,
            nil,
            PWideChar(AValueName),
            RRF_RT_ANY,
            AValueType,
            nil,
            AValueDataSize
          );
          if ARet = ERROR_SUCCESS then begin
            var pData : Pointer;
            GetMem(pData, AValueDataSize * SizeOf(WideChar));
            try
              RegGetValueW(
                hOpenedKey,
                nil,
                PWideChar(AValueName),
                RRF_RT_ANY,
                AValueType,
                pData,
                AValueDataSize
              );

              case AValueType of
                REG_SZ : begin
                  AData := String(PWideChar(pData));
                end;

                REG_DWORD : AData := IntToStr(PDWORD(pData)^);
                REG_QWORD : AData := IntToStr(PUInt64(pData)^);

                // Etc...

                // ... handle other value types ... //

                // https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-value-types?WT_mc_id=SEC-MVP-5005282
              end;

              ///
              _outValues.Add(AValueName, AData);
            finally
              FreeMem(pData, AValueDataSize);
            end;
          end else
            AData := '<could not read>';
        end;
      end;
    finally
      FreeMem(pNameBuffer, ABufferSize);
    end;
  finally
    if ACloseKey then
      RegCloseKey(hOpenedKey);
  end;
end;

procedure PrintKeysValues(const AKeys : TList<String>; const AValues : TDictionary<String, String>);
begin
  if Assigned(AKeys) and (AKeys.Count > 0) then begin
    WriteLn('Keys');
    for var AKey in AKeys do
      WriteLn(#9 + AKey);
  end;

  if Assigned(AValues) and (AValues.Count > 0) then begin
    WriteLn('Values');

    var AValue : String;
    for var AValueName in AValues.Keys do begin
      if not AValues.TryGetValue(AValueName, AValue) then
        Exit();

      WriteLn(Format(#9 + '%s:%s', [AValueName, AValue]));
    end;
  end;
end;

// ...

var AKeys := TList<String>.Create();
var AValues := TDictionary<String, String>.Create();
try
  // Example 1 : HKCU Root
  try
    EnumerateRegistryKeys(HKEY_CURRENT_USER, '', AKeys, AValues);

    WriteLn('Example Enumerate HKCU Root:');
    PrintKeysValues(AKeys, AValues);
  except
    // ...
  end;

  WriteLn;
  WriteLn(' --- ');
  WriteLn;

  // Example 2 : HKCU\Software\Valve\Steam
  try
    EnumerateRegistryKeys(HKEY_CURRENT_USER, 'Software\RegisteredApplications', AKeys, AValues);

    WriteLn('Example Enumerate Steam Key:');
    PrintKeysValues(AKeys, AValues);
  except
    // ...
  end;
finally
  FreeAndNil(AKeys);
  FreeAndNil(AValues);
end;

// ...

Implemented By Technique


Created

April 13, 2025

Last Revised

April 13, 2025