How can I read 64-bit registry key from a 32-bit process?

13,537

Solution 1

Your code is needlessly complex, largely because you are not taking advantage of the built-in TRegistry class which shields you from all the complexities of the low-level registry API. For example, consider the following code:

type
  TRegistryView = (rvDefault, rvRegistry64, rvRegistry32);

function RegistryViewAccessFlag(View: TRegistryView): LongWord;
begin
  case View of
  rvDefault:
    Result := 0;
  rvRegistry64:
    Result := KEY_WOW64_64KEY;
  rvRegistry32:
    Result := KEY_WOW64_32KEY;
  end;
end;

function ReadRegStr(const Root: HKEY; const Key, Name: string;
  const View: TRegistryView=rvDefault): string;
var
  Registry: TRegistry;
begin
  Registry := TRegistry.Create(KEY_READ or RegistryViewAccessFlag(View));
  try
    Registry.RootKey := Root;
    if not Registry.OpenKey(Key) then
      raise ERegistryException.CreateFmt('Key not found: %s', [Key]);
    if not Registry.ValueExists(Name) then
      raise ERegistryException.CreateFmt('Name not found: %s\%s', [Key, Name]);
    Result := Registry.ReadString(Name);//will raise exception in case of failure
  finally
    Registry.Free;
  end;
end;

The function ReadRegStr will return the string value named Name from the key Key relative to the root key Root. If there is an error, for example if the key or name do not exists, or if the value is of the wrong type, then an exception will be raised.

The View parameter is an enumeration that makes it simple for you to access native, 32-bit or 64-bit views of the registry. Note that native means native to the process that is running. So it will be the 32-bit view for a 32-bit process and the 64-bit view for a 64-bit process. This enumeration mirrors the equivalent definition in .net.

Solution 2

I would suggest you use the IsWow64Process() function to know when you are a 32-process running on a 64-bit OS, and then only apply the KEY_WOW64_64KEY flags in that specific condition. If the app is a 32-bit process on a 32-bit OS, or a 64-bit process on a 64-bit OS, the flags is not needed.

For example:

const 
  KEY_WOW64_64KEY = $0100; 
var 
  key: HKEY; 
  str: string; 
  len: DWORD; 
  flag: REGSAM;
  wow64: BOOL;
begin 
  flag := 0;
  wow64 := 0;
  IsWow64Process(GetCurrentProcess(), @wow64);
  if wow64 <> 0 then flag := KEY_WOW64_64KEY;

  if RegOpenKeyEx(HKEY_LOCAL_MACHINE, 'Software\Microsoft\Cryptography', 0, KEY_QUERY_VALUE or flag, key) = ERROR_SUCCESS then 
  try
    SetLength(str, 40); 
    len := Length(str) * SizeOf(Char); 
    if RegQueryValueEx(key, 'MachineGuid', nil, nil, PByte(Pointer(s)), @len) <> ERROR_SUCCESS then len := 0;
    SetLength(str, len div SizeOf(Char)); 
  finally
    RegCloseKey(key); 
  end; 
end;

Solution 3

In my use of this registry key I went a step further. If the value didn't exist I created it: not in HKEY_LOCAL_MACHINE, that would require elevation, but in HKEY_CURRENT_USER. Anyone seeing the introduced key there is unlikely to realise that it's a dummy.

function GetComputerGUID: String;
var
  Reg: TRegistry;
  oGuid: TGUID;
  sGuid: String;
begin
  Result := '';
  // Attempt to retrieve the real key
  Reg := TRegistry.Create(KEY_READ OR KEY_WOW64_64KEY);
  try
    Reg.RootKey := HKEY_LOCAL_MACHINE;
    if Reg.OpenKeyReadOnly('SOFTWARE\Microsoft\Cryptography') and Reg.ValueExists('MachineGuid') then
      Result := Reg.ReadString('MachineGuid');
    Reg.CloseKey;
  finally
    Reg.Free;
  end;
  // If retrieval fails, look for the surrogate
  if Result = '' then begin
    Reg := TRegistry.Create;
    try
      Reg.RootKey := HKEY_CURRENT_USER;
      if Reg.OpenKey('SOFTWARE\Microsoft\Cryptography', True) then begin
        if Reg.ValueExists('MachineGuid') then
          Result := Reg.ReadString('MachineGuid')
        else begin
          // If the surrogate doesn't exist, create it
          if CreateGUID(oGUID) = 0 then begin
            sGuid := Lowercase(GUIDToString(oGUID));
            Reg.WriteString('MachineGuid', Copy(sGuid, 2, Length(sGUID) - 2));
            Result := Reg.ReadString('MachineGuid');
          end;
        end;
      end;
      Reg.CloseKey;
    finally
      Reg.Free;
    end;
  end;
  if Result = '' then
    raise Exception.Create('Unable to access registry value in GetComputerGUID');
end;

That's a good point from @Remy Lebeau - TeamB though; I should mod the above code appropriately.

Solution 4

