Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / lazarus / 2018-02

Windows Registry, how do I read data from rdExpandString

6 messages · 3 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Bo Berglund

I am writing a little Lazarus program to examine the registry values stored by a program we use.
So far I have only used strings and integers in the registry, and I have known in advance what type they are. Now I want to make a more general kind of reader and so I have created a read function like shown below.
So there is a case construct taht uses the data type reported on each item, but I don't know what to do about rdExpandString. In fact I don't know what kind of data may hide behind this data type...

I would apprecieate to get some clarification on this because googling has so far not resulted in anything for Pascal.

function ReadRegValues(Root: HKEY; Key: string; List: TStrings):
boolean;
var
  Reg: TRegistry;
  i: integer;
  sVal: string;
  siz: integer;
  DT: TRegDataType;
  BUF: TBytes;
begin
  List.Clear;
  Result := false;
  Reg := TRegistry.Create;
  try
    Reg.RootKey := Root;
    if not Reg.OpenKeyReadOnly(Key) then
      exit;
    try
      Reg.GetValueNames(List);
      for i := 0 to List.Count-1 do
      begin
        sVal := '';
        try
          if Reg.ValueExists(List[i]) then
          begin
            DT := Reg.GetDataType(List[i]);
  // one of (rdUnknown, rdString, rdExpandString, rdBinary, rdInteger)
            case DT of
              rdString:
                  sVal := Reg.ReadString(List[i]);
              rdExpandString: What to do here????;
              rdBinary:
                  begin
                    siz := Reg.GetDataSize(List[i]);
                    SetLength(BUF, siz);
                    sVal := '';
                    Reg.ReadBinaryData(List[i], BUF, siz);
                    for i := 0 to siz-1 do
                      sVal := sVal + IntToHex(BUF[i], 2) + ' ';
                  end;
              rdInteger:
                  sVal := IntToStr(Reg.ReadInteger(List[i]));
            end;
          end;
          List[i] := List[i] + '=' + sVal;
        except
        end;
      end;
      Result := true;
    except
    end;
  finally
    Reg.Free;
  end;
end;
Bo Berglund Developer in Sweden

Reply by email

Bo Berglund
to Bo Berglund
Bo Berglund wrote
So there is a case construct taht uses the data type reported on each item, but I don't know what to do about rdExpandString. In fact I don't know what kind of data may hide behind this data type...

What I want to know is how to treat data of the type REG_EXPAND_SZ in the registry when reading it. THere are not tha many such but for completeness I want to be able to display these kinds of data too.

And my code example in my previous post is not working, it contains a few obvious bugs like using a loop variable inside the loop as a new loop variable. But these are ironed out already so right now my main concerns is still the treatment of REG_EXPAND_SZ data...

I googled and followed links into the Microsoft site where the format should be described but it was only in words and no exact spec that I could find. Like this:
https://support.microsoft.com/en-us/help/256986

Any ideas on what to do?

Bo Berglund Developer in Sweden

Reply by email

Bo Berglund
to Bo Berglund
Bo Berglund wrote
What I want to know is how to treat data of the type REG_EXPAND_SZ in the registry when reading it. There are not that many such but for completeness I want to be able to display these kinds of data too.

If I use Reg.ReadString method on a REG_EXPAND_SZ item which I have found in the user settings in the registry for CVSNT I get back a string looking like this:
C:\Programs\cvsnt\cvsnt-default.pem

But if I export the registry key with RegEdit I get this instead: "CertificateFile"=hex(2):43,00,3a,00,5c,00,50,00,72,00,6f,00,67,00,72,00,61,00,\

6d,00,73,00,5c,00,63,00,76,00,73,00,6e,00,74,00,5c,00,63,00,76,00,73,00,6e,\

00,74,00,2d,00,64,00,65,00,66,00,61,00,75,00,6c,00,74,00,2e,00,70,00,65,00,\ 6d,00,00,00

It looks like the data is encoded with 16 bit values per character and stored in successive 1-byte hex values with lsb first, but is this always the case and why does the ReadString function not deliver the hex in that case.

Can someone explain how it works? And why there is no TRegistry class method for fetching the REG_EXPAND_SZ data type?
Does it have something to do with Unicode on Windows (I am testing on a Windows 7 Pro X64 machine)?

Bo Berglund Developer in Sweden

Reply by email

Alexander Hofmann
to Bo Berglund

Hi,

