The 删除文件元素 http://wix.sourceforge.net/manual-wix3/wix_xsd_removefile.htm正是为了做到这一点而设计的。您可以使用它来教导 MSI 删除它未安装的应用程序数据。优点是在回滚期间文件将被放回原位。
您还可以使用RemoveFolder 元素删除整个目录。一般来说,概念是删除文件并指定文件夹。这不是递归的,因此您需要对可能已创建的任何子目录执行此操作。
编写自定义操作只是重新发明轮子并增加安装程序的脆弱性。仅当无法提前知道子目录时才应使用它。在这种情况下,理想的情况是在安装时使用 MSI 中的临时行将行动态发送到 MSI 中,并让 MSI 处理实际的删除。这允许回滚功能仍然有效。
这是一个非常简单的版本。可以通过从自定义表驱动数据而不是组件 ID 和目录 ID 的常量字符串来改进它。
public class RecursiveDeleteCustomAction
{
[CustomAction]
public static ActionResult RecursiveDeleteCosting(Session session)
{
// SOMECOMPONENTID is the Id attribute of a component in your install that you want to use to trigger this happening
const string ComponentID = "SOMECOMPONENTID";
// SOMEDIRECTORYID would likely be INSTALLDIR or INSTALLLOCATION depending on your MSI
const string DirectoryID = "SOMEDIRECTORYID";
var result = ActionResult.Success;
int index = 1;
try
{
string installLocation = session[DirectoryID];
session.Log("Directory to clean is {0}", installLocation);
// Author rows for root directory
// * means all files
// null means the directory itself
var fields = new object[] { "CLEANROOTFILES", ComponentID, "*", DirectoryID, 3 };
InsertRecord(session, "RemoveFile", fields);
fields = new object[] { "CLEANROOTDIRECTORY", ComponentID, "", DirectoryID, 3 };
InsertRecord(session, "RemoveFile", fields);
if( Directory.Exists(installLocation))
{
foreach (string directory in Directory.GetDirectories(installLocation, "*", SearchOption.AllDirectories))
{
session.Log("Processing Subdirectory {0}", directory);
string key = string.Format("CLEANSUBFILES{0}", index);
string key2 = string.Format("CLEANSUBDIRECTORY{0}", index);
session[key] = directory;
fields = new object[] { key, ComponentID, "*", key, 3 };
InsertRecord(session, "RemoveFile", fields);
fields = new object[] { key2, ComponentID, "", key, 3 };
InsertRecord(session, "RemoveFile", fields);
index++;
}
}
}
catch (Exception ex)
{
session.Log(ex.Message);
result = ActionResult.Failure;
}
return result;
}
private static void InsertRecord(Session session, string tableName, Object[] objects)
{
Database db = session.Database;
string sqlInsertSring = db.Tables[tableName].SqlInsertString + " TEMPORARY";
session.Log("SqlInsertString is {0}", sqlInsertSring);
View view = db.OpenView(sqlInsertSring);
view.Execute(new Record(objects));
view.Close();
}
}