2012-02-02 139 views
8

我一直在使用来自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; 
+5

为什么不使用TRegistry?你的逻辑也是一团糟。你想要一个函数来调用来读取值。称它两次。第一次通过0.第二次通过KEY_WOW64_64KEY。如果第一次失败,只调用第二次。这是提取方法重构。 – 2012-02-02 22:59:26

+0

我之前使用过TRegistry,但并不知道你通过带参数的构造函数添加KEY_WOW64_64KEY。 – 2012-02-03 10:02:32

+1

当然可以。看到我的答案。编辑:看起来你现在找到了我的答案。另请注意,您可以随时修改'Access'属性以在已创建的注册表对象中切换视图。 – 2012-02-03 10:07:41

回答

6

你的代码是不必要的复杂的,主要是因为你没有利用内置的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中的等效定义。

+0

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

+0

我的代码中存在一个我刚刚修复的错误。 RootKey。但你已经知道了。你的代码应该工作。那里的钥匙?!我会在一个小时左右检查我的机器。晚餐时间在这里! – 2012-02-03 18:27:31

+0

我测试了代码。我修复了另一个bug,因为我不明白'TRegistry'是如何工作的,当一个值不存在时。但是,如果你看到一个空字符串,那么就会出现错误。我的代码(和我的代码)在运行时返回正确的值。我很想知道你的Delphi版本是什么。也许你在一个旧的Delphi上,“TRegistry”不尊重'KEY_WOW64_64KEY'。这会让我感到惊讶。由于您正在定义'KEY_WOW64_64KEY',所以我对此有点怀疑,但我只是从Windows.pas中获得它。那么,你在用什么德尔福? – 2012-02-03 19:31:36

10

我会建议你使用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 2012-02-03 11:49:28

+1

正是如此。如果你总是想从原生视图读取,你可以无条件地使用'KEY_WOW64_64KEY' – 2014-05-22 15:25:35

+2

只有在XP和更高版本中才能识别64位系统的存在,即使在32位版本中也是如此。如果您在Win2k或更低版本上指定该标志,它将作为未知参数失败。在这些系统上,无论如何都需要动态加载'IsWow64Process()'来检测WOW64是否存在。 – 2014-05-22 15:46:47

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; 

这是@Remy Lebeau - TeamB的好点,我应该适当地修改上面的代码。

+0

在调用'ReadString()'之前,您不需要检查'ValueExists()'。如果该值不存在,它将返回一个空字符串,但不会像其他阅读方法那样引发异常。 – 2014-05-22 15:49:12

相关问题