为了账号安全,请及时绑定邮箱和手机立即绑定

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

/ 猿问

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

动漫人物 2019-10-21 11:21:49

在某些情况下,可能需要检索已部署软件包的MSI升级代码

常见方案:

  • 我接管了其他人的MSI项目,并且需要确定已在狂野使用的以前版本使用了哪些升级代码。这是处理升级方案所必需的。我没有任何地方的发行档案

  • 在开发过程中,我无意间更改了WiX软件包的升级代码,并且需要“狂野地”找到所有升级代码版本。我不知道版本之间的升级代码应该保持稳定


这是一个Q / A风格的问题

这个问题曾以各种形式出现过,但这不是重复的。我正在发布一种使用MSI主自动化接口(或严格来说是WMI)的方法。它应该比以前答案中基于注册表的方法更可靠。该答案还试图总结其他检索方法。


查看完整描述

2 回答

?
慕容森

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

解除安装?:Via Upgrade Code, Via Product Code, Via Product Name, etc...


下面的PowerShell脚本应检索计算机上安装的所有相关产品代码,升级代码和产品名称(表输出)。


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


Powershell输出


这些是直接在有关计算机上的Windows Installer数据库中的真实实时值。无需任何转换或解释。我们正在通过适当的API。


技术说明!注意:请注意,直接在原始MSI文件(属性表)或WiX源文件中检查属性可能与实际安装的值不匹配,因为可以在安装时通过转换(在下面的详细信息)或命令中指定的属性值来覆盖属性线。故事的寓意是:尽可能直接从系统检索属性值。


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


作为题外话,还有一个单行的PowerShell命令,该命令将仅检索产品代码和升级代码-不包括程序包名称。对于某些用户,这实际上可能就足够了(不过,我建议使用下面的完整脚本)。下面的部分中有此单线输出的屏幕截图。注意:此命令的显示速度比较大的脚本快得多(“值”字段是升级代码)。另请注意:据我所知,没有关联升级代码的产品代码将不会显示-它们将在较大的脚本中显示:


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

要在下面运行完整的PowerShell脚本:


启动PowerShell(按住Windows键,点按R,释放Windows键,键入“ powershell”,然后按OK或按Enter键)。

完整复制下面的脚本,然后在PowerShell窗口内右键单击。

这应该启动脚本,并且要花相当长的时间才能运行。

请报告任何问题。我不是PowerShell专家-我不是部署专家,也不是编码人员,但是脚本应该可以完成工作。

性能说明:我只得到整个Win32_Product WMI对象

Cherry采摘属性似乎实际上使它变慢了一些(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

在远程计算机上运行

扩展上面的脚本以在远程计算机上运行应该相对容易,但是目前我尚未对其进行正确的测试。

下面的信息有些混乱,如果无法理解或不清楚,请告诉我。

在实际的Windows域中,(理论上)应该只是将远程计算机添加到WMI调用自身中(并遍历计算机列表-请参见下面的模型)。至关重要的是:您应该使用真实的域管理员帐户来运行查询。我不知道,我下面列出的使WMI在工作组环境中工作的更改对于某些域也可能是必需的(防火墙规则和UAC注册表调整)。我想一个真实的域管理员帐户应该具有所需的特权和访问权限。

WMI中的远程连接受(至少)Windows防火墙,DCOM设置,CIMOM设置和用户帐户控制(UAC)(以及任何其他非Microsoft因素)影响,例如,真实防火墙,第三方软件防火墙,各种安全软件种类等)。以下是一些详细信息:

设置远程WMI连接

使用PowerShell远程连接到WMI

在非域网络(小型办公室,家庭等)中,您可能必须直接将用户凭据添加到WMI调用中才能使其工作。并且您可能必须在所讨论的计算机上具有“实际管理员权限”,才能使查询在家庭网络(工作组)中远程运行。我听说内置的Administrator帐户没有任何UAC问题,但我从未尝试过。我认为:请勿使用此帐户。

在测试中,我必须(1)更新Windows防火墙规则,以及(2)禁用远程UAC访问令牌筛选,并在远程系统上使用真实的本地管理员帐户。请注意,我不建议您进行任何更改,而只是报告对我有用的更改。

变化1:Windows防火墙,运行命令(CMD.EXE,以管理员身份运行): netsh advfirewall firewall set rule group="windows management instrumentation (wmi)" new enable=yes(源 -看到这个链接命令来再次禁用此新规则,如果你只是测试本质上只是设置启用=否)。请参阅链接的源,以获得可能也适用的限制性更强的规则。

变化2:禁用远程UAC访问令牌筛选:您需要设置以下注册表值:HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\ LocalAccountTokenFilterPolicy = 1(源 -网页中期,后半)。我设置了一个32位DWORD。

