MSI 为将来的卸载任务存储安装目录。
使用该INSTALLPROPERTY_INSTALLLOCATION
属性(即"InstallLocation"
)仅适用于安装程序在安装期间设置了该ARPINSTALLLOCATION
属性。但是这个属性是可选的,几乎没有人使用它。
如何检索安装目录?
MSI 为将来的卸载任务存储安装目录。
使用该INSTALLPROPERTY_INSTALLLOCATION
属性(即"InstallLocation"
)仅适用于安装程序在安装期间设置了该ARPINSTALLLOCATION
属性。但是这个属性是可选的,几乎没有人使用它。
如何检索安装目录?
使用注册表项来跟踪您的安装目录,这样您就可以在升级和删除产品时引用它。
使用 WIX,我将在安装目录的 Directy 标记之后创建一个创建密钥的组件,声明
我会使用 MsiGetComponentPath() - 您需要 ProductId 和 ComponentId,但您可以获得已安装文件的完整路径 - 只需选择一个指向安装目录位置的路径即可。如果您想获取任何随机 MSI 的目录值,我不相信有一个 API 可以让您做到这一点。
我会尝试使用 Installer.OpenProduct(productcode)。这将打开一个会话,然后您可以在该会话上请求 Property("TARGETDIR")。
试试这个: var sPath = this.Context.Parameters["assemblypath"].ToString();
正如线程中其他地方所述,我通常在 HKLM 中编写一个注册表项,以便能够轻松检索安装目录以进行后续安装。
如果我正在处理尚未执行此操作的设置,我会使用内置的 Windows Installer 功能 AppSearch: http: //msdn.microsoft.com/en-us/library/aa367578 (v=vs.85 ).aspx通过指定要查找的文件签名来定位先前安装的目录。
文件签名可以由文件名、文件大小和文件版本以及其他文件属性组成。可以以一定程度的灵活性指定每个签名,以便您可以找到同一文件的不同版本,例如通过指定要查找的版本范围。请查看 SDK 文档: http: //msdn.microsoft.com/en-us/library/aa371853 (v=vs.85).aspx
在大多数情况下,我使用主应用程序 EXE 并通过寻找具有正确版本和日期的文件的窄版本范围来设置严格的签名。
最近我需要通过Ketarin自动安装Natural Docs。我可以假设它已安装到默认路径 ( ) 中,但我决定采取一种安全的方法。可悲的是,即使安装程序在 上创建了一个密钥,它也没有任何价值让我找到安装目录。%ProgramFiles(x86)%\Natural Docs
HKLM\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall
Stein 的回答建议使用 AppSearch MSI 功能,它看起来很有趣,但遗憾的是 Natural Docs MSI 安装程序没有为他的方法工作提供签名表。
所以我决定在注册表中搜索任何对 Natural Docs 安装目录的引用,并找到一个 into HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components
key。
我在 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构建它。
可以使用允许注册表项递归且不依赖于 .NET Framework 平台或构建过程的RegClassVBS采取更通用的方法。
请注意,枚举组件密钥的过程可能是 CPU 密集型的。上面的示例有一个 Length 参数,您可以使用它向用户显示一些进度(可能类似于“i from (subKeysName.Length - 1) keys remaining” - 有创意)。在 RegClassVBS 中可以采用类似的方法。
两个类(RegClassCS 和 RegClassVBS)都有可以指导您的文档和示例,您可以在任何软件中使用它并为它们的开发做出贡献,在 git repo 上提交,并且(当然)在它的 github 上打开问题如果您发现任何您无法自行解决的问题,请查看页面,以便我们可以尝试重现该问题以找出我们可以做些什么。=)