Windows 安装后和 64 位注册表调整
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 地狱的深处,才能理解整个事情。我不明白为什么注册表像这样被拼凑在一起。可以想象出更多优雅的解决方案,但我猜这是一个快速修复,在大多数情况下都有效,因此应用了鸵鸟算法来编写它。