在远程系统上进行了这些更改之后,我还通过提示用户来向每个呼叫添加用户凭证$Cred = Get-Credential。还有一些用于定义用户凭据的更高级的选项,如此处所述:将密码传递到-credential(和此处)。为了测试运行,这里有一些测试脚本。复制下面的所有行,修改远程计算机名称,然后通过右键单击将粘贴到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 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)

}

要使同一机器环路适用于非域网络,可以将凭据添加到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) 

}

真正的答案到此结束。我相信上述较新的脚本应能涵盖大多数用例,但由于内容还不是过时的,因此我将其内容也保留在下面,只是效率可能不如上述脚本。阅读它可能是重复的。


如果要在运行时从自己的应用程序中检索单个升级代码,则下面用于检索单个升级代码而不是整个列表的脚本可能会很有用。我将保留较旧的内容。


免责声明:上面的脚本使用WMI,并且当您访问类Win32_Product时,它将触发已安装软件包的 完整性检查。这非常慢,并且在非常特殊的情况下会触发MSI自修复。如果您要参加重要的会议,那就不好了:-)。幸运的是,您应该能够取消所有触发的自我修复(但是,在您完成修复之前,您的查询可能不会完成)。快速上下文链接(用于保管)。


恕我直言:不要让这阻止您使用WMI-这只是一个烦人。注意:下面介绍的PowerShell和VBScript方法都使用WMI,也可以触发此问题。


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

如果您需要计算机上未安装的MSI软件包的升级代码,请阅读底部的“ 手动获取升级代码 ”部分,以获取多个选项(本质上是查看MSI文件本身或用于编译)。


