2008-09-24 15 views
13

Ich habe ein WiX Installer und eine einzelne benutzerdefinierte Aktion (plus Rückgängigmachen und Rollback) dafür, die eine Eigenschaft aus dem Installer verwendet. Die benutzerdefinierte Aktion muss ausgeführt werden, nachdem sich alle Dateien auf der Festplatte befinden. Es scheint, dass Sie dafür 16 Einträge in der WXS-Datei benötigen. acht innerhalb der Wurzel, etwa so:Wie kann eine benutzerdefinierte Aktion in WiX am besten definiert werden?

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

und acht innerhalb der InstallExecuteSequence, etwa so:

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

Gibt es einen besseren Weg?

Antwort

2

Ich stieß auf das gleiche Problem beim Schreiben von WiX-Installern. Meine Herangehensweise an das Problem ist größtenteils wie das, was Mike vorgeschlagen hat, und ich habe einen Blog-Beitrag Implementing WiX custom actions part 2: using custom tables.

Kurz gesagt, können Sie eine benutzerdefinierte Tabelle für Ihre Daten definieren:

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

Dann eine einzige unmittelbare benutzerdefinierte Aktion schreiben das latente, Zurückkehren zu planen, und Commit Aktionen:

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

Der folgende Code zeigt, wie eine einzelne benutzerdefinierte Aktion geplant wird. Im Grunde öffnen Sie einfach die benutzerdefinierte Tabelle, lesen Sie die gewünschte Eigenschaft (Sie können das Schema jeder benutzerdefinierten Tabelle abrufen MsiViewGetColumnInfo()), dann formatieren Sie die erforderlichen Eigenschaften in die CustomActionData Eigenschaft (Ich verwende das Formular /propname:value, obwohl du alles verwenden kannst, was du willst).

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

wie für die latenten, Rollback-Implementierung und benutzerdefinierte Aktionen begehen, ziehe ich nur eine Funktion verwenden und MsiGetMode() zu unterscheiden, was getan werden sollte:

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

Durch die Verwendung von Über der Technik können Sie für eine typische benutzerdefinierte Aktion die benutzerdefinierte Aktionstabelle auf fünf Einträge reduzieren:

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

Und InstallSque nce Tabelle nur zwei Einträge:

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

Zusätzlich mit einem wenig Mühe der meisten des Code geschrieben werden können wiederverwendet werden (wie zum Beispiel von benutzerdefinierter Tabelle zu lesen, um die Eigenschaften bekommen, die Formatierung der benötigten Eigenschaften und auf CustomActionData-Eigenschaften), und die Einträge in der benutzerdefinierten Aktionstabelle sind jetzt nicht anwendungsspezifisch (die anwendungsspezifischen Daten werden in die benutzerdefinierte Tabelle geschrieben). Wir können die benutzerdefinierte Aktionstabelle in eine eigene Datei einfügen und sie einfach in jedes WiX-Projekt einfügen .

Da die Anwendungsdaten aus der benutzerdefinierten Tabelle gelesen werden, können anwendungsspezifische Details aus der DLL-Implementierung für die DLL-Datei mit benutzerdefinierter Aktion beibehalten werden, sodass die benutzerdefinierte Aktionstabelle zu einer Bibliothek werden und somit einfacher wiederverwenden kann.

So schreibe ich derzeit meine WiX Custom Actions, wenn jemand weiß, wie ich mich weiter verbessern kann, würde ich es sehr zu schätzen wissen. :)

(Den vollständigen Quellcode finden Sie auch in meinem Blogpost, Implementing Wix custom actions part 2: using custom tables.).

3

Wenn Sie komplexe benutzerdefinierte Aktionen haben, die Rollback unterstützen müssen, sollten Sie eine Wix-Erweiterung schreiben. Erweiterungen bieten normalerweise Authoring-Unterstützung (d. H. Neue XML-Tags, die MSI-Tabelleneinträgen zugeordnet werden) sowie eine automatische Planung von benutzerdefinierten Aktionen.

Es ist mehr Arbeit, als nur eine benutzerdefinierte Aktion zu schreiben, aber sobald Ihre Zertifizierungsstellen ein gewisses Maß an Komplexität erreichen, kann es sich lohnen, die von diesen Erweiterungen bereitgestellten Authoring-Funktionen zu erstellen.

4

Die benutzerdefinierten WiX-Aktionen sind ein hervorragendes Modell. In diesem Fall deklarieren Sie nur mit CustomAction die sofortige Aktion, die verzögerte Aktion und die Rollback-Aktion. Sie planen nur die Sofortaktion mit Custom, bei der die sofortige Aktion als Code in einer systemeigenen DLL implementiert wird.

Dann in der unmittelbaren des Aktionscode, rufen Sie MsiDoAction das Rollback und verzögerte Aktionen planen: wie sie verschoben werden, werden sie in das Skript an der Stelle geschrieben rufen Sie MsiDoAction anstatt sofort ausgeführt. Sie müssen auch MsiSetProperty aufrufen, um die benutzerdefinierten Aktionsdaten festzulegen.

Laden Sie den WiX-Quellcode herunter und untersuchen Sie, wie beispielsweise die IISExtension funktioniert. WiX-Aktionen analysieren im Allgemeinen eine benutzerdefinierte Tabelle und generieren die Daten für die Eigenschaft der verzögerten Aktion basierend auf dieser Tabelle.

Verwandte Themen