65.9K
CodeProject 正在变化。 阅读更多。
Home

Windows 安装后和 64 位注册表调整

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2012年6月30日

CPOL

2分钟阅读

viewsIcon

20138

downloadIcon

172

WPI 和 64 位注册表集成。

引言

当微软开始构建其操作系统的 x64 版本,该版本也可以运行 32 位应用程序时,他们需要找到一种统一注册表的方法,以便可以透明地为两者提供服务。由于从未预见到这一点,所以拼凑了一个典型的微软事后补丁来使其工作。而且它的确做到了,考虑到这件事的混乱本质,做得相当不错。但是,跨 32 位和 64 位版本导入注册表调整是有问题的。

背景

我的一个朋友沮丧地发现,在使用 WPI 调用 Regedit.exe 添加注册表调整时,在 64 位环境中没有像预期的那样工作,而它们在 32 位环境中却工作得很好。由于他对此不熟悉,他请我看看。发生的情况是,WPI 是一个 32 位应用程序,因此在 64 位环境下,注册表项没有写入注册表根键 HKEY_LOCAL_MACHINE,而所有其他键都写入了。这是有道理的,因为 WPI 调用了 32 位 cmd.exe 来执行 Regedit.exe,只有通用键被写入。

使用代码

由于他想继续使用 32 位 WPI,我提出了以下解决方案。基本原则是编写一个编译为 64 位的“轻量级” Regedit 来进行注册表写入。代码使用 Delphi 编写,因为我只有 Delphi 64 位交叉编译器。由于我本人是一个从未见过 Delphi 的 C 程序员,我使用一个面向 Delphi 初学者的网站编写了它,所以作为 Delphi 代码,它可能不是最好的。但它确实有效。一个代码片段

if Pos('[',Temp)>0 then
    begin
      Rootkeyflag:=true;
      Delim:=Pos(']',Temp);
      Slash:=Pos('\',Temp);
      Rootkey:=MidStr(Temp,2,slash-2);
      if Rootkey='HKEY_LOCAL_MACHINE' then OurReg.RootKey:=HKEY_LOCAL_MACHINE;
      if Rootkey='HKEY_CURRENT_USER' then OurReg.RootKey:=HKEY_CURRENT_USER;
      if Rootkey='HKEY_CLASSES_ROOT' then OurReg.RootKey:=HKEY_CLASSES_ROOT;
      if Rootkey='HKEY_CURRENT_CONFIG' then OurReg.RootKey:=HKEY_CURRENT_CONFIG;
      if Rootkey='HKEY_USERS' then OurReg.RootKey:=HKEY_USERS;
      Subkey:=MidStr(Temp,slash+1,Length(Temp));
      Subkey:=LeftStr(Subkey,Length(Subkey)-1);
      if Pos('-',Rootkey)>0 then
       begin
        Rootkey:=RightStr(Rootkey,Length(Rootkey)-1);
        Deleteflag:=true;
       end
      else
        begin
         Deleteflag:=false;
        end;
       end;

    if (Rootkeyflag) and (not Addvalueflag) then
// {by now we have read all lines of complete instruction and can see what to do}   
      begin
       if Deleteflag then
        begin
         if Length(Value)>0 then
           begin
            openResult := OurReg.OpenKey(Subkey,True);
            if DeleteKeyflag then 
             begin
               OurReg.GetDataAsString(Temp);
               Equalsign:=Pos('=',Temp);
               Value:=RightStr(Temp,Length(Temp)-Equalsign);
             end;
              if openResult then OurReg.DeleteValue(Value);
              Key:='';
              ActionTaken:=true;
           end;
          end;
 //        { we can't put an ELSE directive here since we want to end in the reset variables code} 
         if (not Deleteflag) or (Key='') then
          begin
           if Length(Key)>0 then
            begin
             openResult := OurReg.OpenKey(Subkey,True);
             if openResult then
              begin
               ActionTaken:=true;
               if Key='@' then Key:='';
               case Keytype of
 //              { for some odd reason sometimes the StrToInt and StrToFloat conversion routines 
 //                fail (only sometimes) in the debugger if the string contains 000000. Go figure}
               1: try
                    OurReg.WriteInteger(Key,StrToInt(Value));
                  except on E: Exception do
                    OurReg.WriteInteger(Key,0);
                  end;
               2: OurReg.WriteExpandString(Key,Value);
               4: OurReg.WriteBinaryData(Key,Value,Length(Value));
               5: try
                    OurReg.WriteFloat(Key,StrToFloat(Value));
                  except on E: Exception do
                    OurReg.WriteFloat(Key,0);
                  end;
               6: OurReg.WriteString(Key,Value);
              end;
              Rootkeyflag:=false;
             end;
            end;

然而,使用未修改的 WPI 使其运行,只需将生成的 64 位可执行文件添加到脚本中,因为 WPI 脚本将加载 32 位命令 shell,该命令 shell 将对 64 位代码感到窒息。我朋友的一些异想天开的想法让他创建了一个自执行的 Winrar 文件,其中包含将注册表项输入并调用该 WPI 脚本所需的文件,这非常有效,因为 Winrar 正确地执行了它。我假设它也能与 Winzip 一起使用,但从未尝试过。这样,无论使用哪个操作系统版本,都可以使用未修改的 WPI 写入所有键。

兴趣点 

微软的这些补丁的本质常常令人困惑,特别是对于那些不得不忍受旧版 api 以使应用程序工作的人来说。它们文档记录得很差,而且通常到处都是。为了让它真正发挥作用,我不得不深入到微软 MSDN 地狱的深处,才能理解整个事情。我不明白为什么注册表像这样被拼凑在一起。可以想象出更多优雅的解决方案,但我猜这是一个快速修复,在大多数情况下都有效,因此应用了鸵鸟算法来编写它。

© . All rights reserved.