如何从32位进程中读取64位注册表键?

11

我一直使用从HKEY_LOCAL_MACHINE \ Software \ Microsoft \ Cryptography获取的键MachineGuid的值来唯一标识主机,但在64位计算机上运行的32位进程中,该值似乎丢失了。 我猜它正在Wow6432Node下搜索,确实缺失了。根据此文档,您应该能够通过添加标志来访问正确的键,但是以下代码似乎仍然不能完成工作。我错过了什么?

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;

5
为什么不使用 TRegistry?而且你的逻辑混乱不堪。你想要一个单独的函数来调用以读取该值。调用两次。第一次传递 0。第二次传递 KEY_WOW64_64KEY。只有在第一次失败时才调用第二次。这是提取方法重构。 - David Heffernan
我之前使用过TRegistry,但不知道可以通过构造函数参数添加KEY_WOW64_64KEY。 - Stijn Sanders
1
当然可以。请看我的回答。编辑:看起来你现在找到了我的答案。还要注意,您可以随时修改“Access”属性以在已创建的注册表对象中切换视图。 - David Heffernan
4个回答

11
我建议您使用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;

3
我认为你可以简单地始终应用该标志,因为它在x86操作系统上被忽略。 - Remko
1
没错。如果您始终想从本机视图中读取,可以无条件使用 KEY_WOW64_64KEY - David Heffernan
2
仅适用于 XP 及以上系统,这些系统识别 64 位系统的存在,即使在 32 位版本中。如果您在 Win2k 或更早版本上指定标志,则会失败,作为未知参数。在这些系统上,无论如何都需要动态加载 IsWow64Process() 来检测是否存在 WOW64。 - Remy Lebeau

8

您的代码过于复杂,主要是因为您没有利用内置的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;
< p > ReadRegStr 函数将从相对于根键 Root 的键 Key 中返回名为 Name 的字符串值。如果存在错误,例如键或名称不存在,或者值的类型错误,则会引发异常。

View 参数是一个枚举,使您可以访问注册表的本机、32 位或 64 位视图。请注意,本机意味着与正在运行的进程相对应。因此,它将是 32 位进程的 32 位视图,而是 64 位进程的 64 位视图。该枚举反映了 .net 中等效定义。


不行,还是不起作用,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; if r.OpenKeyReadOnly('Software\Microsoft\Cryptography') then s:=r.ReadString('MachineGuid'); - Stijn Sanders
我的代码中有一个错误,我刚刚修复了与RootKey有关的问题。但是你已经得到了这个信息。你的代码应该可以正常运行。那个密钥在那里?!我会在大约一个小时后检查一下我的电脑上是否存在。现在是晚餐时间! - David Heffernan
我已经测试了代码。还有一个错误,因为我不太理解TRegistry在值不存在时的工作原理,但如果你看到一个空字符串,那么肯定是有问题的。当我运行你的代码和我的代码时,它们都返回了正确的值。我想知道你使用的Delphi版本是什么。也许你使用的是旧版的Delphi,TRegistry不支持KEY_WOW64_64KEY。尽管这让我感到惊讶。我对此有点怀疑,因为你定义了KEY_WOW64_64KEY,而我只是从Windows.pas中获取它。那么,你使用的是哪个版本的Delphi呢? - David Heffernan
Delphi 7,我检查了Registry.pas文件,发现在OpenKey方法的samDesired参数中正确传递了FAccess值,但在OpenKeyReadOnly方法中被覆盖了!奇怪的是,我可以将你的答案从OpenKeyReadOnly更改为OpenKey,我猜我有足够的声望。 - Stijn Sanders
1
接受!如果你有兴趣:https://github.com/stijnsanders/TMongoWire/commit/e65a3af4c35948f5eb2a2cf311c1e3febd95b05d - Stijn Sanders
@StijnSanders 做得非常好。干得漂亮,感谢你教给我新的东西。 - David Heffernan

4

在使用这个注册表键时,我更进一步地操作了。如果该值不存在,我会创建它:不是在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;

然而,TeamB组的Remy Lebeau提出了一个很好的观点——我应该适当地修改上面的代码。


在调用ReadString()之前,您不需要检查ValueExists()。如果该值不存在,它将返回一个空字符串,而不会像其他读取方法一样引发异常。 - Remy Lebeau

1

使用以下路径调用reg.exe C:\Windows\sysnative\reg.exe 例如:

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

来源:https://stackoverflow.com/a/25103599


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接