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