as stated in the MSDN article you cited, the REG_EXPAND_SZ type is a hint to the API/API-User, that the string (stored as any other string either 2-byte or 1-byte depending in the Version) might contain environment variables, that need to be expanded before use.

Respective M$ PowerShell, REG.exe versions etc. honor this hint and expand the variables before returning you the results...

Bo Berglund wrote 5 lines
If I use Reg.ReadString method on a REG_EXPAND_SZ item which I have found in the user settings in the registry for CVSNT I get back a string looking like this: C:\Programs\cvsnt\cvsnt-default.pem

Does it look like this in Regedit.exe? I think yes in this case. The REG_EXPAND_SZ was "misused" here, because the next hex data below translates to the same text. The intended use of REG_EXPAND_SZ would be to store "%programfiles%\cvsnt\cvsnt-default.pem" instead - so that on internationalized (pre-win7/vista) systems it might expand to e.g. "c:\Programme\"...

Show quoted text 7 lines
But if I export the registry key with RegEdit I get this instead: "CertificateFile"=hex(2):43,00,3a,00,5c,00,50,00,72,00,6f,00,67,00,72,00,61,00,\ 6d,00,73,00,5c,00,63,00,76,00,73,00,6e,00,74,00,5c,00,63,00,76,00,73,00,6e,\ 00,74,00,2d,00,64,00,65,00,66,00,61,00,75,00,6c,00,74,00,2e,00,70,00,65,00,\ 6d,00,00,00

See e.g.
https://social.msdn.microsoft.com/Forums/en-US/6bbef2d0-425d-4fb7-8ced-29ff18b91698/regexpandsz-to-regsz-question?forum=vblanguage REG_EXPAND_SZ has to be encoded in Hex to be compatible with old Regedit versions / to be sure it is stored with the correct datatype.

Can someone explain how it works? And why there is no TRegistry class method for fetching the REG_EXPAND_SZ data type?

That I don't know - but according to
https://msdn.microsoft.com/de-de/library/windows/desktop/ms724884(v=vs.85).aspx, you are required to do the conversion yourself, i.e. call ExpandEnvironmentStrings to get a usable path. But - you should try reading a "correct" REG_EXPAND_SZ value first, or look at the code of TRegistry, to see if there is any conversion going for displaying - I would suggest displaying the "original" value (without expansion) alongside with a "current reading" e.g. containing the expanded string for the current system/user.

Greetings,
Alex

Reply by email

Bo Berglund
to Alexander Hofmann
Alexander Hofmann wrote 10 lines
Hi, as stated in the MSDN article you cited, the REG_EXPAND_SZ type is a hint to the API/API-User, that the string (stored as any other string either 2-byte or 1-byte depending in the Version) might contain environment variables, that need to be expanded before use. Respective M$ PowerShell, REG.exe versions etc. honor this hint and expand the variables before returning you the results...

...

Show quoted text 5 lines
As for displaying - I would suggest displaying the "original" value (without expansion) alongside with a "current reading" e.g. containing the expanded string for the current system/user.

Thanks, then I will use ReadString for both types and let the registry "fix" the values and expand the environment variables as needed.

While I have been looking at this I am getting more and more confused about the 32/64 bit issues....

I had the impression before embarking on this that the 32 bit applications would read/write into Software/Wow6432Node/<target key> and would not be able to reach the Software/<target key>. But that seems not to be the case for my Lazarus program because in my viewer I can navigate the registry and I see the Wow6432Node item and can step into it too.
I will have to stuff this into Delphi to see if it holds true there as well. Very confusing.

I have not yet embarked on 64 bit computing (at least I believe not).

Bo Berglund Developer in Sweden

Reply by email

Sven Barth
to Bo Berglund
Bo Berglund wrote 6 lines
I had the impression before embarking on this that the 32 bit applications would read/write into Software/Wow6432Node/<target key> and would not be able to reach the Software/<target key>. But that seems not to be the case for my Lazarus program because in my viewer I can navigate the registry and I see the Wow6432Node item and can step into it too.

32-bit applications are de facto accessing Software/Wow6432Node/<target key>, but to them it appears as if they're accessing Software/<target key>. This is handled transparently by Windows.
If you want to disable this, you need to create the TRegistry class with the KEY_WOW64_64KEY flag set, this way you'll access the real Software/<target key>. Same is true if you use RegCreateKeyEx(), RegOpenKeyEx() or RegDeleteKeyEx() directly.

Regards,
Sven

Reply by email