Eliminar archivos al desinstalar WiX

84

Al desinstalar mi aplicación, me gustaría configurar la instalación de Wix para eliminar todos los archivos que se agregaron después de la instalación original . Parece que el desinstalador elimina solo los directorios y archivos que se instalaron originalmente del archivo MSI y deja todo lo demás que se agregó más tarde en la carpeta de la aplicación. En otras palabras, me gustaría purgar el directorio al desinstalarlo. ¿Cómo puedo hacer eso?

pribeiro
fuente

Respuestas:

83

Utilice el elemento RemoveFile con On = " desinstalar ". He aquí un ejemplo:

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

Actualizar

No funcionó al 100%. Eliminó los archivos, sin embargo, no se eliminó ninguno de los directorios adicionales, los creados después de la instalación. ¿Alguna idea sobre eso? - pribeiro

Desafortunadamente, Windows Installer no admite la eliminación de directorios con subdirectorios. En este caso hay que recurrir a la acción personalizada. O, si sabe qué son las subcarpetas, cree un grupo de elementos RemoveFolder y RemoveFile.

Pavel Chuchuva
fuente
1
Gracias Pavel. Sin embargo, no funcionó al 100%. Eliminó los archivos, sin embargo, no se eliminó ninguno de los directorios adicionales, los creados después de la instalación. ¿Alguna idea sobre eso?
pribeiro
Oh, tampoco se borraron los archivos de esos directorios.
pribeiro
Cuando mantenga archivos (archivos de configuración, por ejemplo) en 'MyAppFolder' en actualizaciones importantes, tendrá problemas con este enfoque. Todos los archivos se eliminarán mediante una actualización.
Simon
1
¿Es posible con su código crear un directorio en MyAppFolder? Cuando agrego un directorio después de </Component>haber fallado una compilaciónFound orphaned Component 'MyAppFolder'.
A.Pissicat
2
@PhilipRego Consulte msdn.microsoft.com/en-us/library/windows/desktop/aa367992.aspx para obtener información sobre la documentación de CommonAppDataFolder.
Pavel Chuchuva
30

Utilice el RemoveFolderExelemento de la extensión Util en WiX.
Con este enfoque, también se eliminan todos los subdirectorios (en lugar de usar el RemoveFileelemento directamente ). Este elemento añade filas temporales para RemoveFileyRemoveFolder tabla en la base de datos de MSI.

Alexey Ivanov
fuente
2
Advertencia: Cuando usas RemoveFolderEx en = "desinstalar", también elimina la carpeta en una actualización (Wix 3.9). Mismo comportamiento en RemoveFiley RemoveFolder. Si desea mantener los archivos en una actualización, no puede utilizar todos estos enfoques.
Simon
@Simon ¿qué enfoque sugeriría si quisiera mantener los archivos en una actualización?
Bijington
@Bijington: cuando desee mantener archivos específicos en su carpeta de instalación durante una actualización, utilice una acción personalizada que ejecute código personalizado (p. Ej., HandleSetup.exe escrito en c #). El código personalizado entrega sus archivos en la instalación, mantiene sus archivos en actualización y elimina los archivos en la desinstalación.
Simon
@Simon gracias, es posible que tenga que analizar ese enfoque, aunque este está funcionando actualmente: stackoverflow.com/a/21383113/32348
Bijington
13

Para hacer esto, simplemente creé una acción personalizada que se llamará al desinstalar.

El código de WiX se verá así:

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

El código para el método OnBeforeUninstall en InstallerCustomActions.DLL se verá así (en 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
Amigo de jorge
fuente
11

Aquí hay una variación de la sugerencia de @ tronda. Estoy eliminando un archivo "install.log" que se crea mediante otra acción personalizada durante la desinstalación:

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

Por lo que tengo entendido, no puedo usar "RemoveFile" porque este archivo se crea después de la instalación y no forma parte de un grupo de componentes.

Pierre
fuente
4
Usé esta solución, con algunos cambios para eliminar todo el directorio: ExeCommand = "cmd / C RD & quot; [INSTALLFOLDER] & quot; / s / q"
Dennis
@Dennis cómo eliminar INSTALLFOLDER, en win 10 se elimina pero en Windows Server 2012 no.
eomeroff
Gran solucion ¡Gracias!
Yurii Komarnytskyi
Probé varias cosas; uno no pensaría que podría ser tan difícil eliminar un solo archivo durante la desinstalación. Sin embargo, esto funcionó para mí, ¡gracias!
Lucky Luke
7

Esta sería una respuesta más completa para la sugerencia de @Pavel , para mí está funcionando al 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>

Y, en el elemento Producto:

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

Este enfoque establece un valor de registro con la ruta deseada de la carpeta que se eliminará en la desinstalación. Al final, tanto INSTALLFOLDER como la carpeta de registro se eliminan del sistema. Tenga en cuenta que la ruta al registro puede estar en otras colmenas y otras ubicaciones.

Eli
fuente
6

No soy un experto en WIX, pero ¿podría una solución posible (¿más simple?) Para esto ejecutar la acción personalizada de ejecución silenciosa que es parte de las extensiones integradas de WIX?

Podría ejecutar el comando rmdir MS DOS con las opciones / S y / Q.

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

Y la acción personalizada que hace el trabajo es simple:

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

Luego, tendrá que modificar InstallExecuteSequence como se documenta en muchos lugares.

Actualización: tuve problemas con este enfoque. Terminó haciendo una tarea personalizada en su lugar, pero aún considera que esta es una solución viable, pero sin que los detalles funcionen.

tronda
fuente
Me gusta esta opción, excepto por el hecho de que está incluyendo cmd.exe en el instalador. Seguramente todas las máquinas lo tendrán, ¡solo necesita usar un DirectorySearch para encontrarlo! :)
caveman_dick
4
No hagas esto. 1) está incrustando cmd.exeen su instalador. 2) Está realizando cambios en el sistema durante la generación del script 3) No hay opción de reversión 4) No maneja los archivos bloqueados correctamente
Nick Whaley
Tengo dudas de que sea legal distribuir un archivo desde la instalación de Windows. Tampoco está claro si funcionará en el sistema de destino que puede ejecutar una versión diferente de Windows.
Paul B.
No se han distribuido archivos. Utiliza los archivos instalados en el sistema operativo.
tronda