在某些情况下,可能会出现 the need to retrieve MSI upgrade codes for deployed packages .
常见场景:
-
我接管了其他人的MSI项目,我需要确定哪些升级代码用于以前的版本 . 这是处理升级方案所必需的 . 我没有任何版本的存档 .
-
我在开发期间多次意外更改了我的WiX软件包的升级代码,我需要查找所有升级代码版本"in the wild" . 我不知道升级代码应该在版本之间保持稳定 .
这是 Q/A style question .
这个问题在各种形式之前出现过,但是 this is not a duplicate . 我发布了一种使用 main MSI automation interface (或严格来说WMI)的方法 . 它比之前答案的基于注册表的方法更为明确 . 这个答案也试图总结其他检索方法 .
2 回答
MSI升级代码检索(通过PowerShell / WMI)
下面的 PowerShell script 应检索您机器上安装的所有相关 product codes , upgrade codes 和 product names (表格输出) .
output 的屏幕截图(下面的完整脚本):
这些是 real, live values 直接来自机器上的 the Windows Installer database . 无需任何转换或解释 . 我们正在通过适当的API .
Technical note! :请注意,直接在原始MSI文件(属性表)或WiX源文件中检查属性可能与实际安装的值不匹配,因为可以在安装时通过transforms(更多信息如下)覆盖属性 - 或者在命令行 . 故事的寓意:尽可能直接从系统中检索属性值 .
作为一个题外话,还有一个 one-line PowerShell command ,它只会检索产品代码和升级代码 - 不包括包名 . 对于某些用户来说这实际上可能就足够了(不过我建议使用下面的完整脚本) . 在下面的部分中有一个单行输出的屏幕截图 . Note :此命令出现 a lot faster 而不是较大的脚本("Value"字段是升级代码) . 另请注意:没有相关升级代码的产品代码将无法显示 - 我会在较大的脚本中显示:
To run the full PowerShell script below:
Launch PowerShell (按住Windows键,点击R,释放Windows键,输入"powershell"并按OK或按Enter键) .
Copy the script below 完整,然后只是 right click inside the PowerShell window .
这应该启动脚本, it will take quite a while to run .
请报告任何问题 . 我不是PowerShell专家 - 我是部署专家而不是编码员,但脚本应该完成这项工作 .
Performance note :我刚刚得到整个 Win32_Product WMI对象
樱桃采摘属性似乎实际上使它略微变慢(VBScript测试) .
我想我们无论如何都需要获得所有行,樱桃采摘柱只是额外提升?
对于 Win32_Property ,我们过滤行和列(升级代码只是众多行类型中的一种) . 为慢速操作做好准备,WMI非常慢 .
在远程计算机上运行
将上面的脚本扩展为在远程计算机上运行应该相对容易,但我目前还没有设置正确测试它 .
以下信息有点乱,如果不可理解或不清楚,请告诉我 .
在 real Windows domain 中它应该(理论上)只是将远程机器添加到WMI调用本身(并循环遍历机器列表 - 参见下面的模拟) . And crucially: you should use a real domain admin account to run the query . 我可能需要在下面列出的更改以使WMI在工作组环境中工作,但我不知道(防火墙规则和UAC注册表调整) . 我猜想真正的域管理员帐户应该具有所需的权限和访问权限 .
WMI中的远程连接受(至少) Windows Firewall , DCOM settings , CIMOM Settings 和 User Account Control (UAC) (以及任何其他非Microsoft因素 - 例如真正的防火墙,第三方软件防火墙,各种安全软件等...)的影响 . 以下是一些细节:
Setting up a Remote WMI Connection
Connecting to WMI Remotely with PowerShell
在 non-domain networks (小型办公室,家庭等等)中,您可能必须直接向WMI调用添加用户凭据才能使其正常工作 . 并且您可能必须在相关计算机上使用"real admin rights"来使查询在家庭网络(工作组)中远程运行 . 我听说内置管理员帐户没有任何UAC问题,但我从未尝试过 . 在我看来:不要使用这个帐户 .
In my testing 我不得不( 1 )更新Windows防火墙规则和( 2 )禁用远程UAC访问令牌过滤并使用远程系统上的真实本地管理员帐户 . 请注意 I don't recommend either of these changes ,只是报告对我有用的东西 .
Change 1 :Windows防火墙,运行命令(cmd.exe,以管理员身份运行):
netsh advfirewall firewall set rule group="windows management instrumentation (wmi)" new enable=yes
(source - 如果您只是测试,请参阅命令行的此链接以再次禁用此新规则 . 基本上只需设置enable = no) . 请参阅链接的源代码,了解可能更有效的限制性规则 .Change 2 :禁用远程UAC访问令牌过滤:您需要设置以下注册表值:
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\ LocalAccountTokenFilterPolicy = 1
(source - 中间页,后半部分) . 我设置了一个32位的DWORD .通过远程系统上的这些更改,我还通过提示用户
$Cred = Get-Credential
为每个呼叫添加了用户凭据 . 还有更多用于定义用户凭据的高级选项,如下所述:Pass password into -credential(和here) . 要测试运行,这里有一个小测试脚本 . 复制下面的所有行,修改远程计算机名称并通过右键单击粘贴到PowerShell中(系统将提示您输入凭据):对于上面的大型PowerShell脚本,在 Windows domain 中的几台机器上远程运行的基本补充可能是这样的(我赢得了't update the above script since I can' t真正测试了这一点) . 请记住更新脚本顶部的远程计算机名称列表,并使用域管理员帐户运行:
要为 non-domain network 调整相同的机器循环,您可以向WMI调用添加凭据 . 这样的事情(系统会提示您输入每台机器的凭据 - 这可能令人困惑) . 请记住更新脚本顶部的远程计算机名称列表,并在目标框中使用具有本地管理员权限的帐户:
The real answer ends here . 我相信上面的新脚本应该涵盖大多数用例,但是我将保留下面的内容,因为它不是过时的,可能效率低于上面的脚本 . 阅读它可能会重复 .
如果要在运行时从自己的应用程序中检索单个升级代码,则可能会对下面用于检索单个升级代码而不是整个列表的脚本感兴趣 . 我会留下那些旧内容 .
检索未安装的MSI文件的升级代码
如果您需要在您的计算机上使用 not installed 的MSI软件包的升级代码,请阅读底部的“手动检索升级代码”部分以获取多个选项(主要查看MSI文件本身或其用于编译的源文件)它) .
从原始MSI安装文件本身或从用于编译MSI的(WiX)源获取 installed packages 的升级代码是不安全的,因为 upgrade codes can be overridden at install time using transforms (下面的文本中的详细信息 - 转换是在安装时应用的小数据库片段,请参阅Symantec链接了解详情) .
升级代码的编程检索依赖于WMI,您可以使用 PowerShell 或 VBScript 来调用 WMI . 两种方法如下所示 . 基本上运行以下 WMI query 来检索指定产品代码的升级代码:
它与VBScript和PowerShell使用的查询相同 . 您还可以使用诸如
WMIExplorer.exe
之类的工具将其作为直接WMI查询运行 . 一个非常有用的工具 - 强烈推荐 . 我相信这是他们的网站:https://github.com/vinaypamnani/wmie2/releases通过PowerShell / WMI检索单个升级代码
您可以 retrieve a single upgrade code 获取指定的产品代码,而不是输出包含所有产品代码和升级代码的整个表格 . 如果您尝试从自己的应用程序代码中进行检索(这只是一个标准的WMI查询并且与PowerShell无关),这很好 .
下面是通过PowerShell完成的单个升级代码检索(要启动PowerShell:按住Windows键,点击R,释放Windows键,输入"powershell"并按OK或按Enter键):
输出应该是这样的(可能有点难读,我应该使用更大的字体):
上面查询中指定的产品代码适用于“Windows SDK Intellidocs” . 显然,您必须使用自己的产品代码guid替换它 . 要查找需要传入的产品代码,还可以使用PowerShell查询,如下所述: How can I find the product GUID of an installed MSI setup?
The returned upgrade code is coming straight from the real Windows Installer registry database. It requires no further processing or interpretation or manual conversion steps . 它也是正确的,即使转换改变了原始升级代码时也是如此已安装MSI(有关转换问题的详细信息如下) .
Update, special notice :没有不必要的复杂化,我相信我发现WMI中的一个非常具体的错误 . 当原始MSI没有设置升级代码,并且您通过转换添加一个时,WMI似乎根本不报告升级代码 . 但是:如果原始MSI具有升级代码,并且您在转换中覆盖它,则WMI会报告转换的升级代码(这是预期的) . 我肯定看到了这一点,但需要确认一个更多的测试包 . The moral of the story :始终在MSI中设置升级代码!然后你永久地避免整个问题 . 并且不要自动生成它 - 硬编码(请参阅下面的"Manual Retrieval of Upgrade Codes"以获得解释) .
使用VBScript / WMI检索单个升级代码(旧版方法)
There is nothing wrong with the VBScript solution found below - it even has some benefits over PowerShell - 尽管VBScript现在已成为传统技术 . 好处是它应该适用于所有机器,即使.NET框架丢失(或锁定),也适用于PowerShell丢失(或锁定)的机器 . 这是一个过时但可行的解决方案,非常灵活(除非VBScript也被锁定,但所有现代操作系统版本都完全支持VBScript) .
为了尽可能简单地检索升级代码,我创建了一个“ bare-bone VBScript ”,它应该可以解决问题 . 它尚未针对远程计算机进行测试,即使WMI应该能够通过设计实现这一目标 . 该脚本旨在在安装了具有未知升级代码的神秘MSI的系统上运行 .
此VBScript需要输入产品代码(运行脚本时显示输入对话框),然后它将继续查找相应的升级代码(如果有) . 如上所述,要找到MSI的产品代码,您可以使用以下方法: How can I find the product GUID of an installed MSI setup? . 获得产品代码(guid)后,您可以在目标计算机上运行此VBScript,并且您应该在几秒钟内收到返回的升级代码 . WMI检索可能非常慢 .
检索计算机上的所有升级代码和产品代码
有一个 one-line PowerShell command 来检索所有产品代码和相关的升级代码,但此输出填充缺少产品的名称 . 我把它包括在这里是为了完整性:
输出将与此类似(“值”字段是升级代码 - 没有相关升级代码的产品代码将无法显示到我所知的范围内):
手动检索升级代码
本节列出了一些"manual ways"来检索不需要任何编码或命令行的升级代码 . 这些手动方法是 not 推荐的方法 . 我只包括它们,因为它试图成为“ reference answer ” . 应提供几种不同的选择 . My recommendation 是使用上面提供的PowerShell或VBScript .
话虽这么说,升级代码通常不会在产品版本之间发生变化,因此您可以尝试在MSI文件本身中找到的版本,或者用于编译它的源代码,如下所述 . 已经多次提到的问题是转换可以在安装时更改升级代码,因此如果要确保找到正确的升级代码,则需要以编程方式检索升级代码 . 除非您尝试从未安装在系统上的MSI获取升级代码 . 然后你只需要一个MSI文件查看器,如下面的项目符号1所述 .
transform 只是 database fragment ,其中包含在安装时应用于原始MSI的更改 . 它是一种主要用于 corporate application packaging 的工具,用于修改安装程序而无需直接修改MSI文件 . 变换的扩展名为
.mst
. 通过转换更改升级代码是不寻常的,但并非闻所未闻 - 特别是对于企业重新打包 . 在 rare cases 中,应用程序打包者可能会故意更改升级guid,以使他们能够将自己的升级交付给已安装的软件包(而不是直接依赖供应商更新) . 很少见,但我已经看到了 . 这是否是一件好事是值得商榷的 .轻松, manual ways 查找MSI升级代码:
虽然进攻性很明显,但找到升级代码的最简单方法是 open the original MSI 安装产品和 find the upgrade code in the Property table . 您只需要一个能够打开MSI文件的工具 . 以下是一些工具:What installation product to use? InstallShield, WiX, Wise, Advanced Installer, etc . 你最快的赌注可能是Orca,如果你安装了Visual Studio(搜索
Orca-x86_en-us.msi
并安装它 - 这是Microsoft 's own, official MSI viewer and editor), or Super Orca if you don' t安装了Visual Studio(按照上面的链接找到它) .如果您是使用WiX(或任何其他部署工具)的开发人员,您显然可以 find the upgrade code easily in your WiX source file 用于编译MSI(或Installshield源,高级安装程序源或您正在使用的任何部署工具) .
让我们不要离开这里的句柄太多意味着混乱主要问题的建议,但你显然应该在你的来源 hard code the upgrade code , never auto-generate it !
升级代码定义“ families of related products ”,并且应在各版本(版本)之间保持稳定 . 在大多数情况下,它也应该在语言版本中保持稳定 . 确切的设置取决于部署要求 .
如果产品应该能够并排存在,那么对于需要共存的产品,通常会有不同的升级代码 .
Rule of thumb :尽可能保持升级代码尽可能长时间稳定 . 在需求绝对需要时更改它们 .
要总结:永远不要对具有自己的“ life cycle " and no real relation to each other. They are not related. This is just as important as keeping your upgrade code stable for related products. Think " life cycle " and " family relation " and " co-existence ”要求的不同产品使用相同的升级代码 .
这是一个很大的题外话,回到手头的问题:找到升级代码 .
即使您没有原始MSI,也可以在
%SystemRoot%\Installer
文件夹中找到原始安装中的 cached MSI . 这里的MSI文件有一个神秘的十六进制名称,但它们只是用于安装不同产品的原始MSI文件的副本 - 缓存在安全的地方,可用于修改,修复和卸载操作 . Whatever you do, don't mess around in this folder. Never, ever delete anything . 您可以通过选择第一个MSI文件找到安装了产品的MSI,并在Windows资源管理器状态栏中查找旧Windows版本的产品名称 . 在Windows 10中,您似乎可以使用指针悬停在MSI上,然后弹出一些MSI详细信息 . 然后,只需单击列表,直到找到正确的产品并打开MSI并在 Property table 中找到升级代码 .有些人使用注册表来阅读升级代码:How can I find the upgrade code for an installed application in C#? . 在我看来,这不是一个好方法,有更好的方法 - 例如如上所述仅使用PowerShell . 不需要对packed GUIDs(这是Windows Installer注册表数据库中使用的GUID格式)进行所有这些转换和解释 .
这应该完成主要的“手动方法”,以快速检索升级代码 . 只是一些有时足够好的武器库的方法 . 我可能还有几种方法可以忘记 .
Do prefer the programmatic approaches ,但是如果您在没有所有工具的情况下匆忙工作,一些手动选项都很好 . 但是,这些手动方法中的一些需要比PowerShell命令行更多的工具(如果您在某人的机器上"support mission",则需要一个MSI文件查看器,该查看器并不总是可用) . 现在是使用PowerShell的时候了(是的,我觉得过时了) .
顺便提一下,MSI文件基本上是剥离存储为COM结构存储文件(MS Office文件格式)的SQL Server数据库 . 本质上是文件中的文件系统,具有各种类型的存储流 .
如果您遇到没有MSI查看器的计算机,则可以直接从PowerShell查询缓存的MSI数据库:
https://gallery.technet.microsoft.com/scriptcenter/Get-MsiDatabaseProperties-09d9c87c
http://www.adamtheautomator.com/powershell-windows-installer-msi-properties/
为了满足直接使用WMI的要求,或者只需要一次性而不使用Powershell(或者需要使用.bat或其他),请使用wmic:
有多种格式和输出选项 .