首页 文章

如何找到已安装的MSI文件的升级代码?

提问于
浏览
12

在某些情况下,可能会出现 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 回答

  • 23

    MSI升级代码检索(通过PowerShell / WMI)

    卸载?:通过升级代码,通过产品代码,通过产品名称等...

    下面的 PowerShell script 应检索您机器上安装的所有相关 product codesupgrade codesproduct names (表格输出) .

    output 的屏幕截图(下面的完整脚本):

    powershell output

    这些是 real, live values 直接来自机器上的 the Windows Installer database . 无需任何转换或解释 . 我们正在通过适当的API .

    Technical note! :请注意,直接在原始MSI文件(属性表)或WiX源文件中检查属性可能与实际安装的值不匹配,因为可以在安装时通过transforms(更多信息如下)覆盖属性 - 或者在命令行 . 故事的寓意:尽可能直接从系统中检索属性值 .

    快速免责声明:在极少数情况下,运行脚本可以触发Windows Installer自我修复 . 阅读下面“免责声明部分”中的更多内容 . 只是一个潜在的麻烦,但请阅读免责声明 .

    作为一个题外话,还有一个 one-line PowerShell command ,它只会检索产品代码和升级代码 - 不包括包名 . 对于某些用户来说这实际上可能就足够了(不过我建议使用下面的完整脚本) . 在下面的部分中有一个单行输出的屏幕截图 . Note :此命令出现 a lot faster 而不是较大的脚本("Value"字段是升级代码) . 另请注意:没有相关升级代码的产品代码将无法显示 - 我会在较大的脚本中显示:

    gwmi -Query "SELECT ProductCode,Value FROM Win32_Property WHERE Property='UpgradeCode'" | Format-Table ProductCode,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非常慢 .

    $wmipackages = Get-WmiObject -Class win32_product
    $wmiproperties = gwmi -Query "SELECT ProductCode,Value FROM Win32_Property WHERE Property='UpgradeCode'"
    $packageinfo = New-Object System.Data.Datatable
    [void]$packageinfo.Columns.Add("Name")
    [void]$packageinfo.Columns.Add("ProductCode")
    [void]$packageinfo.Columns.Add("UpgradeCode")
    
    foreach ($package in $wmipackages) 
    {
        $foundupgradecode = $false # Assume no upgrade code is found
    
        foreach ($property in $wmiproperties) {
    
            if ($package.IdentifyingNumber -eq $property.ProductCode) {
               [void]$packageinfo.Rows.Add($package.Name,$package.IdentifyingNumber, $property.Value)
               $foundupgradecode = $true
               break
            }
        }
    
        if(-Not ($foundupgradecode)) { 
             # No upgrade code found, add product code to list
             [void]$packageinfo.Rows.Add($package.Name,$package.IdentifyingNumber, "") 
        }
    
    }
    
    $packageinfo | Format-table ProductCode, UpgradeCode, Name
    
    # Enable the following line to export to CSV (good for annotation). Set full path in quotes
    # $packageinfo | Export-Csv "[YourFullWriteablePath]\MsiInfo.csv"
    
    # copy this line as well
    

    在远程计算机上运行

    • 将上面的脚本扩展为在远程计算机上运行应该相对容易,但我目前还没有设置正确测试它 .

    • 以下信息有点乱,如果不可理解或不清楚,请告诉我 .

    • real Windows domain 中它应该(理论上)只是将远程机器添加到WMI调用本身(并循环遍历机器列表 - 参见下面的模拟) . And crucially: you should use a real domain admin account to run the query . 我可能需要在下面列出的更改以使WMI在工作组环境中工作,但我不知道(防火墙规则和UAC注册表调整) . 我猜想真正的域管理员帐户应该具有所需的权限和访问权限 .

    • WMI中的远程连接受(至少) Windows FirewallDCOM settingsCIMOM SettingsUser 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=yessource - 如果您只是测试,请参阅命令行的此链接以再次禁用此新规则 . 基本上只需设置enable = no) . 请参阅链接的源代码,了解可能更有效的限制性规则 .

    • Change 2 :禁用远程UAC访问令牌过滤:您需要设置以下注册表值: HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\ LocalAccountTokenFilterPolicy = 1source - 中间页,后半部分) . 我设置了一个32位的DWORD .

    通过远程系统上的这些更改,我还通过提示用户 $Cred = Get-Credential 为每个呼叫添加了用户凭据 . 还有更多用于定义用户凭据的高级选项,如下所述:Pass password into -credential(和here) . 要测试运行,这里有一个小测试脚本 . 复制下面的所有行,修改远程计算机名称并通过右键单击粘贴到PowerShell中(系统将提示您输入凭据):

    $Cred = Get-Credential
    gwmi -ComputerName RemoteMachineName -credential $Cred -Query "SELECT ProductCode,Value FROM Win32_Property WHERE Property='UpgradeCode'" | Format-Table ProductCode,Value
    # copy this line too
    

    对于上面的大型PowerShell脚本,在 Windows domain 中的几台机器上远程运行的基本补充可能是这样的(我赢得了't update the above script since I can' t真正测试了这一点) . 请记住更新脚本顶部的远程计算机名称列表,并使用域管理员帐户运行:

    # DOMAIN NETWORK: mock-up / pseudo snippet ONLY - lacks testing, provided "as is"
    $ArrComputers = "Computer1", "Computer2", "Computer3"
    foreach ($Computer in $ArrComputers) 
    {
        # here we modify the WMI calls to add machine name
        $wmipackages = Get-WmiObject -Class win32_product -ComputerName $Computer
        $wmiproperties = gwmi  -ComputerName $Computer -Query "SELECT ProductCode,Value FROM Win32_Property WHERE Property='UpgradeCode'"
    
        # the rest of the above, large script here (minus the first 2 WMI lines)
    }
    

    要为 non-domain network 调整相同的机器循环,您可以向WMI调用添加凭据 . 这样的事情(系统会提示您输入每台机器的凭据 - 这可能令人困惑) . 请记住更新脚本顶部的远程计算机名称列表,并在目标框中使用具有本地管理员权限的帐户:

    # WORKGROUP NETWORK: mock-up / pseudo snippet ONLY - lacks testing, provided "as is"
    $ArrComputers = "Computer1", "Computer2", "Computer3"
    foreach ($Computer in $ArrComputers) 
    {
         $Cred = Get-Credential
    
         # here we modify the WMI calls to add machine name AND credentials
         $wmipackages = Get-WmiObject -Class win32_product -ComputerName $Computer -credential $cred
         $wmiproperties = gwmi  -ComputerName $Computer -credential $cred -Query "SELECT ProductCode,Value FROM Win32_Property WHERE Property='UpgradeCode'"
    
         # the rest of the above, large script here (minus the first 2 WMI lines) 
    }
    

    The real answer ends here . 我相信上面的新脚本应该涵盖大多数用例,但是我将保留下面的内容,因为它不是过时的,可能效率低于上面的脚本 . 阅读它可能会重复 .

    如果要在运行时从自己的应用程序中检索单个升级代码,则可能会对下面用于检索单个升级代码而不是整个列表的脚本感兴趣 . 我会留下那些旧内容 .

    免责声明:上述脚本使用WMI,当您访问Win32_Product类时,它会触发已安装软件包的完整性检查 . 这非常慢,并且可以在非常特殊的情况下触发MSI自修复 . 如果你要参加一个重要的 Session ,那就不好了:-) . 幸运的是,您应该可以取消任何触发的自我修复(但是在您完成修复之前,您的查询可能无法完成) . 快速上下文链接(用于保管) . 恕我直言:不要让这阻止你使用WMI - 这只是一个烦恼 . 注意:下面描述的PowerShell和VBScript方法都使用WMI,也可以触发此问题 .


    检索未安装的MSI文件的升级代码

    如果您需要在您的计算机上使用 not installed 的MSI软件包的升级代码,请阅读底部的“手动检索升级代码”部分以获取多个选项(主要查看MSI文件本身或其用于编译的源文件)它) .

    从原始MSI安装文件本身或从用于编译MSI的(WiX)源获取 installed packages 的升级代码是不安全的,因为 upgrade codes can be overridden at install time using transforms (下面的文本中的详细信息 - 转换是在安装时应用的小数据库片段,请参阅Symantec链接了解详情) .

    升级代码的编程检索依赖于WMI,您可以使用 PowerShellVBScript 来调用 WMI . 两种方法如下所示 . 基本上运行以下 WMI query 来检索指定产品代码的升级代码:

    SELECT * FROM Win32_Property WHERE Property='UpgradeCode' AND ProductCode='{YourProdGuid}'
    

    它与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键):

    gwmi -Query "SELECT Value FROM Win32_Property WHERE Property='UpgradeCode' AND ProductCode='{YourGuid}'" | Format-Table Value
    

    输出应该是这样的(可能有点难读,我应该使用更大的字体):

    Retrieving upgrade code using PowerShell - annotated

    上面查询中指定的产品代码适用于“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检索可能非常慢 .

    '
    ' Purpose: Barebone / minimal VBScript implementation to allow retrieval of MSI UpgradeCodes via WMI.
    '
    ' Version: 0.2, September.2017 - Stein Åsmul.
    '
    ' Notes:
    '
    '  - As it stands, this script is intended to be run interactively (WScript).
    '  - Conversion to run via CScript should be trivial (nothing ever is...)
    '  - The script will ask the user to provide a valid product GUID for an installed MSI.
    '  - To find a valid product GUID for your system, perhaps see this SO answer: https://stackoverflow.com/a/29937569/129130
    '  - The script does not RegEx anything to check for valid GUID format (this is barebone - as terse as possible,
    '    with as little as possible included that can break).
    '
    ' UPDATE: for information on remote running, check "Running on remote machines" section here:
    ' https://stackoverflow.com/a/46637095/129130 (firewall and registry change seems to be needed).
    
    strComputer = "."
    ' Remote connections was NOT tested for this script. In principle you should just add the machine name to "strComputer" above.
    ' AFAIK you must have "real" admin rights on the box you try to connect to. Many users report intermittent problems running remote WMI.
    ' Remote connections in WMI are affected by the Windows Firewall, DCOM settings, and User Account Control (UAC).
    '    - Setting up a Remote WMI Connection: https://msdn.microsoft.com/en-us/library/aa822854(v=vs.85).aspx
    '    - Connecting to WMI on a Remote Computer: https://msdn.microsoft.com/en-us/library/aa389290(v=vs.85).aspx
    '    - Perhaps useful: https://social.technet.microsoft.com/Forums/lync/en-US/05205b52-0e43-4ce3-a8b8-58ec4c2edea5/wmi-generic-failure-when-accessing-win32product-remotely?forum=winserverManagement
    '    - Maybe it is also worth noting that I think WMI queries can be slow enough to trigger timeouts,
    '      and then you have the old favorite: intermittent bugs.
    
    Set owmi = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
    
    ' User interaction
    productcode = InputBox("Please paste or type in the product code for the product whose upgrade code you want " + _
                           "to retrieve (not case sensitive, a blank product code will abort the script)." + vbNewLine + vbNewLine + _
                           "Please note that the script can take up to a minute to run due to WMI's slowness.", "UpgradeCode retrieval:")
    If productcode = vbCancel Or Trim(productcode) = "" Then
       WScript.Quit(0)
    End If
    
    ' Run WMI call and verify that it completes successfully.
    On Error Resume Next
    Set upgradecode = owmi.ExecQuery("SELECT Value FROM Win32_Property WHERE Property='UpgradeCode' AND ProductCode='" & productcode & "'")
    If (Err.number <> 0) Then
       MsgBox "The WMI query failed, this is a critical error - aborting.", vbCritical, "Fatal error."
       WScript.Quit(2) ' Following exit code "standard" from MSI SDK automation samples
    End If
    On Error GoTo 0
    
    ' Report results.
    Select Case upgradecode.count
    
       Case 0
           ' We have to provide a separate message for this state, since some packages may not have an UpgradeCode.
           ' However, the product GUID could also have been misspelled.
           MsgBox "No UpgradeCode was found, are you sure you entered the correct product GUID?" & vbNewLine & vbNewLine & _
                  "Note: It is possible for a product to NOT have an UpgradeCode.", vbInformation, "No UpgradeCode found."
    
       Case 1
          ' The "default state" - should cover almost all normal packages.
    
          ' Only one upgrade code should have been retrieved, and it can be referenced by upgradecode.ItemIndex(0).Value on newer systems 
          ' (Vista and later), but on XP this apparently does not work (never tested by me), for compatibility we use a standard For Each 
          ' enumeration instead. Source: https://stackoverflow.com/questions/2378723/get-first-record-from-wmi-execquery
    
          For Each u in upgradecode
            Msgbox "The Upgrade Code is: " & u.Value & vbNewLine & vbNewLine & _
                  "Just press CTRL + C to copy all text in this dialog (then paste to notepad or similar to extract the GUID).", _
                  vbInformation, "UpgradeCode found."
              ' Exit For
          Next
    
       Case Else
           ' Should never get here - let us know if you do get this message.
           MsgBox "An error occurred, the query returned more than one result. There can only be one UpgradeCode. " & _ 
                  "Please report this error on StackOverflow", vbInformation, "Error while retrieving UpgradeCode."
    End Select
    

    检索计算机上的所有升级代码和产品代码

    我应该提一下,我有一个大的VBScript,它将为运行它的机器上的所有已安装的MSI包生成一个全面的HTML报告 . 这包括所有升级代码和相关产品代码列表(共享相同升级代码的产品代码) . 但是,我对代码不太满意(我是部署专家,而不是编码员) . 脚本太大,太慢而且未经测试使用,因此我创建了上面找到的裸骨VBScript,仅对单个包进行检索 . 此脚本更易于测试和修改以供您自己使用 . 如果感兴趣,我可以提供这个大型VBScript进行测试 . 除了将单个HTML文件输出到“我的文档”之外,它是只读的 . 应该可以调整此脚本以便在远程计算机上使用 .

    有一个 one-line PowerShell command 来检索所有产品代码和相关的升级代码,但此输出填充缺少产品的名称 . 我把它包括在这里是为了完整性:

    gwmi -Query "SELECT ProductCode,Value FROM Win32_Property WHERE Property='UpgradeCode'" | Format-Table ProductCode,Value
    

    输出将与此类似(“值”字段是升级代码 - 没有相关升级代码的产品代码将无法显示到我所知的范围内):

    output of all upgrade codes and product codes


    手动检索升级代码

    本节列出了一些"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 codenever 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数据库:

  • 0

    为了满足直接使用WMI的要求,或者只需要一次性而不使用Powershell(或者需要使用.bat或其他),请使用wmic:

    C:\WINDOWS\system32>wmic product list brief
    Caption                                                                                              IdentifyingNumber                       Name                                                                                                 Vendor                          Version
        Sourcetree                                                                                           {1B05DFFD-1DB9-48CD-9265-F3976512A579}  Sourcetree                                                                                           Atlassian                       2.6.10.0
        Microsoft Office Access database engine 2007 (English)                                               {90120000-00D1-0409-0000-0000000FF1CE}  Microsoft Office Access database engine 2007 (English)                                               Microsoft Corporation           12.0.4518.1031
        Office 16 Click-to-Run Extensibility Component                                                       {90160000-008C-0000-0000-0000000FF1CE}  Office 16 Click-to-Run Extensibility Component
    

    有多种格式和输出选项 .

相关问题