Call reg.exe using this path C:\Windows\sysnative\reg.exe For example:

C:\Windows\sysnative\reg.exe QUERY "HKLM\SOFTWARE\JavaSoft\JDK" /v CurrentVersion

source: https://stackoverflow.com/a/25103599

Share:
13,537
Stijn Sanders
Author by

Stijn Sanders

Have been doing BASIC, Turbo Pascal, Delphi, HTML, CSS, C/C++, C#, Cold Fusion, PHP, Javascript, SQL, regular expressions, but like any kind of code.

Updated on July 16, 2022

Comments

  • Stijn Sanders
    Stijn Sanders almost 2 years

    I've been using the value of key MachineGuid from HKEY_LOCAL_MACHINE\Software\Microsoft\Cryptography to uniquely identify hosts, but from 32-bit processes running on 64-bit computers, the value appears to be missing. I guess it's searching under Wow6432Node, where it is indeed missing. According to this you should be able to get to the right key by adding a flag, but below code still doesn't appear to do the job. What am I missing?

    const
      KEY_WOW64_64KEY=$0100;
    var
      r:HKEY;
      s:string;
      i,l:integer;
    begin
      //use cryptography machineguid, keep a local copy of this in initialization?
      l:=40;
      if RegOpenKeyEx(HKEY_LOCAL_MACHINE,PChar('Software\Microsoft\Cryptography'),
        0,KEY_QUERY_VALUE,r)=ERROR_SUCCESS then
       begin
        SetLength(s,l);
        if RegQueryValue(r,'MachineGuid',PChar(s),l)=ERROR_SUCCESS then
         begin
          SetLength(s,l);
          RegCloseKey(r);
         end
        else
         begin
          //try from-32-to-64
          RegCloseKey(r);
          if RegOpenKeyEx(HKEY_LOCAL_MACHINE,PChar('Software\Microsoft\Cryptography'),
            0,KEY_QUERY_VALUE or KEY_WOW64_64KEY,r)=ERROR_SUCCESS then
           begin
            l:=40;
            if RegQueryValue(r,'MachineGuid',PChar(s),l)=ERROR_SUCCESS then
              SetLength(s,l)
            else
              l:=0;
            RegCloseKey(r);
           end;
         end;
       end;
    
  • Remko
    Remko over 12 years
    I think you can simply always apply the flag, it is ignored on x86 os
  • Stijn Sanders
    Stijn Sanders over 12 years
    nope still doesn't work, ReadString gives empty string (could it my a problem witht his laptop?): const KEY_WOW64_64KEY=$0100; var r:TRegistry; s:string; begin r:=TRegistry.Create(KEY_READ or KEY_WOW64_64KEY); r.RootKey:=HKEY_LOCAL_MACHINE; if r.OpenKeyReadOnly('Software\Microsoft\Cryptography') then s:=r.ReadString('MachineGuid');
  • David Heffernan
    David Heffernan over 12 years
    There was a bug in my code which I just fixed re. RootKey. But you already got that. Your code should work. Is that key there?! I'll check on my machine in an hour or so. Dinner time here!!
  • David Heffernan
    David Heffernan over 12 years
    I've tested the code. There was another bug which I fixed because I didn't understand quite how TRegistry works when a value is not present. But if you are seeing an empty string then there is something wrong. Your code (and my code) return the correct value when I run them. I do wonder what version of Delphi you are on. Perhaps you are on an old Delphi for which TRegistry doesn't respect KEY_WOW64_64KEY. That would surprise me though. I am a bit suspicious of this since you are defining KEY_WOW64_64KEY but I am just getting it from Windows.pas. So, what Delphi are you using?
  • Stijn Sanders
    Stijn Sanders over 12 years
    Delphi 7, I checked Registry.pas, and the FAccess value is properly passed for samDesired parameter in the OpenKey method, but overwritten in the OpenKeyReadOnly method! <s>So if you change...</s> Strange, I can change you're answer form OpenKeyReadOnly to OpenKey, guess I have enough rep.
  • Stijn Sanders
    Stijn Sanders over 12 years
    Accepted! In case you're interested: github.com/stijnsanders/TMongoWire/commit/…
  • David Heffernan
    David Heffernan over 12 years
    @StijnSanders Excellent work. Well done and thanks for teaching me something new.
  • David Heffernan
    David Heffernan almost 10 years
    Exactly. If you always want to read from the native view, you can use KEY_WOW64_64KEY unconditionally
  • Remy Lebeau
    Remy Lebeau almost 10 years
    Only on XP and later, which recognize the existence of 64bit systems, even in the 32bit versions. If you specify the flag on Win2k or earlier, it will fail as an unknown parameter. On those systems, IsWow64Process() needs to be dynamically loaded anyway to detect if WOW64 is even present.
  • Remy Lebeau
    Remy Lebeau almost 10 years
    You don't need to check ValueExists() before calling ReadString(). It returns an empty string if the value does not exist, it does not raise an exception like the other reading methods do.