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

Posted

技术标签:

【中文标题】如何从 32 位进程读取 64 位注册表项?【英文标题】:How can I read 64-bit registry key from a 32-bit process? 【发布时间】:2012-02-25 14:55:32 【问题描述】:

我一直在使用来自HKEY_LOCAL_MACHINE\Software\Microsoft\Cryptography 的键MachineGuid 的值来唯一标识主机,但是对于在64 位计算机上运行的32 位进程,该值似乎丢失了。我猜它是在 Wow6432Node 下搜索,它确实不见了。根据this,您应该可以通过添加标志来获得正确的键,但下面的代码似乎仍然无法完成这项工作。我错过了什么?

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?你的逻辑也是一团糟。您希望调用单个函数来读取值。叫它两次。第一次通过 0。第二次通过 KEY_WOW64_64KEY。如果第一次失败,只有第二次调用。这是提取方法重构。 我以前用过TRegistry,不知道你可以通过带参数的构造函数添加KEY_WOW64_64KEY。 当然可以。看我的回答。编辑:看起来你现在找到了我的答案。另请注意,您可以随时修改Access 属性以在已创建的注册表对象中切换视图。 【参考方案1】:

我建议您使用 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 操作系统上被忽略 没错。如果你总是想从原生视图读取,可以无条件使用KEY_WOW64_64KEY 仅在 XP 及更高版本上,即使在 32 位版本中也能识别 64 位系统的存在。如果您在 Win2k 或更早版本上指定标志,它将作为未知参数失败。在这些系统上,无论如何都需要动态加载 IsWow64Process() 以检测 WOW64 是否存在。【参考方案2】:

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

View 参数是一个枚举,可让您轻松访问注册表的本机、32 位或 64 位视图。请注意,本机意味着正在运行的进程的本机。所以它将是 32 位进程的 32 位视图和 64 位进程的 64 位视图。此枚举反映了 .net 中的等效定义。

【讨论】:

不,仍然无法正常工作,ReadString 给出了空字符串(我的笔记本电脑有问题吗?): const KEY_WOW64_64KEY=$0100; var r:TRegistry; s:字符串;开始 r:=TRegistry.Create(KEY_READ 或 KEY_WOW64_64KEY); r.RootKey:=HKEY_LOCAL_MACHINE;如果 r.OpenKeyReadOnly('Software\Microsoft\Cryptography') 则 s:=r.ReadString('MachineGuid'); 我的代码中有一个错误,我刚刚修复了它。根密钥。但你已经明白了。您的代码应该可以工作。那把钥匙在吗?!我会在一个小时左右检查我的机器。晚餐时间到了!! 我已经测试了代码。我修复了另一个错误,因为我不太了解TRegistry 在不存在值时如何工作。但是如果你看到一个空字符串,那就有问题了。当我运行它们时,您的代码(和我的代码)返回正确的值。我确实想知道您使用的是哪个版本的Delphi。也许您使用的是 TRegistry 不尊重 KEY_WOW64_64KEY 的旧 Delphi。但这会让我感到惊讶。我对此有点怀疑,因为您正在定义KEY_WOW64_64KEY,但我只是从 Windows.pas 得到它。那么,您使用的是什么 Delphi? Delphi 7,我检查了Registry.pas,在OpenKey方法中为samDesired参数正确传递了FAccess值,但在OpenKeyReadOnly方法中被覆盖了! 所以如果你改变......奇怪,我可以将你的答案从 OpenKeyReadOnly 更改为 OpenKey,我猜我有足够的代表。 接受!如果您有兴趣:github.com/stijnsanders/TMongoWire/commit/…【参考方案3】:

在使用此注册表项时,我更进一步。如果该值不存在,我创建了它:不在 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()。如果值不存在,则返回一个空字符串,它不会像其他读取方法那样引发异常。【参考方案4】:

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

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

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

【讨论】:

以上是关于如何从 32 位进程读取 64 位注册表项?的主要内容,如果未能解决你的问题,请参考以下文章

如何查找注册表项? 32/64 位

vbscript 从32位应用程序读取和写入64位注册表

Qt_32位程序在64位系统下读取64位注册表

Qt_32位程序在64位系统下读取64位注册表

64位系统下注册32位dll文件

32 位和 64 位应用程序如何共享 HKLM 注册表设置?