'MsiEnumRelatedProducts returns ERROR_INVALID_PARAMETER with iProductIndex > 0

I'm trying to write an AutoIt script that uninstalls all MSI packages with a specific Upgrade Code. This is my code so far:

$i = 0
Do
  $buffer = DllStructCreate("wchar[39]")
  $ret = DllCall("msi.dll", "UINT", "MsiEnumRelatedProductsW", _
    "wstr", "{a1b6bfda-45b6-43cc-88de-d9d29dcafdca}", _ ; lpUpgradeCode
    "dword", 0, _ ; dwReserved
    "dword", $i, _ ; iProductIndex
    "ptr", DllStructGetPtr($buffer)) ; lpProductBuf
  $i = $i + 1
  MsgBox(0, "", $ret[0] & " " & DllStructGetData($buffer, 1))
Until($ret[0] <> 0)

This works flawlessly to determine the Product Code for the first installed product, but it returns 87 (ERROR_INVALID_PARAMETER) as soon as iProductIndex is incremented to 1. Usually this error is returned when the input GUID is malformed, but if that would be the case, it shouldn't work with iProductIndex = 0 either...

What I expected from this code (when 2 packages with the same Upgrade Code are installed) is:

  1. Print "0 <first Product Code>"
  2. Print "0 <second Product Code>"
  3. Print "259" (ERROR_NO_MORE_ITEMS)

What it currently does:

  1. Print "0 <first Product Code>"
  2. Print "87" (ERROR_INVALID_PARAMETER)

Any ideas?

(If you want to test this code on your own computer, you will need to have two MSI packages with the same UpgradeCode installed. Here are my WiX test packages: http://pastie.org/3022676 )



Solution 1:[1]

OK, I've found a simple workaround: I just remove every product I can find with iProductIndex = 0 in a loop.

Func GetProduct($UpgradeCode)
  $buffer = DllStructCreate("wchar[39]")
  $ret = DllCall("msi.dll", "UINT", "MsiEnumRelatedProductsW", _
    "wstr", $UpgradeCode, _ ; lpUpgradeCode
    "dword", 0, _ ; dwReserved
    "dword", 0, _ ; iProductIndex
    "ptr", DllStructGetPtr($buffer)) ; lpProductBuf
  Return DllStructGetData($buffer, 1)
EndFunc

$Last = ""
$Product = ""
Do
  $Last = $Product
  $Product = GetProduct("{a1b6bfda-45b6-43cc-88de-d9d29dcafdca}")
  If $Product = "" Then Exit

  $Ret = RunWait("msiexec /qn /x " & $Product)
  ConsoleWrite($Ret & " " & $Product & @CRLF)
  If $Product = $Last Then Exit 1
Until($product = "")

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Martin von Wittich