編寫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。)。