Powershell: удаление приложения с помощью UpgradeCode

Когда я обновляю / понижаю версию своего приложения с помощью скрипта Powershell, я хочу сначала принудительно удалить установленную версию перед запуском нового установщика.

Как я могу сделать это с Powershell, используя UpgradeCode приложения?

Делать это по имени приложения было бы менее надежным.

3 ответа

Поскольку вы упоминаете код обновления, это должно означать, что вы говорите о файле MSI ( установщик Windows). Как утверждают другие, такая деинсталляция обычно выполняется автоматически с помощью правильно созданного пакета MSI - это называется серьезным обновлением - которое, по сути, является деинсталляцией существующей версии продукта, а затем установкой самой новой версии.

В таблице обновлений устанавливаемого MSI будет указано, какие существующие пакеты на коробке будут удалены перед установкой новой версии. Теоретически вы можете удалить любое количество существующих установок. Вы даже можете удалить конкурентный продукт, если вы без ума от шляпника. Честно говоря, и удивительно, я никогда не пытался удалить несколько продуктов во время одного крупного обновления - это редко требуется. В большинстве случаев вы удаляете один существующий продукт, а затем устанавливаете последнюю версию.

  1. Вы можете изменить таблицу обновлений, используя преобразование, чтобы изменить поведение основного обновления - другими словами, запустить или прекратить удаление определенной ранее существующей установки.

  2. Вы также можете перечислить все связанные продукты, которые используют один и тот же код обновления, вызвав эту функцию MSI API (COM - VBScript, используемый в качестве примера):

Set installer = CreateObject("WindowsInstaller.Installer")

' Enumerate all products related to "Microsoft Visual C++ 2008 Redistributable - x86 9.0.30729.4148"

' {AA783A14-A7A3-3D33-95F0-9A351D530011} is the upgrade code
Set upgrades = installer.RelatedProducts("{AA783A14-A7A3-3D33-95F0-9A351D530011}")

For Each u In upgrades
   MsgBox u, vbOKOnly, "Product Code: "
Next

Затем вы можете удалить продукты, передав код (ы) продукта в командную строку msiexec.exe (см. Ниже, как это сделать с помощью автоматизации COMI MSI API):

msiexec.exe /x {11111111-1111-1111-1111-11111111111X} /L*V "C:\msilog.log" REBOOT=ReallySuppress

Краткое объяснение параметров (так как я рекомендую эту опцию):

 /X = run uninstall sequence
 /QN = run completely silently
 /L*V "C:\msilog.log"= verbose logging at path specified
 {11111111-1111-1111-1111-11111111111X} = product guid of app to uninstall
 REBOOT=ReallySuppress = prevent reboot without warning (badly authored MSI packages)

Если вы не хотите удалять через msiexec.exeЗатем вы можете найти множество способов вызвать MSI-деинсталляцию здесь: Деинсталляция MSI-файла из командной строки без использования msiexec.

И вы можете найти код продукта установленного MSI несколькими различными способами: Как я могу найти GUID продукта установленной установки MSI?


ОБНОВЛЕНИЕ: я думаю, я забыл очевидное, вы можете удалить непосредственно с помощью автоматизации MSI API. В приведенном ниже сценарии мы получаем все продукты, использующие один и тот же код обновления, а затем удаляем их последовательно.

Обратите внимание, что при запуске в режиме без вывода сообщений вы должны работать с правами администратора, поскольку UAC может быть подавлен, а затем удаление обычно завершится неудачно (разрешение отклонено). Из-за этого нижеприведенный скрипт выполняет деинсталляцию в интерактивном режиме, что позволяет запрашивать и повышение прав UAC

И если это не очевидно: запуск этого скрипта приведет к удалению Orca! Я использую этот продукт в качестве образца, потому что он быстро устанавливается снова ( подсказки по быстрому поиску установщика, если вам нужно перейти к нижней части здесь - найдите "orca"):

БОЛЬШОЙ ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ:

Метод COM installer.ConfigureProduct не принимает никаких аргументов, которые позволяют нам передавать в REBOOT=ReallySuppress, Это означает, что (очень) плохо созданный пакет, который запускает действие ScheduleReboot (или использует более неясное волшебство, чтобы вызвать перезагрузку) - может перезагрузить систему без предупреждения, если вы запустите приведенный ниже скрипт с правами администратора и в режиме без вывода сообщений.

Есть более новый звонок ConfigureProductEx которая доступна как функция Win32, но не предоставляется через интерфейс автоматизации COM. если ты platform invoke Вы можете использовать этот вызов - здесь есть пример C++ в разделе 14: Удаление MSI-файла из командной строки без использования msiexec. Или вы можете использовать функцию DTF из набора инструментов WiX (см. Раздел 6 в той же ссылке, что и в примере C++).


ОБНОВЛЕНИЕ июль 2018 года:

Set installer = CreateObject("WindowsInstaller.Installer")
installer.InstallProduct "product.msi", "REMOVE=ALL REBOOT=ReallySuppress"
Set installer = Nothing

Возможно, приведенный выше фрагмент является лучшим способом удаления? Это должно подавлять любые перезагрузки. У меня нет времени или настройки для его тестирования прямо сейчас (на коробке с Linux), но я хотел добавить его, прежде чем забыл.


Оригинальный скрипт удаления:

Const msiUILevelNone = 2
Const msiInstallStateAbsent = 2

Set installer = CreateObject("WindowsInstaller.Installer")
'installer.UILevel = msiUILevelNone ' Disabled to prevent silent uninstall. Now the UAC prompt will show

' Uninstall Orca, replace upgrade code with yours
Set products = installer.RelatedProducts("{CFF4D510-79B2-1CCD-0061-5741A0565A76}")

For Each product In products
   ' MsgBox "Product Code: " & product ' Show the product code found, if you want

   ' The following call when run silently with admin rights may reboot the system without warning!
   ' This is due to badly authored MSI packages - most packages will not trigger this problem.
   installer.ConfigureProduct product, 0,  msiInstallStateAbsent ' Uninstall product

   ' See text above for info on the newer ConfigureProductEx method.
Next

Set installer = Nothing

MsgBox "Finished" ' Just so we know the script ran if nothing found to uninstall

Некоторые ссылки:

Поскольку в вопросе конкретно упоминается powershell, я просто поставлю это здесь. Существуют и другие решения PS, связанные с использованием WMI и/или Get-Package. Это решение основано на https://outnull.wordpress.com/2016/11/02/uninstalling-application-based-on-upgradecode/ , но принимает различные формы синтаксиса кода обновления и пытается избежать манипуляций со строками при преобразовании. в/из пакета/обновления Guid и представления реестра.

      $upgradecode = "{CFF4D510-79B2-1CCD-0061-5741A0565A76}"
$installer = Join-Path -Path $env:SystemRoot -ChildPath "system32\msiexec.exe" -Resolve

function Reverse-Nibbles {
    param ( [byte[]] $bytes )

    # reverse nibbles of each byte
    for($i = 0; $i -lt $bytes.Length; $i++ )
    {
        $bytes[$i] = (($bytes[$i] -band 0x0F0F) -shl 4) -bor (($bytes[$i] -band 0xF0F0) -shr 4)
    }

    Write-Output -NoEnumerate $bytes
}

function GuidToRegString {
    param ( [guid] $guid )
    $bigendian = (Reverse-Nibbles $guid.ToByteArray())
    return [System.Runtime.Remoting.Metadata.W3cXsd2001.SoapHexBinary]::new($bigendian).ToString()
}

function RegStringToGuid {
    param ( [string] $guid )
    $littleendian = (Reverse-Nibbles ([System.Runtime.Remoting.Metadata.W3cXsd2001.SoapHexBinary]::Parse($guid).Value))
    return [guid]::new($littleendian)
}

$upcode = GuidToRegString ([guid]::Parse($upgradecode))

if (Test-Path -Path "HKLM:\Software\Classes\Installer\UpgradeCodes\$upcode") {
    $products = RegStringToGuid (Get-Item -Path "HKLM:\Software\Classes\Installer\UpgradeCodes\$upcode").Property

    foreach ($prod in $products) {
        $pguid = [guid]::new($prod)
        $p = $pguid.ToString("B")

        if ((Test-Path -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\$p") -or
            (Test-Path -Path "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\$p"))
        {
            $logfile = Join-Path -Path $PSScriptRoot -ChildPath uninstall-$($pguid.ToString("D")).log
            $args = @( "/x", $p, "/l*v", """$logfile""", "/q", "COMPLETE_UNINSTALL=1", "REBOOT=REALLYSUPPRESS" )
            Write-Host "Uninstalling $p"
            $uninst = Start-Process -FilePath """$installer""" -ArgumentList $args -PassThru -Wait
            Write-Host $uninst.ExitCode
        }
    }
}

ИспользоватьWindowsInstaller.Installerчтобы найти код продукта в коде обновления, затем запуститеmsiexec /x {productcode}.

Пример использования PowerShell для удаления кода обновления{939BFD63-B6E1-44BC-BD9E-225DDC30CF51}:

      PS C:\Users\user> $inst = New-Object -ComObject "WindowsInstaller.Installer"
PS C:\Users\user> $inst.RelatedProducts('{939BFD63-B6E1-44BC-BD9E-225DDC30CF51}')
{F587B97A-43C7-43D9-9243-DF76B0A54DE8}
PS C:\Users\user> msiexec /x "{F587B97A-43C7-43D9-9243-DF76B0A54DE8}"
PS C:\Users\user>
Другие вопросы по тегам