卸载WiX时删除文件


84

卸载我的应用程序时,我想配置Wix设置以删除原始安装之后添加的所有文件。似乎卸载程序仅从MSI文件中删除了最初安装的目录和文件,而将以后添加的所有其他内容保留在应用程序文件夹中。换句话说,我想在卸载时清除目录。我怎么做?

Answers:


83

RemoveFile元素与On =“ uninstall ”一起使用。这是一个例子:

<Directory Id="CommonAppDataFolder" Name="CommonAppDataFolder">
  <Directory Id="MyAppFolder" Name="My">
    <Component Id="MyAppFolder" Guid="*">
      <CreateFolder />
      <RemoveFile Id="PurgeAppFolder" Name="*.*" On="uninstall" />
    </Component>
  </Directory>
</Directory>

更新资料

它没有100%起作用。它删除了文件,但是没有删除其他目录(安装后创建的目录)。有什么想法吗?–普里贝罗

不幸的是,Windows Installer不支持删除带有子目录的目录。在这种情况下,您必须诉诸自定义操作。或者,如果您知道什么是子文件夹,则创建一堆RemoveFolder和RemoveFile元素。


1
谢谢帕维尔。但是,它不能100%工作。它删除了文件,但是没有删除其他目录(安装后创建的目录)。有什么想法吗?
pribeiro

哦,这些目录下的文件都没有删除。
pribeiro

当您在重大升级中将文件(例如配置文件)保留在“ MyAppFolder”中时,您会遇到这种方法的问题。升级将删除所有文件。
西蒙

1
您的代码可以在MyAppFolder中创建目录吗?当我在</Component>编译失败后添加目录时Found orphaned Component 'MyAppFolder'.
A.Pissicat '16

2
@PhilipRego有关CommonAppDataFolder文档的信息,请参见msdn.microsoft.com/zh-cn/library/windows/desktop/aa367992.aspx
帕维尔·楚楚瓦

30

RemoveFolderEx在WiX中使用Util扩展中的元素。
使用这种方法,所有子目录也将被删除(与直接使用RemoveFileelement相反)。此元素添加临时行RemoveFileRemoveFolder表中的MSI数据库。


2
警告:在on =“ uninstall”上使用RemoveFolderEx时,它还会在升级(Wix 3.9)时删除该文件夹。RemoveFile和的 行为相同RemoveFolder。如果要保持文件升级,则不能使用所有这些方法。
西蒙

@Simon如果要保留文件升级,您会建议采用哪种方法?
京顿

