Как лучше всего определить пользовательское действие в WiX?

StackOverflow https://stackoverflow.com/questions/128279

  •  02-07-2019
  •  | 
  •  

Вопрос

у меня есть WiX установщика и одно настраиваемое действие (плюс отмена и откат) для него, которое использует свойство установщика.Специальное действие должно произойти после того, как все файлы окажутся на жестком диске.Кажется, для этого вам понадобится 16 записей в файле WXS;восемь внутри корня, вот так:

<CustomAction Id="SetForRollbackDo" Execute="immediate" Property="RollbackDo" Value="[MYPROP]"/>
<CustomAction Id="RollbackDo" Execute="rollback" BinaryKey="MyDLL" DllEntry="UndoThing" Return="ignore"/>
<CustomAction Id="SetForDo" Execute="immediate" Property="Do" Value="[MYPROP]"/>
<CustomAction Id="Do" Execute="deferred" BinaryKey="MyDLL" DllEntry="DoThing" Return="check"/>
<CustomAction Id="SetForRollbackUndo" Execute="immediate" Property="RollbackUndo" Value="[MYPROP]"/>
<CustomAction Id="RollbackUndo" Execute="rollback" BinaryKey="MyDLL" DllEntry="DoThing" Return="ignore"/>
<CustomAction Id="SetForUndo" Execute="immediate" Property="Undo" Value="[MYPROP]"/>
<CustomAction Id="Undo" Execute="deferred" BinaryKey="MyDLL" DllEntry="UndoThing" Return="check"/>

И восемь внутри InstallExecuteSequence, вот так:

<Custom Action="SetForRollbackDo" After="InstallFiles">REMOVE&lt;>"ALL"</Custom>
<Custom Action="RollbackDo" After="SetForRollbackDo">REMOVE&lt;>"ALL"</Custom>
<Custom Action="SetForDo" After="RollbackDo">REMOVE&lt;>"ALL"</Custom>
<Custom Action="Do" After="SetForDo">REMOVE&lt;>"ALL"</Custom>
<Custom Action="SetForRollbackUndo" After="InstallInitialize">REMOVE="ALL"</Custom>
<Custom Action="RollbackUndo" After="SetForRollbackUndo">REMOVE="ALL"</Custom>
<Custom Action="SetForUndo" After="RollbackUndo">REMOVE="ALL"</Custom>
<Custom Action="Undo" After="SetForUndo">REMOVE="ALL"</Custom>

Есть ли способ лучше?

Это было полезно?

Решение

Я столкнулся с той же проблемой при написании установщиков WiX.Мой подход к проблеме во многом аналогичен предложенному Майком, и у меня есть сообщение в блоге. Реализация пользовательских действий WiX, часть 2:использование пользовательских таблиц.

Короче говоря, вы можете определить собственную таблицу для своих данных:

<CustomTable Id="LocalGroupPermissionTable">
    <Column Id="GroupName" Category="Text" PrimaryKey="yes" Type="string"/>
    <Column Id="ACL" Category="Text" PrimaryKey="no" Type="string"/>
    <Row>
        <Data Column="GroupName">GroupToCreate</Data>
        <Data Column="ACL">SeIncreaseQuotaPrivilege</Data>
    </Row>
</CustomTable>

Затем напишите одно немедленное настраиваемое действие, чтобы запланировать настраиваемые действия отсрочки, отката и фиксации:

extern "C" UINT __stdcall ScheduleLocalGroupCreation(MSIHANDLE hInstall)
{
    try {
        ScheduleAction(hInstall,L"SELECT * FROM CreateLocalGroupTable", L"CA.LocalGroupCustomAction.deferred", L"create");
        ScheduleAction(hInstall,L"SELECT * FROM CreateLocalGroupTable", L"CA.LocalGroupCustomAction.rollback", L"create");
    }
    catch( CMsiException & ) {
        return ERROR_INSTALL_FAILURE;
    }
    return ERROR_SUCCESS;
}

Следующий код показывает, как запланировать одно настраиваемое действие.По сути, вы просто открываете пользовательскую таблицу, читаете нужное свойство (вы можете получить схему любой пользовательской таблицы, вызвав MsiViewGetColumnInfo()), затем отформатируйте необходимые свойства в ПользовательскиеActionData свойство (я использую форму /propname:value, хотя вы можете использовать все, что захотите).

void ScheduleAction(MSIHANDLE hInstall,
            const wchar_t *szQueryString,
            const wchar_t *szCustomActionName,
            const wchar_t *szAction)
{
    CTableView view(hInstall,szQueryString);
    PMSIHANDLE record;

    //For each record in the custom action table
    while( view.Fetch(record) ) {
        //get the "GroupName" property
        wchar_t recordBuf[2048] = {0};
        DWORD    dwBufSize(_countof(recordBuf));
        MsiRecordGetString(record, view.GetPropIdx(L"GroupName"), recordBuf, &dwBufSize);

        //Format two properties "GroupName" and "Operation" into
        //the custom action data string.
        CCustomActionDataUtil formatter;
        formatter.addProp(L"GroupName", recordBuf);
        formatter.addProp(L"Operation", szAction );

        //Set the "CustomActionData" property".
        MsiSetProperty(hInstall,szCustomActionName,formatter.GetCustomActionData());

        //Add the custom action into installation script. Each
        //MsiDoAction adds a distinct custom action into the
        //script, so if we have multiple entries in the custom
        //action table, the deferred custom action will be called
        //multiple times.
        nRet = MsiDoAction(hInstall,szCustomActionName);
    }
}

