Domanda

Ho un WiX e una singola azione personalizzata (più annulla e ripristina) per che utilizza una proprietà dall'installer. L'azione personalizzata deve avvenire dopo che tutti i file si trovano sul disco rigido. Sembra che tu abbia bisogno di 16 voci nel file WXS per questo; otto all'interno della radice, in questo modo:

<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 otto all'interno del InstallExecuteSequence , in questo modo:

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

C'è un modo migliore?

È stato utile?

Soluzione

Mi sono imbattuto nello stesso problema durante la scrittura di programmi di installazione WiX. Il mio approccio al problema è principalmente come quello che Mike ha suggerito e ho un post sul blog Implementazione di azioni personalizzate WiX parte 2: utilizzo di tabelle personalizzate .

In breve, puoi definire una tabella personalizzata per i tuoi dati:

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

Quindi scrivi una singola azione personalizzata immediata per pianificare le azioni personalizzate posticipate, di rollback e di commit:

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

Il codice seguente mostra come pianificare una singola azione personalizzata. Fondamentalmente basta aprire la tabella personalizzata, leggere la proprietà desiderata (è possibile ottenere lo schema di qualsiasi tabella personalizzata chiamando MsiViewGetColumnInfo () ), quindi formattare le proprietà necessarie in CustomActionData (utilizzo il modulo / propname: value , sebbene sia possibile utilizzare tutto ciò che si desidera).

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

Per quanto riguarda l'implementazione delle azioni personalizzate posticipate, di rollback e di commit, preferisco utilizzare solo una funzione e utilizzare MsiGetMode () per distinguere cosa deve essere fatto:

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

Utilizzando la tecnica sopra descritta, per un tipico set di azioni personalizzate è possibile ridurre la tabella di azioni personalizzate a cinque voci:

<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 la tabella InstallSquence con solo due voci:

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

Inoltre, con un piccolo sforzo, la maggior parte del codice può essere scritta per essere riutilizzata (come lettura dalla tabella personalizzata, acquisizione delle proprietà, formattazione delle proprietà necessarie e impostazione delle proprietà CustomActionData) e voci nell'azione personalizzata tabella ora non è specifica dell'applicazione (i dati specifici dell'applicazione sono scritti nella tabella personalizzata), possiamo mettere una tabella di azioni personalizzata in un file a sé stante e includerla in ogni progetto WiX.

Per il file DLL dell'azione personalizzata, poiché i dati dell'applicazione vengono letti dalla tabella personalizzata, possiamo mantenere i dettagli specifici dell'applicazione fuori dall'implementazione della DLL, in modo che la tabella delle azioni personalizzate possa diventare una libreria e quindi più facile da riutilizzare.

Ecco come attualmente scrivo le mie azioni personalizzate WiX, se qualcuno sa come migliorare ulteriormente lo apprezzerei molto. :)

(Puoi anche trovare il codice sorgente completo nel mio post sul blog, Implementazione di azioni personalizzate Wix parte 2: utilizzo di tabelle personalizzate .).

Altri suggerimenti

Le azioni personalizzate WiX sono un ottimo modello da seguire. In questo caso, dichiari solo, con CustomAction , l'azione immediata, l'azione differita e l'azione di rollback. Pianifichi solo, con Personalizzato , l'azione immediata, in cui l'azione immediata viene implementata come codice in una DLL nativa.

Quindi, nel codice dell'azione immediata , si chiama MsiDoAction per pianificare il rollback e le azioni differite: quando vengono rinviati, vengono scritti nello script nel punto chiami MsiDoAction anziché eseguire immediatamente. Dovrai anche chiamare MsiSetProperty per impostare i dati dell'azione personalizzata.

Scarica il codice sorgente WiX e studia come funziona IISExtension , ad esempio. Le azioni WiX generalmente analizzano una tabella personalizzata e generano i dati per la proprietà dell'azione differita in base a quella tabella.

Se hai azioni personalizzate complesse che devono supportare il rollback, potresti considerare di scrivere un'estensione Wix. Le estensioni in genere forniscono supporto per la creazione (ovvero nuovi tag XML che vengono mappati alle voci della tabella MSI), oltre alla pianificazione automatica delle azioni personalizzate.

È più lavoro che scrivere semplicemente un'azione personalizzata, ma una volta che le autorità di certificazione raggiungono un certo livello di complessità, la facilità di creazione fornita dalle estensioni può valerne la pena.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top