The Delphi Bug List

Entry No.
679
VCL - General - Registry - TRegistry
TRegistry.GetKeyNames returns a list of zero-lengthed strings.
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 4.03 5.0 5.01 6.0 6.01 6.02 Kylix 1.0
N/AGotchaGotchaGotchaGotchaGotchaGotchaGotchaGotchaGotchaGotchaGotchaGotchaGotchaN/A
Description
Reported by Jon Oliver; checked by Jordan Russell
TRegistry.GetKeyNames returns a list of zero-lengthed strings.

Steps to Reproduce:

  1. Open a "troublesome" key--see below.
  2. Get the list of subkey names.

Why is this a bug: it's not legitimate for a subkey's name to be blank.
What's the cause: a bug in ADVAPI32's RegQueryInfoKey function
What's a workaround: don't entirely trust RegQueryInfoKey's results

Version of Delphi: D5u1
OS: Windows 2000 sp2

I'm not sure if this qualifies as a Delphi bug, as it's inherited from ADVAPI32.DLL.

At any rate, for some registry keys TRegistry.GetKeyNames will return a list of zero-lengthed key names. The list has the proper count, but lacks the text. This is a consequence of RegQueryInfoKey returning zero for the maximum length of a key name.

If D5 takes a hit for this is, I suppose, a matter for debate. I can find no microsoft document that addresses this behavior exactly--so how would Boland's developers expect it? At any rate, you can work around it somewhat, but distrusting RegQueryInfoKey's returns--there are MS documents that suggest that at least.

Oddly enough, if you rename and rename back one of the troublesome keys, it heals itself. This suggests to me that the key info values (the troublesome one at least) are cached, rather than computed.

Here is a console app that searches your registry for "troublesome" keys.

Solution / workaround
Jordan Russell:
Adding a line to TRegistry.GetKeyNames to force a minimum length for the buffer worked for me:
procedure TRegistry.GetKeyNames(Strings: TStrings);
var
  Len: DWORD;
  I: Integer;
  Info: TRegKeyInfo;
  S: string;
begin
  Strings.Clear;
  if GetKeyInfo(Info) then
  begin
    if Info.MaxSubKeyLen < 255 then Info.MaxSubKeyLen := 255;
    SetString(S, nil, Info.MaxSubKeyLen + 1);
    for I := 0 to Info.NumSubKeys - 1 do
    begin
      Len := Info.MaxSubKeyLen + 1;
      RegEnumKeyEx(CurrentKey, I, PChar(S), Len, nil, nil, nil, nil);
      Strings.Add(PChar(S));
    end;
  end;
end;
User-contributed comments
Jon Oliver
20 Feb 2002  05:57 AM GMT
I've looked into this a bit more.

You can't really trust MaxSubKeyLen or MaxValueNameLen are returned from RegQueryInfoKey. It's just not getting updated properly.

Jordan's workaround does it for me -- you'll have to implement it in GetValueNames as well.

RegHance 2.1 is a great product, but it has been snafu'd by this "feature" of the reg api. Attempting to do a registry wide search will cause it to gag on the mis-measured key and value names.

You can induce the registry to correct itself, though. For bad MaxSubKeyLen readings, add a subkey (a sibling to those measured) and then delete it.

For MaxValueNameLen, you do similarly. One difference (on this Win2000 box,) though, is that the temporary value name must be longer than the erroneous MaxValueNameLen, or it will not trigger the recalculation.

I've got some code to do this, if you're having difficulty with RegHance or some other favored registry tool. Regedit (surprise) is not affected by this Windows defect.
Jon Oliver
20 Feb 2002  06:26 AM GMT
I found an article that's worth heeding.

RegQueryInfoKey() Function May Return Zero for ControlSetnnn
Subkey Parameters (Q307308)
http://support.microsoft.com/default.aspx?scid=kb;en-us;Q307308

It looks as if GetKeyNames needs a complete overhaul:
procedure TRegistry.GetKeyNames(Strings: TStrings);
const
  MAX_SUBKEY_LEN = 255;
var
  I: Integer;
  S: string;
  sz: DWORD;
begin
  Strings.Clear;
  SetString(S, nil, MAX_SUBKEY_LEN + 1);

  I := 0;
  repeat
    sz := MAX_SUBKEY_LEN + 1;
    case RegEnumKeyEx(CurrentKey, I, PChar(S), sz, nil, nil, nil, nil) of
    ERROR_SUCCESS:
      Strings.Add(PChar(S));
    else // ERROR_NO_MORE_ITEMS:
      break;
    end;
    Inc(I);
  until False; 
end;
Latest update of this entry: 2001-12-27

Post a comment on this bug


Index page
Delphi Bug List home page
The Delphi Bug Lists are presently maintained by Jordan Russell, who has taken over this task from Reinier Sterkenburg since August 2000.
All feedback is appreciated. See also the feedback section of the Delphi Bug List home page.