2008-09-24 140 views
13

我有一個WiX安裝程序和一個自定義操作(加上撤消和回滾),它使用安裝程序的屬性。在所有文件都在硬盤上後,自定義操作必須發生。看來你需要在WXS文件中有16個條目。八根目錄中,像這樣:如何最好地在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"/> 

八的InstallExecuteSequence內,像這樣:

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

有沒有更好的辦法?

回答

2

編寫WiX安裝程序時遇到同樣的問題。我對這個問題的解決方法大多是像Mike所建議的,我有一篇博文Implementing WiX custom actions part 2: using custom tables

總之,你可以定義自定義表爲您的數據:

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

然後寫一個直接的自定義操作來安排推遲,回滾和提交自定義操作:

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

以下代碼顯示如何計劃單個自定義操作。基本上你只要打開自定義表,閱讀你想要的屬性(你可以通過調用MsiViewGetColumnInfo())得到任何自定義表的模式,然後格式化所需的屬性到CustomActionData屬性(我使用表格/propname:value,儘管你可以使用任何你想要的)。

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

作爲實現遞延,回滾和提交自定義操作,我更喜歡使用只有一個功能,並使用MsiGetMode()區分應該做什麼:

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

通過使用以上技術,對於典型的自定義動作集,您可以將自定義動作表減少到五個條目:

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

And InstallSque NCE表只有兩個條目:

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

此外,隨着一點點的努力大部分代碼可以被寫入到被重用(如自定義表中讀取,得到的屬性,格式化所需的性能和設置CustomActionData屬性),並且自定義操作表中的條目現在不是特定於應用程序的(特定於應用程序的數據寫入自定義表),我們可以將自定義操作表放入其自己的文件中,並將其包含在每個WiX項目中。

對於自定義操作DLL文件,由於應用程序數據是從自定義表中讀取的,因此我們可以將特定於應用程序的詳細信息保留在DLL實現之外,因此自定義操作表可以成爲庫並因此更易於重用。

這是目前我寫我的WiX自定義操作,如果有人知道如何進一步改善,我會非常感激。 :)

(您也可以在我的博客文章中找到完整的源代碼,Implementing Wix custom actions part 2: using custom tables。)。

3

如果您有複雜的需要支持回滾的自定義操作,可以考慮編寫一個Wix擴展。擴展通常提供創作支持(即映射到MSI表條目的新XML標記),以及自定義操作的自動調度。

這不僅僅是編寫自定義操作,它的工作量更大,但是一旦您的CA達到一定的複雜程度,擴展所提供的易於編寫就值得。

4

WiX自定義操作是一個很好的模型。在這種情況下,您只需聲明CustomAction即時操作,延遲操作和回滾操作。您只需計劃Custom即時操作,其中即時操作作爲本機DLL中的代碼實現。

然後,在立即採取行動的代碼,你叫MsiDoAction安排回滾和遞延行動:他們被推遲,他們在點寫入腳本調用MsiDoAction,而不是立即執行。您還需要撥打MsiSetProperty以設置自定義操作數據。

下載WiX源代碼並研究IISExtension的工作方式。 WiX操作通常會分析自定義表並根據該表生成延期操作屬性的數據。