Pergunta

Eu tenho um WiX instalador e uma única ação personalizada (mais desfazer e reversão) para -lo que usa uma propriedade a partir do instalador. A ação personalizada tem que acontecer depois que todos os arquivos estão no disco rígido. Parece que você precisa de 16 entradas no arquivo WXS para isso; oito dentro da raiz, assim:

<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"/>

E oito dentro da InstallExecuteSequence, assim:

<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>

Existe uma maneira melhor?

Foi útil?

Solução

me deparei com o mesmo problema ao escrever instaladores WiX. Minha abordagem para o problema é muito parecido com o que Mike sugeriu e eu tenho um post Implementação WiX ações personalizadas parte 2 personalizados mesas--2-usando-: usando tabelas personalizadas

.

Em suma, você pode definir uma tabela personalizada para seus dados:

<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>

Em seguida, escreva uma única ação personalizada imediata para agendar o diferido, rollback, e cometer ações personalizadas:

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;
}

Os seguintes mostra o código como agendar uma única ação personalizada. Basicamente, você só abrir a tabela personalizada, leia o imóvel que deseja (você pode obter o esquema de qualquer tabela personalizada chamando MsiViewGetColumnInfo () ), então formatar as propriedades necessárias para o CustomActionData alojamento (I use o formulário /propname:value, embora você pode usar qualquer coisa que você quiser).

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);
    }
}

Como para a implementação do diferido, rollback e cometem ações personalizadas, eu prefiro usar apenas uma função e uso MsiGetMode () para distinguir o que deve ser feito:

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;
}

Ao usar a técnica acima, por uma ação personalizada típico ajustá-lo pode reduzir a tabela de ação personalizada para cinco entradas:

<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"/>

E InstallSquence tabela para apenas duas entradas:

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

Além disso, com um pouco de esforço maior parte do código pode ser escrito para ser reutilizado (como a leitura da tabela personalizada, obter as propriedades, formatação as propriedades necessárias e conjunto de propriedades CustomActionData), e as entradas na ação personalizada mesa agora não é de aplicação específica (os dados específicos de aplicação é escrita na tabela de costume), podemos colocar mesa de ação personalizada em um arquivo próprio e apenas incluí-lo em cada projeto WiX.

Para o arquivo DLL ação personalizada, uma vez que os dados do aplicativo é lido da tabela personalizada, podemos manter os detalhes específicos da aplicação para fora da implementação DLL, assim que a tabela de acção personalizada pode se tornar uma biblioteca e, portanto, mais fácil de reutilização.

Esta é a forma como atualmente eu escrevo minhas ações personalizadas WiX, se alguém sabe como melhorar ainda mais eu gostaria muito aprecio isso. :)

(Você também pode encontrar o código-fonte completo no meu blog post, Implementação Wix ações personalizadas parte 2 personalizados mesas--2-usando-: usando tabelas personalizadas )

..

Outras dicas

ações personalizadas Wix são um grande modelo a seguir. Neste caso, você só declarar, com CustomAction, a ação imediata, a ação diferida, ea ação de reversão. Você só cronograma, com Custom, a ação imediata, onde a ação imediata é implementado como código em uma DLL nativa.

Então, em Código da ação imediata , você chama MsiDoAction para agendar a reversão e ações adiadas: como eles são diferidos, eles são escritos no roteiro no ponto em que chamar MsiDoAction em vez de executadas imediatamente . Você precisará chamar MsiSetProperty, bem como para definir os dados de ação personalizada.

Faça o download do código-fonte WiX e estudar como o IISExtension funciona, por exemplo. ações WiX geralmente analisar uma tabela personalizada e gerar os dados para a propriedade da ação diferida com base nessa tabela.

Se você tem ações personalizadas complexas que precisam de reversão apoio, você pode considerar a escrever uma extensão Wix. Extensões normalmente fornecem autoria de apoio (ou seja, novas tags XML que são mapeados para as entradas da tabela MSI), além de programação automática de ações personalizadas.

É mais trabalho do que apenas escrevendo uma ação personalizada, mas uma vez que seus CAs chegar a um certo nível de complexidade, a facilidade de autoria que extensões fornecem pode valer a pena.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top