Pregunta

Tengo un instalador WiX y una única acción personalizada (más deshacer y deshacer) para que utiliza una propiedad del instalador. La acción personalizada tiene que ocurrir después de que todos los archivos estén en el disco duro. Parece que necesitas 16 entradas en el archivo WXS para esto; ocho dentro de la raíz, así:

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

Y ocho dentro del InstallExecuteSequence , así:

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

¿Hay una mejor manera?

¿Fue útil?

Solución

Encontré el mismo problema al escribir instaladores de WiX. Mi enfoque del problema es casi como lo que sugirió Mike y tengo una publicación en el blog Implementación de las acciones personalizadas de WiX, parte 2: uso de tablas personalizadas .

En resumen, puede definir una tabla personalizada para sus datos:

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

Luego, escriba una única acción personalizada inmediata para programar las acciones personalizadas diferidas, retrotractadas y confirmadas:

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

El siguiente código muestra cómo programar una única acción personalizada. Básicamente, simplemente abre la tabla personalizada, lee la propiedad que desea (puede obtener el esquema de cualquier tabla personalizada llamando a MsiViewGetColumnInfo () ), luego formatee las propiedades necesarias en CustomActionData property (uso el formulario / propname: value , aunque puedes usar lo que quieras)

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

En cuanto a la implementación de las acciones personalizadas diferidas, retrotraer y confirmar, prefiero usar solo una función y usar MsiGetMode () para distinguir lo que se debe hacer:

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

Al utilizar la técnica anterior, para un conjunto de acciones personalizado típico, puede reducir la tabla de acciones personalizadas a 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"/>

Y tabla de InstallSquence a solo dos entradas:

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

Además, con un poco de esfuerzo, la mayoría del código puede escribirse para ser reutilizado (como leer de la tabla personalizada, obtener las propiedades, formatear las propiedades necesarias y establecerlas en las propiedades CustomActionData) y las entradas en la acción personalizada La tabla ahora no es específica de la aplicación (los datos específicos de la aplicación están escritos en la tabla personalizada), podemos poner la tabla de acciones personalizada en un archivo propio y simplemente incluirla en cada proyecto de WiX.

Para el archivo DLL de acciones personalizadas, ya que los datos de la aplicación se leen de la tabla personalizada, podemos mantener los detalles específicos de la aplicación fuera de la implementación de la DLL, por lo que la tabla de acciones personalizadas puede convertirse en una biblioteca y, por lo tanto, más fácil de reutilizar.

Así es como actualmente escribo mis acciones personalizadas de WiX, si alguien sabe cómo mejorar aún más, lo apreciaría mucho. :)

(También puede encontrar el código fuente completo en la publicación de mi blog, Implementando las acciones personalizadas de Wix parte 2: utilizando tablas personalizadas .).

Otros consejos

Las acciones personalizadas de WiX son un gran modelo a seguir. En este caso, solo declara, con CustomAction , la acción inmediata, la acción diferida y la acción de retrotracción. Solo programa, con Custom , la acción inmediata, donde la acción inmediata se implementa como código en una DLL nativa.

Luego, en el código de la acción inmediata, llama a MsiDoAction para programar la reversión y las acciones diferidas: a medida que se difieren, se escriben en el script en el punto llama a MsiDoAction en lugar de ejecutarse inmediatamente. Deberá llamar también a MsiSetProperty para configurar los datos de acción personalizados.

Descargue el código fuente de WiX y estudie cómo funciona IISExtension , por ejemplo. Las acciones de WiX generalmente analizan una tabla personalizada y generan los datos para la propiedad de la acción diferida basada en esa tabla.

Si tiene acciones personalizadas complejas que necesitan ser compatibles con la reversión, podría considerar escribir una extensión Wix. Las extensiones suelen proporcionar soporte de creación (es decir, nuevas etiquetas XML que se asignan a las entradas de la tabla MSI), además de la programación automática de acciones personalizadas.

Es más trabajo que solo escribir una acción personalizada, pero una vez que sus CA alcancen un cierto nivel de complejidad, la facilidad de creación que proporcionan las extensiones puede valer la pena.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top