?

Log in

Previous Entry | Next Entry

 
У wix установщика есть такая неприятная вещь, как оставлять нетронутыми предыдущие установки, если они были сделаны в другом контексте.
 
Например, если у вас имеется версия "для всех", а сейчас вы ставите версию "только для себя" и хотите использовать механизм FindRelatedProducts для удаления старых уставок, то сразу ничего не получится. Нет, wix, конечно, найдёт старую версию, но честно признается в том, что пропустит её удаление, т.к. она сделана в другом контексте.
Решение есть.
Предлагается перед стандартной процедурой удаление RemoveExistingProducts запускать "свой" поиск предыдущих версий, если wix отказывается это делать за нас.
Таким образом, нам нужно

1. свойство для записи GUID'ов предыдущих версий
OLD_PRODUCTS

2. условия при которых удалять старые версии. Например, всегда:
<Upgrade Id="INSERT_UPGRADE_GUID_HERE">
<UpgradeVersion Minimum="0.0.0.0" IncludeMinimum="yes" Maximum="99.0.0.0" IncludeMaximum="yes" Property="OLD_PRODUCTS" />
</Upgrade>
 
3. условия при которых делать принудительное удаление (вернее принудительный поиск) старых версий
<Custom Action="Action.FindPreviousProducts" Before="RemoveExistingProducts">(NOT Installed) AND (NOT (REMOVE="ALL")) AND OLD_PRODUCTS=""</Custom>

для чего необходимо заранее объявить действие Action.FindPreviousProducts:
<CustomAction BinaryKey="Binary.FindPreviousProducts" Id="Action.FindPreviousProducts" DllEntry="FindPreviousProducts" Execute="immediate" Return="check" />
<Binary Id="Binary.FindPreviousProducts" SourceFile="OldProductFinder.dll" />
 
а в библиотеке OldProductFinder.dll выставить наружу CustomAction метод FindPreviousProducts.

Пример C# кода для п.3:

[DllImport("msi.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern uint MsiEnumRelatedProducts(string lpUpgradeCode,int dwReserved,int lProductIndex,StringBuilder lpProductBuf);

private static string FindPreviousProductCodes(string methodName, Session session)
{
int index = 0;
List<string> productCodes = new List<string>();
do
{
StringBuilder productCode = new StringBuilder(39);
uint rc = MsiEnumRelatedProducts(Constants.UPGRADE_CODE, 0, index++, productCode);
if (rc != 0)
{
break;
}
productCodes.Add(productCode.ToString());
} while (true);
if (productCodes.Count == 0)
{
//session.Log(...);
return string.Empty;
}
string previousProductCodes = string.Join(";", productCodes.ToArray());
return previousProductCodes;
}

[CustomAction]
public static ActionResult FindPreviousProducts(Session session)
{
try
{
session["OLD_PRODUCTS"] = FindPreviousProductCodes(methodName, session);
return ActionResult.Success;
}
catch (Exception ex)
{
//session.Log(...);
return ActionResult.Failure;
}
finally
{
//session.Log(...);
}
}

По мотивам статьи.