Что касается реализации пользовательских действий отложенного выполнения, отката и фиксации, я предпочитаю использовать только одну функцию и использовать Мсижетмоде() различать, что следует делать:

extern "C" UINT __stdcall LocalGroupCustomAction(MSIHANDLE hInstall)
{
    try {
        //Parse the properties from the "CustomActionData" property
        std::map<std::wstring,std::wstring> mapProps;
        {
            wchar_t szBuf[2048]={0};
            DWORD dwBufSize = _countof(szBuf); MsiGetProperty(hInstall,L"CustomActionData",szBuf,&dwBufSize);
            CCustomActionDataUtil::ParseCustomActionData(szBuf,mapProps);
        }

        //Find the "GroupName" and "Operation" property
        std::wstring sGroupName;
        bool bCreate = false;
        std::map<std::wstring,std::wstring>::const_iterator it;
        it = mapProps.find(L"GroupName");
        if( mapProps.end() != it ) sGroupName = it->second;
        it = mapProps.find(L"Operation");
        if( mapProps.end() != it )
            bCreate = wcscmp(it->second.c_str(),L"create") == 0 ? true : false ;

        //Since we know what opeartion to perform, and we know whether it is
        //running rollback, commit or deferred script by MsiGetMode, the
        //implementation is straight forward
        if( MsiGetMode(hInstall,MSIRUNMODE_SCHEDULED) ) {
            if( bCreate )
                CreateLocalGroup(sGroupName.c_str());
            else
                DeleteLocalGroup(sGroupName.c_str());
        }
        else if( MsiGetMode(hInstall,MSIRUNMODE_ROLLBACK) ) {
            if( bCreate )
                DeleteLocalGroup(sGroupName.c_str());
            else
                CreateLocalGroup(sGroupName.c_str());
        }
    }
    catch( CMsiException & ) {
        return ERROR_INSTALL_FAILURE;
    }
    return ERROR_SUCCESS;
}

Используя описанный выше метод, для типичного набора дополнительных действий вы можете сократить таблицу дополнительных действий до пяти записей:

<CustomAction Id="CA.ScheduleLocalGroupCreation"
              Return="check"
              Execute="immediate"
              BinaryKey="CustomActionDLL"
              DllEntry="ScheduleLocalGroupCreation"
              HideTarget="yes"/>
<CustomAction Id="CA.ScheduleLocalGroupDeletion"
              Return="check"
              Execute="immediate"
              BinaryKey="CustomActionDLL"
              DllEntry="ScheduleLocalGroupDeletion"
              HideTarget="yes"/>
<CustomAction Id="CA.LocalGroupCustomAction.deferred"
              Return="check"
              Execute="deferred"
              BinaryKey="CustomActionDLL"
              DllEntry="LocalGroupCustomAction"
              HideTarget="yes"/>
<CustomAction Id="CA.LocalGroupCustomAction.commit"
              Return="check"
              Execute="commit"
              BinaryKey="CustomActionDLL"
              DllEntry="LocalGroupCustomAction"
              HideTarget="yes"/>
<CustomAction Id="CA.LocalGroupCustomAction.rollback"
              Return="check"
              Execute="rollback"
              BinaryKey="CustomActionDLL"
              DllEntry="LocalGroupCustomAction"
              HideTarget="yes"/>

В таблице InstallSquence всего две записи:

<InstallExecuteSequence>
    <Custom Action="CA.ScheduleLocalGroupCreation" 
            After="InstallFiles">
        Not Installed
    </Custom>
    <Custom Action="CA.ScheduleLocalGroupDeletion" 
            After="InstallFiles">
        Installed
    </Custom>
</InstallExecuteSequence>

Кроме того, приложив небольшие усилия, большую часть кода можно написать для повторного использования (например, чтение из настраиваемой таблицы, получение свойств, форматирование необходимых свойств и установка свойств CustomActionData), а записи в таблице настраиваемых действий теперь имеют вид не зависит от приложения (данные, специфичные для приложения, записываются в настраиваемую таблицу), мы можем поместить таблицу настраиваемых действий в отдельный файл и просто включить ее в каждый проект WiX.

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

Вот как сейчас я пишу свои пользовательские действия WiX, если кто-нибудь знает, как их улучшить дальше, я был бы очень признателен.:)

(Вы также можете найти полный исходный код в моем блоге, Реализация пользовательских действий Wix, часть 2:использование пользовательских таблиц.).

Другие советы

Пользовательские действия WiX — отличная модель для подражания.В этом случае вы только заявляете, с CustomAction, немедленное действие, отложенное действие и действие отката.Вы только планируете, с Custom, немедленное действие, при котором немедленное действие реализуется в виде кода в собственной DLL.

Затем в ходе немедленных действий код, ты звонишь MsiDoAction чтобы запланировать откат и отложенные действия:поскольку они отложены, они записываются в сценарий в тот момент, когда вы вызываете MsiDoAction а не выполнять немедленно.Вам нужно будет позвонить MsiSetProperty а также для установки данных специального действия.

Загрузите исходный код WiX и изучите, как IISExtension работает, например.Действия WiX обычно анализируют пользовательскую таблицу и генерируют данные для свойства отложенного действия на основе этой таблицы.

Если у вас есть сложные пользовательские действия, которые должны поддерживать откат, вы можете рассмотреть возможность написания расширения Wix.Расширения обычно обеспечивают поддержку авторской разработки (т. е.новые теги XML, которые сопоставляются с записями таблицы MSI), а также автоматическое планирование настраиваемых действий.

Это больше работы, чем просто написание специального действия, но как только ваши центры сертификации достигнут определенного уровня сложности, простота разработки, обеспечиваемая расширениями, может того стоить.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top