C# wix-安装前删除旧程序文件夹

C# wix-安装前删除旧程序文件夹,c#,wix,custom-action,C#,Wix,Custom Action,我需要安装程序删除旧的安装目录(如果存在),就在安装程序开始复制新文件之前。此文件夹包含程序使用期间生成的一些文件和子文件夹,它们不包括在安装程序中。因此,我创建了自定义操作来执行此操作 所以,一些代码。首先,自定义操作代码(没有什么特别之处): 和wix代码(定义自定义操作调用): 自找的 纽尔费 未安装且未升级ProductCode 已安装但未重新安装且未升级ProductCode 未安装且未升级ProductCode 已安装但未重新安装且未升级ProductCode 有什么问题吗?如您

我需要安装程序删除旧的安装目录(如果存在),就在安装程序开始复制新文件之前。此文件夹包含程序使用期间生成的一些文件和子文件夹,它们不包括在安装程序中。因此,我创建了自定义操作来执行此操作

所以,一些代码。首先,自定义操作代码(没有什么特别之处):

和wix代码(定义自定义操作调用):


自找的
纽尔费
未安装且未升级ProductCode
已安装但未重新安装且未升级ProductCode
未安装且未升级ProductCode
已安装但未重新安装且未升级ProductCode
有什么问题吗?如您所见,actionRemoveOldDatabase应该在安装程序开始复制新文件之前触发(参数已经由SetRemoveOldDatabaseParameters设置)。因此-只应删除旧文件,但这不会发生。如果我这样做,actionactionRemoveOldDatabase,安装程序将新文件复制到安装目录后,安装目录将被删除。因此-安装程序复制的所有新文件都将被删除

我不明白为什么?如何只删除旧的、已经存在的文件夹,以及为什么我的自定义操作会删除所有复制的文件

[编辑] 看来我已经知道原因了。在这种情况下,Install Dir正在使用中(可能是windows installer将其锁定),并在安装结束后释放。自定义操作将等待文件夹释放,然后将其删除。不幸的是,太晚了-文件夹已经包含新文件

您知道任何解决方法吗?

的设计正是为了做到这一点。您可以使用此命令来教导MSI删除未安装的应用程序数据。优点是在回滚期间,文件将放回原位

您还可以使用RemoveFolder元素删除整个目录。通常,概念是*文件删除,并指定文件夹。这不是递归的,所以您需要对可能已经创建的任何子目录执行此操作

编写自定义操作只是重新发明轮子,增加了安装程序的脆弱性。仅当无法预先知道子目录时,才应使用它。在这种情况下,理想的情况是使用MSI中的临时行在安装时将这些行动态地发送到MSI中,并让MSI处理实际的删除。这允许回滚功能仍然工作

这是一个非常简单的版本。可以通过使其从自定义表而不是ComponentID和DirectoryID的常量字符串中驱动数据来改进它

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

我也有过类似的问题。我绕过了它,为每个版本使用了不同的install.dir,apppending dir名称和版本号,例如“install_V2.03”。好的,但是如果我有一些文件夹和子文件夹呢。我不知道在程序使用过程中创建了哪些文件和哪些子目录,我需要删除整个install dir,而不关心它的内容(因为它的内容是未知的)-我可以使用RemoveFile或RemoveFolder来执行此操作吗?您可以编写一个C#/DTF自定义操作,以获取子目录列表,并为每个子目录在MSI中创建一个临时行。然后,当标准动作触发时,它会清除所有内容。类似这样但不同的SQL表:您的意思是在卸载期间调用的自定义操作-它枚举要删除的所有剩余文件/目录并将检索到的数据传递到某处-在此之后,标准卸载操作将删除这些文件?似乎不那么容易,有没有什么例子?我应该使用什么sql表?是的,做正确的事情很复杂。更新了一些示例代码。谢谢,我会试试这个。这很奇怪-这么简单又这么复杂,我对msi很困惑:/
<CustomAction Id="actionCheckServerName" BinaryKey="actionBinary" DllEntry="CheckServerName" Execute="immediate" Return="check" />
        <CustomAction Id="actionInstall" BinaryKey="actionBinary" DllEntry="Install" Execute="deferred" HideTarget="no" Impersonate ="no" Return="check"/>
        <CustomAction Id="actionUninstall" BinaryKey="actionBinary" DllEntry="Uninstall" Execute="deferred" HideTarget="no" Impersonate ="no" Return="check"/>

        <CustomAction Id="actionRemoveOldDatabase" BinaryKey="actionBinary" DllEntry="RemoveOldDatabase" Execute="deferred" HideTarget="no" Impersonate ="no" Return="ignore"/>


        <CustomAction Id="actionGetNetworkComputers" BinaryKey="actionBinary" DllEntry="GetNetworkComputers" Execute="immediate" Return="check"/>

        <CustomAction Id="SetInstallParameters" Return="check" Property="actionInstall" Value="InstallDir=[INSTALLDIR];ServerName=[SERVER_LIST];InstallMode=[SETUP_MODE];Single=[single];RemoveDatabase=[REMOVE_DATABASE]" />
        <CustomAction Id="SetUninstallParameters" Return="check" Property="actionUninstsall" Value="UnInstallDir=[INSTALLDIR];ServerName=[SERVER_LIST];UnInstallMode=[INSTALL_MODE]" />

        <CustomAction Id="SetRemoveOldDatabaseParameters" Return="check" Property="actionRemoveOldDatabase" Value="InstallDir=[INSTALLDIR];RemoveDatabase=[REMOVE_DATABASE]" />


        <InstallExecuteSequence>
            <Custom Action='AlreadyUpdated' After='FindRelatedProducts'>SELFFOUND</Custom>
            <Custom Action='NoDowngrade' After='FindRelatedProducts'>NEWERFOUND</Custom>

            <Custom Action="SetRemoveOldDatabaseParameters" Before="ProcessComponents"/>
            <Custom Action="actionRemoveOldDatabase" After="SetRemoveOldDatabaseParameters">NOT Installed AND NOT UPGRADINGPRODUCTCODE</Custom>

            <Custom Action="SetInstallParameters" Before="actionInstall"/>
            <Custom Action="SetUninstallParameters" Before="RemoveFiles">Installed AND NOT REINSTALL AND NOT UPGRADINGPRODUCTCODE</Custom>
            <Custom Action="actionInstall" Before="InstallFinalize">NOT Installed AND NOT UPGRADINGPRODUCTCODE</Custom>
            <Custom Action="actionUninstall" After="SetUninstallParameters">Installed AND NOT REINSTALL AND NOT UPGRADINGPRODUCTCODE</Custom>
        </InstallExecuteSequence>
 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(); 
        }
    }