@Bijington:如果要在升级时将特定文件保留在安装文件夹中,请使用执行自定义代码的自定义操作(例如,用C#编写的HandleSetup.exe)。定制代码在安装时提供文件,在升级时保留文件,在卸载时删除文件。
西蒙(Simon)

@Simon谢谢,尽管这种方法目前正在工作,但我可能不得不研究这种方法:stackoverflow.com/a/21383113/32348
Bijington

13

为此,我只是创建了一个自定义操作,以在卸载时调用。

WiX代码如下所示:

<Binary Id="InstallUtil" src="InstallUtilLib.dll" />

<CustomAction Id="DIRCA_TARGETDIR" Return="check" Execute="firstSequence" Property="TARGETDIR" Value="[ProgramFilesFolder][Manufacturer]\[ProductName]" />
<CustomAction Id="Uninstall" BinaryKey="InstallUtil" DllEntry="ManagedInstall" Execute="deferred" />
<CustomAction Id="UninstallSetProp" Property="Uninstall" Value="/installtype=notransaction /action=uninstall /LogFile= /targetDir=&quot;[TARGETDIR]\Bin&quot; &quot;[#InstallerCustomActionsDLL]&quot; &quot;[#InstallerCustomActionsDLLCONFIG]&quot;" />

<Directory Id="BinFolder" Name="Bin" >
    <Component Id="InstallerCustomActions" Guid="*">
        <File Id="InstallerCustomActionsDLL" Name="SetupCA.dll" LongName="InstallerCustomActions.dll" src="InstallerCustomActions.dll" Vital="yes" KeyPath="yes" DiskId="1" Compressed="no" />
        <File Id="InstallerCustomActionsDLLCONFIG" Name="SetupCA.con" LongName="InstallerCustomActions.dll.Config" src="InstallerCustomActions.dll.Config" Vital="yes" DiskId="1" />
    </Component>
</Directory>

<Feature Id="Complete" Level="1" ConfigurableDirectory="TARGETDIR">
    <ComponentRef Id="InstallerCustomActions" />
</Feature>

<InstallExecuteSequence>
    <Custom Action="UninstallSetProp" After="MsiUnpublishAssemblies">$InstallerCustomActions=2</Custom>
    <Custom Action="Uninstall" After="UninstallSetProp">$InstallerCustomActions=2</Custom>
</InstallExecuteSequence>

InstallerCustomActions.DLL中OnBeforeUninstall方法的代码将如下所示(在VB中)。

Protected Overrides Sub OnBeforeUninstall(ByVal savedState As System.Collections.IDictionary)
    MyBase.OnBeforeUninstall(savedState)

    Try
        Dim CommonAppData As String = Me.Context.Parameters("CommonAppData")
        If CommonAppData.StartsWith("\") And Not CommonAppData.StartsWith("\\") Then
            CommonAppData = "\" + CommonAppData
        End If
        Dim targetDir As String = Me.Context.Parameters("targetDir")
        If targetDir.StartsWith("\") And Not targetDir.StartsWith("\\") Then
            targetDir = "\" + targetDir
        End If

        DeleteFile("<filename.extension>", targetDir) 'delete from bin directory
        DeleteDirectory("*.*", "<DirectoryName>") 'delete any extra directories created by program
    Catch
    End Try
End Sub

Private Sub DeleteFile(ByVal searchPattern As String, ByVal deleteDir As String)
    Try
        For Each fileName As String In Directory.GetFiles(deleteDir, searchPattern)
            File.Delete(fileName)
        Next
    Catch
    End Try
End Sub

Private Sub DeleteDirectory(ByVal searchPattern As String, ByVal deleteDir As String)
    Try
        For Each dirName As String In Directory.GetDirectories(deleteDir, searchPattern)
            Directory.Delete(dirName)
        Next
    Catch
    End Try
End Sub

11

这是@tronda建议的一种变体。我要删除在卸载过程中由另一个“自定义操作”创建的文件“ install.log”:

<Product>
    <CustomAction Id="Cleanup_logfile" Directory="INSTALLFOLDER"
    ExeCommand="cmd /C &quot;del install.log&quot;"
    Execute="deferred" Return="ignore" HideTarget="no" Impersonate="no" />

    <InstallExecuteSequence>
      <Custom Action="Cleanup_logfile" Before="RemoveFiles" >
        REMOVE="ALL"
      </Custom>
    </InstallExecuteSequence>
</Product>

据我了解,我无法使用“ RemoveFile”,因为此文件是在安装后创建的,并且不属于组件组。


4
我确实使用了此解决方案,但进行了一些更改以删除整个目录:ExeCommand =“ cmd / C RD” [[INSTALLFOLDER]“ / s / q”
Dennis,

@Dennis如何删除INSTALLFOLDER,在win 10上将其删除,但在Windows Server 2012上则不会删除。
eomeroff '16

很好的解决方案。谢谢!
Yurii Komarnytskyi

我尝试了很多事情-不会认为在卸载过程中删除单个文件可能会那么困难。虽然这对我有用-谢谢!
幸运卢克

7

对于@Pavel建议,这将是一个更完整的答案,对我而言,它正在100%起作用:

<Fragment Id="FolderUninstall">
    <?define RegDir="SYSTEM\ControlSet001\services\[Manufacturer]:[ProductName]"?>
    <?define RegValueName="InstallDir"?>
    <Property Id="INSTALLFOLDER">
        <RegistrySearch Root="HKLM" Key="$(var.RegDir)" Type="raw" 
                  Id="APPLICATIONFOLDER_REGSEARCH" Name="$(var.RegValueName)" />
    </Property>

    <DirectoryRef Id='INSTALLFOLDER'>
        <Component Id="UninstallFolder" Guid="*">
            <CreateFolder Directory="INSTALLFOLDER"/>
            <util:RemoveFolderEx Property="INSTALLFOLDER" On="uninstall"/>
            <RemoveFolder Id="INSTALLFOLDER" On="uninstall"/>
            <RegistryValue Root="HKLM" Key="$(var.RegDir)" Name="$(var.RegValueName)" 
                    Type="string" Value="[INSTALLFOLDER]" KeyPath="yes"/>
        </Component>
    </DirectoryRef>
</Fragment>

并且,在“产品”元素下:

<Feature Id="Uninstall">
    <ComponentRef Id="UninstallFolder" Primary="yes"/>
</Feature>

此方法使用卸载时要删除的文件夹的所需路径设置注册表值。最后,将INSTALLFOLDER和注册表文件夹从系统中删除。请注意,注册表的路径可以在其他配置单元和其他位置。


6

不是WIX专家,但是对此的一种可能的(更简单的)解决方案是运行Quiet Execution Custom Action,它是WIX内置扩展的一部分?

可以使用/ S和/ Q选项运行rmdir MS DOS命令。

<Binary Id="CommandPrompt" SourceFile="C:\Windows\System32\cmd.exe" />

而执行此操作的自定义操作很简单:

<CustomAction Id="DeleteFolder" BinaryKey="CommandPrompt" 
              ExeCommand='/c rmdir /S /Q "[CommonAppDataFolder]MyAppFolder\PurgeAppFolder"' 
              Execute="immediate" Return="check" />

然后,您必须按照许多地方的说明修改InstallExecuteSequence。

更新: 这种方法有问题。最终做了一个自定义任务,但仍然认为这是一个可行的解决方案,但没有使细节起作用。


我喜欢这个选项栏,因为您将cmd.exe包含在安装程序中。当然,每台机器都会拥有它,您只需要使用DirectorySearch即可找到它!:)
Caveman_dick 2011年

4
不要这样 1)您正在嵌入cmd.exe到安装程序中。2)您正在脚本生成过程中对系统进行更改3)没有回滚选项4)无法正确处理锁定的文件
Nick Whaley 2015年

我怀疑从Windows安装分发文件是否合法。还不清楚它是否将在可能运行其他版本Windows的目标系统上运行。
Paul B.

没有文件已分发。它使用操作系统上安装的文件。
tronda
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.