最近我需要通过Ketarin 自动安装Natural Docs。我可以假设它已安装到默认路径 (%ProgramFiles(x86)%\Natural Docs),但我决定采取一种安全的方法。可悲的是,即使安装程序在 HKLM\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall 上创建了一个密钥,它也没有任何价值让我找到安装目录。
Stein 的回答建议使用 AppSearch MSI 功能,它看起来很有趣,但遗憾的是,Natural Docs MSI 安装程序没有为他的方法工作提供签名表。
所以我决定在注册表中搜索任何对 Natural Docs 安装目录的引用,并在 HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components 键中找到一个。
我在 C# 中为 Ketarin 开发了一个允许递归的 Reg 类。所以我通过HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components 查看所有值,如果在其中一个子键值中找到主应用程序可执行文件(NaturalDocs.exe),则将其提取(C:\Program Files (x86)\Natural Docs\NaturalDocs.exe 变为C:\Program Files (x86)\Natural Docs)并将其添加到系统环境变量 %PATH% (所以我可以直接调用“NaturalDocs.exe”而不是使用完整路径)。
注册表“类”(实际上是函数)可以在 GitHub (RegClassCS) 上找到。
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo("NaturalDocs.exe", "-h");
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
var process = System.Diagnostics.Process.Start (startInfo);
process.WaitForExit();
if (process.ExitCode != 0)
{
string Components = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components";
bool breakFlag = false;
string hKeyName = "HKEY_LOCAL_MACHINE";
if (Environment.Is64BitOperatingSystem)
{
hKeyName = "HKEY_LOCAL_MACHINE64";
}
string[] subKeyNames = RegGetSubKeyNames(hKeyName, Components);
// Array.Reverse(subKeyNames);
for(int i = 0; i <= subKeyNames.Length - 1; i++)
{
string[] valueNames = RegGetValueNames(hKeyName, subKeyNames[i]);
foreach(string valueName in valueNames)
{
string valueKind = RegGetValueKind(hKeyName, subKeyNames[i], valueName);
switch(valueKind)
{
case "REG_SZ":
// case "REG_EXPAND_SZ":
// case "REG_BINARY":
string valueSZ = (RegGetValue(hKeyName, subKeyNames[i], valueName) as String);
if (valueSZ.IndexOf("NaturalDocs.exe") != -1)
{
startInfo = new System.Diagnostics.ProcessStartInfo("setx", "path \"%path%;" + System.IO.Path.GetDirectoryName(valueSZ) + "\" /M");
startInfo.Verb = "runas";
process = System.Diagnostics.Process.Start (startInfo);
process.WaitForExit();
if (process.ExitCode != 0)
{
Abort("SETX failed.");
}
breakFlag = true;
}
break;
/*
case "REG_MULTI_SZ":
string[] valueMultiSZ = (string[])RegGetValue("HKEY_CURRENT_USER", subKeyNames[i], valueKind);
for(int k = 0; k <= valueMultiSZ.Length - 1; k++)
{
Ketarin.Forms.LogDialog.Log("valueMultiSZ[" + k + "] = " + valueMultiSZ[k]);
}
break;
*/
default:
break;
}
if (breakFlag)
{
break;
}
}
if (breakFlag)
{
break;
}
}
}
即使不使用 Ketarin,也可以轻松粘贴函数并通过 Visual Studio 或CSC 构建它。
可以使用RegClassVBS 采取更通用的方法,该方法允许注册表项递归并且不依赖于 .NET Framework 平台或构建过程。
请注意,枚举组件密钥的过程可能是 CPU 密集型的。上面的示例有一个 Length 参数,您可以使用该参数向用户显示一些进度(可能类似于“i from (subKeysName.Length - 1) keys remaining” - 有创意)。在 RegClassVBS 中可以采用类似的方法。
两个类(RegClassCS 和 RegClassVBS)都有可以指导您的文档和示例,您可以在任何软件中使用它并为它们的开发做出贡献,在 git repo 上提交,并且(当然)打开一个问题如果您发现任何您无法自行解决的问题,请访问它的 github 页面,以便我们尝试重现该问题以找出我们可以做些什么。 =)