从原始MSI安装文件本身或用于编译MSI的(WiX)源获取已安装软件包的升级代码是不安全的,因为可以在安装时使用转换覆盖升级代码(以下文本中的详细信息-转换在安装时应用的数据库碎片很少,有关详细信息,请参见Symantec链接。


升级代码的编程检索依赖于WMI,并且可以使用PowerShell或VBScript调用WMI。两种方法都在下面介绍。本质上,运行以下WMI查询以检索指定产品代码的升级代码:


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

这是用于VBScript和PowerShell的同一查询。您也可以使用诸如之类的工具将其作为直接WMI查询运行WMIExplorer.exe。一个非常有用的工具-强烈推荐。我相信这是他们的网站:https://github.com/vinaypamnani/wmie2/releases


通过PowerShell / WMI检索单个升级代码

您无需输出包含所有产品代码和升级代码的整个表格,而是可以检索指定产品代码的单个升级代码。如果您尝试从自己的应用程序代码内部进行检索,那么这很好(然后,这只是一个标准的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

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


使用PowerShell检索升级代码-带注释


在上面的查询中指定的产品代码用于“ Windows SDK Intellidocs ”。您显然必须用自己的产品代码guid替换它。要查找您需要传递的产品代码,您还可以按以下说明使用PowerShell查询:如何找到已安装的MSI设置的产品GUID?


返回的升级代码直接来自真实的Windows Installer注册表数据库。它不需要进一步的处理或解释或手动转换步骤。即使在安装MSI时转换更改了原始升级代码,它也将是正确的(有关以下转换问题的详细信息)。


更新,特别注意:我相信我在WMI中发现了一个非常具体的错误,而无需将事情复杂化。如果原始MSI没有设置升级代码,而您通过转换添加了一个代码,则WMI似乎根本不会报告升级代码。但是:如果原始MSI具有升级代码,并且您在转换中覆盖了它,则WMI将报告转换的升级代码(这是预期的)。我确实看到了这一点,但需要确定再加上一个测试包。故事的寓意:始终在MSI中设置升级代码!然后,您可以永久避免整个问题。并且不要自动生成-对其进行硬编码(请参阅下面的“手动获取升级代码”以获取说明)。


使用VBScript / WMI(传统方法)检索单个升级代码

下面找到的VBScript解决方案没有任何问题-它甚至比PowerShell具有一些优势 -尽管目前VBScript是一种旧技术。好处是,即使.NET Framework丢失(或锁定),它也可以在所有计算机上运行,并且PowerShell(丢失)(或锁定)的机器也可以运行。这是一个过时但可行的解决方案,非常灵活(除非也锁定了VBScript,但是所有现代OS版本都完全支持VBScript)。


为了使检索升级代码变得尽可能简单,我创建了一个“ 准VBScript ”,可以解决问题。即使WMI应该能够设计出针对远程计算机的功能,也尚未经过测试。该脚本旨在在安装了未知升级代码的神秘MSI的系统上运行。


此VBScript需要输入产品代码(运行脚本时显示输入对话框),然后它将继续查找相应的升级代码(如果有)。如上所述,要找到您的MSI的产品代码,可以使用以下方法:如何找到已安装的MSI设置的产品GUID?。获得产品代码(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文件输出到“我的文档”外,它是只读的。 也应该可以将此脚本改编为在远程计算机上使用。


有一个单行PowerShell命令可检索所有产品代码和相关的升级代码,但是此输出填充缺少产品名称。为了完整起见,我将其包括在此处:


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

输出将与此类似(“值”字段是升级代码-据我所知,没有关联升级代码的产品代码将不会显示):


输出所有升级代码和产品代码


手动获取升级代码

本节列出了一些“手动方式”来检索不需要任何编码或命令行的升级代码。不建议使用这些手动方法。我之所以将它们包括在内,是因为这试图成为“ 参考答案 ”。应该提供几个不同的选项。我的建议是使用上面提供的PowerShell或VBScript。


话虽如此,升级代码通常不应在产品的各个版本之间发生变化,因此您可以尝试在MSI文件本身或如下所述的用于编译该文件的源代码中找到升级代码。已经多次提到的问题是,转换可以在安装时更改升级代码,因此,如果要确保找到正确的升级代码,则需要以编程方式检索升级代码。除非您尝试从系统上未安装的MSI获取升级代码。然后,您只需要一个MSI文件查看器,如下文要点1所述。


一个变换只是一个数据库碎片与被安装时应用于原始MSI的变化。它是主要用于公司应用程序打包的工具,用于修改安装程序而无需直接修改MSI文件。转换具有扩展名.mst。通过转换更改升级代码是不常见的,但并非闻所未闻-特别是对于公司重新包装。在极少数情况下,应用程序打包人员可能会故意更改升级指南,以使他们能够将自己的升级交付给已安装的软件包(而不是直接依赖于供应商更新)。很少,但我已经看到了。这是好事还是有争议的。


简单,手动的方法来查找MSI升级代码:


尽管很明显,但查找升级代码的最简单方法是打开用于安装产品的原始MSI,然后在“属性”表中找到升级代码。您所需要的只是一个能够打开MSI文件的工具。这里有一些工具:使用什么安装产品?InstallShield,WiX,Wise,Advanced Installer等。如果您安装了Visual Studio(搜索Orca-x86_en-us.msi并安装它-这是Microsoft自己的官方MSI查看器和编辑器),则最快的选择可能是Orca ,或者如果您未安装Visual Studio,则最好选择Super Orca(请按照上面的链接查找)它)。


如果您是使用WiX(或任何其他部署工具)的开发人员,则显然可以在用于编译MSI的WiX源文件(或Installshield源,Advanced Installer源或任何您使用的部署工具)中轻松找到升级代码。使用)。


在这里,我们不要提供过多的意味深长的建议来杂乱主要问题,但显然您应该在源代码中对升级代码进行硬编码,并且不要自动生成!

升级代码定义“ 相关产品系列 ”,并且应在各个发行版(版本)中保持稳定。在大多数情况下,它在所有语言版本中也应保持稳定。确切的设置取决于部署要求。

如果产品应该能够并存,那么对于需要共存的产品,通常会有不同的升级代码。

经验法则:尽可能使升级代码保持稳定。在绝对需要时更改它们。

总结一下:对于具有自己的“ 生命周期 ”并且彼此之间没有真正关系的不同产品,切勿使用相同的升级代码。它们没有关系。这与保持相关产品的升级代码稳定一样重要。考虑“ 生命周期 ”和“ 家庭关系 ”和“ 共存 ”的要求。

这是一个很大的背离,回到当前的问题:查找升级代码。

即使您没有原始MSI,也可以从文件夹中的原始安装中找到缓存的MSI%SystemRoot%\Installer。这里的MSI文件带有一个神秘的十六进制名称,但它们只是用于安装不同产品的原始MSI文件的副本-缓存在安全的地方,可用于修改,修复和卸载操作。无论您做什么,都不要在该文件夹中乱扔东西。永远不要删除任何东西。您可以通过选择第一个MSI文件并检查Windows资源管理器状态栏,找到旧版本Windows的产品名称,来找到安装产品的MSI。在Windows 10中,看来您可以将鼠标悬停在MSI上,然后弹出带有一些MSI详细信息的弹出窗口。然后,您只需单击列表,直到找到合适的产品,然后打开MSI,并在“ 属性”表中找到升级代码。


有人使用注册表读取升级代码:如何在C#中找到已安装应用程序的升级代码?。我认为这不是一个好方法,有更好的方法-例如仅使用上面解释的PowerShell。不需要对打包的GUID(Windows Installer注册表数据库中使用的GUID格式)进行所有这些转换和解释。


那应该完成主要的“手动方法”以快速获取升级代码。只是一些武器库的方法有时足够好。我可能已经忘记了几种其他方式。


不要偏爱编程方法,但是如果您急于在没有所有可用工具的情况下工作,则可以使用一些手动选项。但是,其中一些手动方法比PowerShell命令行需要更多的工具(您需要一个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/


查看完整回答
反对 回复 2019-10-21
?
BIG阳

为了满足直接使用WMI的要求,或者在那些时候,您只需要一次性使用PowerMI(或者需要使用.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    

有多种格式和输出选项。


查看完整回答
反对 回复 2019-10-21

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信