2008-09-24 23 views
13

J'ai un programme d'installation WiX et une seule action personnalisée (plus annulation et annulation) pour elle qui utilise une propriété du programme d'installation. L'action personnalisée doit se produire après que tous les fichiers sont sur le disque dur. Il semble que vous avez besoin de 16 entrées dans le fichier WXS pour cela; huit à l'intérieur de la racine, comme ceci:Quelle est la meilleure façon de définir une action personnalisée dans WiX?

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

Et huit dans le InstallExecuteSequence, comme ceci:

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

Y at-il une meilleure façon?

Répondre

2

J'ai rencontré le même problème lors de l'écriture des installateurs WiX. Mon approche du problème est la plupart du temps comme ce que Mike a suggéré et j'ai un billet de blog Implementing WiX custom actions part 2: using custom tables.

En bref, vous pouvez définir une table personnalisée pour vos données:

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

Puis écrire une seule action personnalisée immédiate pour planifier le différé, rollback et engager des actions personnalisées:

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

Le code suivant montre comment planifier une seule action personnalisée. Vous devez simplement ouvrir la table personnalisée, lire la propriété souhaitée (vous pouvez obtenir le schéma de toute table personnalisée en appelant MsiViewGetColumnInfo()), puis formater les propriétés requises dans la propriété CustomActionData (j'utilise le formulaire /propname:value, Bien que vous pouvez utiliser tout ce que vous voulez).

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 ce qui concerne la mise en œuvre du report, annulation et engager des actions personnalisées, je préfère utiliser une seule fonction et utiliser MsiGetMode() de distinguer ce qu'il faut faire:

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

En utilisant le Au-dessus de la technique, pour un ensemble d'actions personnalisées standard, vous pouvez réduire la table d'actions personnalisées à cinq entrées:

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

et InstallSque table nce à deux entrées:

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

En outre, avec un peu d'effort la plupart du code peut être écrit pour être réutilisés (telles que la lecture de la table personnalisée, obtenir les propriétés, le formatage des propriétés nécessaires et mis à Propriétés CustomActionData) et les entrées de la table d'actions personnalisées ne sont pas spécifiques à l'application (les données spécifiques à l'application sont écrites dans la table personnalisée), nous pouvons placer une table d'actions personnalisées dans un fichier et l'inclure dans chaque projet WiX . Pour le fichier DLL d'actions personnalisées, les données d'application étant lues dans la table personnalisée, nous pouvons conserver les détails spécifiques de l'application DLL afin que la table d'actions personnalisées puisse devenir une bibliothèque et donc plus facile à réutiliser.

Voici comment j'écris actuellement mes actions personnalisées WiX, si quelqu'un sait comment améliorer encore je l'apprécierais beaucoup. :)

(Vous pouvez également trouver le code source complet dans mon article de blog, Implementing Wix custom actions part 2: using custom tables.).

3

Si vous avez des actions personnalisées complexes qui doivent prendre en charge la restauration, vous pouvez envisager d'écrire une extension Wix. Les extensions fournissent généralement un support de création (c'est-à-dire de nouvelles étiquettes XML qui sont mappées aux entrées de table MSI), plus une planification automatique des actions personnalisées.

Il ne vous suffit pas d'écrire une action personnalisée, mais une fois que vos autorités de certification atteignent un certain niveau de complexité, la facilité de création offerte par les extensions peut en valoir la peine.

4

Les actions personnalisées WiX sont un excellent modèle à suivre. Dans ce cas, vous déclarez uniquement, avec CustomAction, l'action immédiate, l'action différée et l'action de restauration. Vous planifiez uniquement, avec Custom, l'action immédiate, où l'action immédiate est implémentée en tant que code dans une DLL native.

Ensuite, dans le code de l'action immédiate , vous appelez MsiDoAction pour planifier la restauration et les actions différées: comme ils sont reportés, ils sont écrits dans le script au point que vous appelez MsiDoAction plutôt que exécutée immédiatement. Vous devrez également appeler le MsiSetProperty pour définir les données d'action personnalisées.

Téléchargez le code source WiX et étudiez le fonctionnement du IISExtension, par exemple. Les actions WiX analysent généralement une table personnalisée et génèrent les données de la propriété de l'action différée en fonction de cette table.