How can I read 64-bit registry key from a 32-bit process?
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
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, 2022Comments
-
Stijn Sanders almost 2 years
I've been using the value of key
MachineGuid
fromHKEY_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 over 12 yearsI think you can simply always apply the flag, it is ignored on x86 os
-
Stijn Sanders over 12 yearsnope 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 over 12 yearsThere 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 over 12 yearsI'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 whichTRegistry
doesn't respectKEY_WOW64_64KEY
. That would surprise me though. I am a bit suspicious of this since you are definingKEY_WOW64_64KEY
but I am just getting it from Windows.pas. So, what Delphi are you using? -
Stijn Sanders over 12 yearsDelphi 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 over 12 yearsAccepted! In case you're interested: github.com/stijnsanders/TMongoWire/commit/…
-
David Heffernan over 12 years@StijnSanders Excellent work. Well done and thanks for teaching me something new.
-
David Heffernan almost 10 yearsExactly. If you always want to read from the native view, you can use
KEY_WOW64_64KEY
unconditionally -
Remy Lebeau almost 10 yearsOnly 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 almost 10 yearsYou don't need to check
ValueExists()
before callingReadString()
. It returns an empty string if the value does not exist, it does not raise an exception like the other reading methods do.