如何从32位进程读取64位注册表项?
我一直在使用来自HKEY_LOCAL_MACHINE\Software\Microsoft\Cryptography
的密钥MachineGuid
的值来唯一标识主机,但是使用在64位计算机上运行的32位进程的值似乎丢失了。我想它是在Wow6432Node下搜索的,它确实缺少。根据this你应该能够通过添加一个标志,以获得正确的关键,但下面的代码仍然没有出现工作。我错过了什么?如何从32位进程读取64位注册表项?
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;
你的代码是不必要的复杂的,主要是因为你没有利用内置的TRegistry
类,它避免了低层注册表API的复杂性。例如,请考虑下面的代码:
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;
功能ReadRegStr
将从关键Key
相对于根密钥Root
返回命名为Name
字符串值。如果存在错误,例如,如果键或名称不存在,或者值的类型错误,则会引发异常。
View
参数是一个枚举,它使您可以轻松访问注册表的原生32位或64位视图。请注意,本机对于正在运行的进程来说是本机的。所以它将是32位视图的32位视图和64位过程的64位视图。此枚举映射.net中的等效定义。
nope仍然不起作用,ReadString给出空字符串(这可能是我的笔记本电脑有问题吗?):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; 如果r.OpenKeyReadOnly('Software \ Microsoft \ Cryptography')则 s:= r.ReadString('MachineGuid'); – 2012-02-03 18:08:11
我的代码中存在一个我刚刚修复的错误。 RootKey。但你已经知道了。你的代码应该工作。那里的钥匙?!我会在一个小时左右检查我的机器。晚餐时间在这里! – 2012-02-03 18:27:31
我测试了代码。我修复了另一个bug,因为我不明白'TRegistry'是如何工作的,当一个值不存在时。但是,如果你看到一个空字符串,那么就会出现错误。我的代码(和我的代码)在运行时返回正确的值。我很想知道你的Delphi版本是什么。也许你在一个旧的Delphi上,“TRegistry”不尊重'KEY_WOW64_64KEY'。这会让我感到惊讶。由于您正在定义'KEY_WOW64_64KEY',所以我对此有点怀疑,但我只是从Windows.pas中获得它。那么,你在用什么德尔福? – 2012-02-03 19:31:36
我会建议你使用IsWow64Process()
功能要知道,当你在64位操作系统上运行32的过程,然后只在特定条件下应用KEY_WOW64_64KEY
标志。如果应用程序在32位操作系统上是32位进程,或者在64位操作系统上是64位进程,则不需要这些标志。
例如:
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;
我认为你可以简单地总是应用该标志,它在x86操作系统上被忽略 – Remko 2012-02-03 11:49:28
正是如此。如果你总是想从原生视图读取,你可以无条件地使用'KEY_WOW64_64KEY' – 2014-05-22 15:25:35
只有在XP和更高版本中才能识别64位系统的存在,即使在32位版本中也是如此。如果您在Win2k或更低版本上指定该标志,它将作为未知参数失败。在这些系统上,无论如何都需要动态加载'IsWow64Process()'来检测WOW64是否存在。 – 2014-05-22 15:46:47
在我使用此注册表项的我进了一步。如果该值不存在,我创建它:不在HKEY_LOCAL_MACHINE中,这需要提升,但在HKEY_CURRENT_USER中。任何人看到引进的钥匙都不会意识到这是一个虚拟。
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;
这是@Remy Lebeau - TeamB的好点,我应该适当地修改上面的代码。
在调用'ReadString()'之前,您不需要检查'ValueExists()'。如果该值不存在,它将返回一个空字符串,但不会像其他阅读方法那样引发异常。 – 2014-05-22 15:49:12
为什么不使用TRegistry?你的逻辑也是一团糟。你想要一个函数来调用来读取值。称它两次。第一次通过0.第二次通过KEY_WOW64_64KEY。如果第一次失败,只调用第二次。这是提取方法重构。 – 2012-02-02 22:59:26
我之前使用过TRegistry,但并不知道你通过带参数的构造函数添加KEY_WOW64_64KEY。 – 2012-02-03 10:02:32
当然可以。看到我的答案。编辑:看起来你现在找到了我的答案。另请注意,您可以随时修改'Access'属性以在已创建的注册表对象中切换视图。 – 2012-02-03 10:07:41