欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 手游 > 使用Chocolatey打包MSI软件包的完整解决方案及技术总结

使用Chocolatey打包MSI软件包的完整解决方案及技术总结

2025/2/9 3:42:46 来源:https://blog.csdn.net/zhlh_xt/article/details/144277163  浏览:    关键词:使用Chocolatey打包MSI软件包的完整解决方案及技术总结

使用Chocolatey打包MSI软件包的完整解决方案及技术总结

在Windows系统上使用Chocolatey管理软件包是一种高效且自动化的方式,尤其是针对MSI格式的软件包。然而,在实际操作中,我们可能会遇到各种问题,例如检测旧版本、卸载旧版本以及处理多个匹配记录等。本文将详细记录从问题发现到最终解决的全过程,并分享最终的Chocolatey打包脚本,希望能为软件仓库维护人员解决类似问题提供启发。


问题背景

我们希望通过Chocolatey将一个MSI软件包打包成可安装的Chocolatey包,并实现以下功能:

  1. 检测是否存在旧版本。
  2. 如果存在旧版本,先卸载旧版本。
  3. 安装新版本的MSI软件包。
  4. 确保整个过程自动化且无用户干预。

问题与解决过程

1. 检测旧版本耗时过长

问题
起初,我们尝试使用以下命令检测是否存在已安装的软件:

Get-WmiObject -Class Win32_Product | Where-Object { $_.Name -like "*Your Software Name*" }

然而,这个命令会触发所有已安装MSI软件的一致性检查,导致系统卡顿甚至长时间无响应。

解决方案
改用注册表查询的方式,通过以下路径快速检索已安装的软件信息:

  • 64位应用程序路径:HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
  • 32位应用程序路径:HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall

优化后的查询命令如下:

Get-ChildItem -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall | 
Get-ItemProperty | 
Where-Object { $_.DisplayName -like "*Your Software Name*" }

2. 检测到多个匹配记录

问题
在注册表中查询时,可能会返回多个匹配记录(例如同一软件的不同版本或不同语言包)。这可能导致脚本只处理第一个匹配项,而忽略其他记录。

解决方案
通过遍历所有匹配记录,逐一处理每个已安装的软件。具体实现如下:

$installedSoftwareList = Get-ChildItem -Path $path | Get-ItemProperty | Where-Object { $_.DisplayName -like "*Your Software Name*" }foreach ($installedSoftware in $installedSoftwareList) {# 针对每个匹配的软件进行处理
}

3. 卸载旧版本失败

问题
在尝试卸载旧版本时,我们直接使用 UninstallString 作为 Start-Process-FilePath 参数,但由于 UninstallString 包含了路径和参数,导致报错 InvalidOperationException

例如:

MsiExec.exe /X{GUID}

解决方案
UninstallString 拆分为可执行文件路径和参数,然后分别传递给 Start-Process-FilePath-ArgumentList 参数。具体实现如下:

if ($uninstallString -match '^(.*\.exe)(.*)$') {$exePath = $matches[1]$arguments = $matches[2].Trim()$arguments = $arguments -replace '/I', '/X'  # 替换为卸载参数$arguments += ' /qn'  # 添加静默卸载参数Start-Process -FilePath $exePath -ArgumentList $arguments -Wait -NoNewWindow
}

最终完整的Chocolatey打包脚本

以下是经过优化后的完整脚本,能够检测并卸载旧版本,然后静默安装新版本的MSI软件包:

$ErrorActionPreference = 'Stop'# 定义软件名称和新版本号
$softwareName = 'Your Software Name'  # 替换为实际的软件名称
$newVersion = '1.2.3'  # 替换为新版本号# 定义注册表路径
$registryPaths = @("HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall","HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
)# 检查是否存在旧版本并卸载
foreach ($path in $registryPaths) {$installedSoftwareList = Get-ChildItem -Path $path | Get-ItemProperty | Where-Object { $_.DisplayName -like "*$softwareName*" }foreach ($installedSoftware in $installedSoftwareList) {$oldVersion = $installedSoftware.DisplayVersionWrite-Host "Found installed version: $oldVersion"if ([version]$oldVersion -lt [version]$newVersion) {$uninstallString = $installedSoftware.UninstallStringif ($uninstallString -match '^(.*\.exe)(.*)$') {$exePath = $matches[1]$arguments = $matches[2].Trim()$arguments = $arguments -replace '/I', '/X'  # 替换为卸载参数$arguments += ' /qn'  # 添加静默卸载参数Write-Host "Uninstalling version $oldVersion..."try {Start-Process -FilePath $exePath -ArgumentList $arguments -Wait -NoNewWindow}catch {Write-Host "Error uninstalling version $oldVersion: $_"continue  # 继续处理下一个版本}}}else {Write-Host "Version $oldVersion is up to date. No action needed."}}
}# 安装新版本的MSI
$packageArgs = @{packageName    = $env:ChocolateyPackageNamefileType       = 'MSI'url            = 'https://example.com/your-software.msi'  # 替换为实际的下载URLsoftwareName   = $softwareNamechecksum       = '1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF'  # 替换为实际的checksum值checksumType   = 'sha256'silentArgs     = "/qn /norestart ALLUSERS=1"  # 添加ALLUSERS=1以确保所有用户都能访问validExitCodes = @(0, 3010, 1641)
}Install-ChocolateyPackage @packageArgs

总结与思考

  1. 问题分解与逐步解决
    在面对复杂的问题时,将其分解为小问题逐一解决。例如,本案例中我们分别处理了检测、卸载和安装三个步骤。

  2. 选择合适的方法
    遇到性能瓶颈时(如使用 Get-WmiObject),及时切换到更高效的方法(如注册表查询)。

  3. 处理异常情况
    考虑到可能出现多个匹配项或卸载失败等情况,通过循环和异常捕获机制提高脚本的健壮性。

  4. 自动化与可维护性
    脚本设计时注重自动化和通用性,使其能够适应不同的软件和场景需求。

通过这个案例,我们不仅完成了具体任务,还锻炼了分析和解决问题的能力。希望这篇文章能为您在技术实践中提供启发!

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com