2

当我运行允许自定义安装位置/路径的安装程序时,文件将正确放置在我选择的位置。

当我运行相同的 MSI 并选择删除(或从添加/删除程序中卸载)时,它如何知道安装位置以便删除正确的文件?

我以为它将存储在“Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall{GUID}”,但是当我查看已安装软件的该位置时,“InstallLocation”键为空

但是,无论我如何卸载它,它都知道要删除哪个文件夹,无论我把它放在哪里。该信息是存储在注册表中的其他位置,还是存储在 MSI 文件本身中?

4

2 回答 2

3

这是一个非常复杂的问题,因为可以将 MSI 配置为在您告诉它的任何地方删除其卸载文件。通常默认情况下,它会在 C:\Windows\Installer 中创建一个具有特定名称的卸载 .msi 文件。

但不要依赖卸载 .msi 被放置在此目录中,也不要依赖卸载注册表项中存在卸载路径。这个关键与最终用户的便利性一样重要。

卸载信息通常包含在 MSI 文件中,但它不是必须的,并且在安装期间它可以创建密钥以帮助升级和卸载。安装将保留在注册表中的信息完全取决于您如何配置 .msi 数据库。

添加更多内容... Nullsoft、InstallAware 和 InstallShield 等许多安装程序喜欢自己做事并将卸载信息放在其他地方。所以InstallShield 喜欢创建一个InstallShield 安装信息文件夹,而Nullsoft 喜欢创建.dat 文件和uninstall.exe。但除此之外,这些安装程序仍在调用 MSI 并创建安装表和数据库。因此,卸载信息的实际位置并不是一门精确的科学!

于 2020-02-11T16:21:55.370 回答
2

更新:

查找组件的安装位置有没有办法在没有卸载注册表或 C:\Windows\Installer 的情况下检测安装位置?


实施细节:MSI 如何存储这些东西是实施细节,不应干预、尝试修改或直接用于任何目的 - 只是为了清楚起见。您应该通过MSI API,它是作为Win32 函数实现的,带有互补的COM 包装器,以便通过脚本语言进行访问。

注册表: MSI 数据库主要存储在注册表中,但磁盘上也有组件 - 您引用其中的一些 - 例如%SystemDrive%\Windows\Installer(不应以任何方式修改的超级隐藏文件夹)。MSI 数据库存储在注册表中的多个位置:

  • HKCR\Installer
  • HKCU\Software\Microsoft\Installer
  • HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer
  • HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
  • HKLM\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall
  • HKLM\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Installer
  • Etc...

其中一些是真实的,一些是别名,一些是合并。这一切都有些模糊。再说一遍:implementation details-我们大家都知道的委婉说法:“现在就放弃吧,你愿意吗?” :-)。只需应用 MSI API 即可获取您需要的信息。


MSI API:上面有很多东西要阅读以达到重点,通过 MSI API 获取有关目录解析的信息。我们要做的有点奇怪,我们必须为已安装的产品启动一个会话对象并运行两个标准操作(Microsoft 的内置 MSI 操作)以解析 MSI 的目录表和安装目录问题(关于“成本核算”)。下面是一个实际示例:

作为记录:

Set installer = CreateObject("WindowsInstaller.Installer")

' Other test product codes: {2F73A7B2-E50E-39A6-9ABC-EF89E4C62E36}

productcode = Trim(InputBox("Please paste or type in the product code you want to look up details for:", _
              "Find Product Details (test GUID provided):", "{766AD270-A684-43D6-AF9A-74165C9B5796}"))
If search = vbCancel Or Trim(productcode) = "" Then
   WScript.Quit(0)
End If

Set session = installer.OpenProduct(productcode)

' Crucially, resolve the directory table and properties by running "MSI Costing"
session.DoAction("CostInitialize")
session.DoAction("CostFinalize")

' Can be any directory property from the Directory table in the MSI:
MsgBox session.Property("INSTALLFOLDER")

' Open the MSI in Orca to find the directory folder property names

抛出一个关于如何在 MSI 文件中列出表的旧答案的链接。


全部解决:有点得意忘形,并进行了一次更新以解析任何已安装软件包的所有目录。这是一个脚本(没有经过太多测试):

' https://stackoverflow.com/questions/17543132/how-can-i-resolve-msi-paths-in-vbscript
' On Error resume Next

Set installer = CreateObject("WindowsInstaller.Installer")

' Other test product codes: {2F73A7B2-E50E-39A6-9ABC-EF89E4C62E36}

const READONLY = 0
Dim DirList

productcode = Trim(InputBox("Please paste or type in the product code you want to look up details for:", _
              "Find Product Details (test GUID provided):", "{766AD270-A684-43D6-AF9A-74165C9B5796}"))
If search = vbCancel Or Trim(productcode) = "" Then
   WScript.Quit(0)
End If

Set session = installer.OpenProduct(productcode)
session.DoAction("CostInitialize")
session.DoAction("CostFinalize")

set view = session.Database.OpenView("SELECT * FROM Directory")
view.Execute
set record = view.Fetch

Do until record is Nothing
    
    ResolvedDir = session.Property(record.StringData(1))
    DirList = DirList + record.StringData(1) + " => " + ResolvedDir + vbCrLf
    set record = view.Fetch

Loop

' Dismiss dialog with ESC key if it falls off screen 
WScript.Echo DirList ' Use WScript.Echo due to MsgBox restrictions (number of characters)

链接

于 2020-02-15T20:17:14.597 回答