How to add a WiX custom action that happens only on uninstall (via MSI)?

后端 未结 6 1286
一生所求
一生所求 2020-11-22 06:20

I would like to modify an MSI installer (created through WiX) to delete an entire directory on uninstall.

I understand the RemoveFile and RemoveFo

相关标签:
6条回答
  • 2020-11-22 06:51

    Here's a set of properties i made that feel more intuitive to use than the built in stuff. The conditions are based off of the truth table supplied above by ahmd0.

    <!-- truth table for installer varables (install vs uninstall vs repair vs upgrade) https://stackoverflow.com/a/17608049/1721136 -->
     <SetProperty Id="_INSTALL"   After="FindRelatedProducts" Value="1"><![CDATA[Installed="" AND PREVIOUSVERSIONSINSTALLED=""]]></SetProperty>
     <SetProperty Id="_UNINSTALL" After="FindRelatedProducts" Value="1"><![CDATA[PREVIOUSVERSIONSINSTALLED="" AND REMOVE="ALL"]]></SetProperty>
     <SetProperty Id="_CHANGE"    After="FindRelatedProducts" Value="1"><![CDATA[Installed<>"" AND REINSTALL="" AND PREVIOUSVERSIONSINSTALLED<>"" AND REMOVE=""]]></SetProperty>
     <SetProperty Id="_REPAIR"    After="FindRelatedProducts" Value="1"><![CDATA[REINSTALL<>""]]></SetProperty>
     <SetProperty Id="_UPGRADE"   After="FindRelatedProducts" Value="1"><![CDATA[PREVIOUSVERSIONSINSTALLED<>"" ]]></SetProperty>
    

    Here's some sample usage:

      <Custom Action="CaptureExistingLocalSettingsValues" After="InstallInitialize">NOT _UNINSTALL</Custom>
      <Custom Action="GetConfigXmlToPersistFromCmdLineArgs" After="InstallInitialize">_INSTALL OR _UPGRADE</Custom>
      <Custom Action="ForgetProperties" Before="InstallFinalize">_UNINSTALL OR _UPGRADE</Custom>
      <Custom Action="SetInstallCustomConfigSettingsArgs" Before="InstallCustomConfigSettings">NOT _UNINSTALL</Custom>
      <Custom Action="InstallCustomConfigSettings" Before="InstallFinalize">NOT _UNINSTALL</Custom>
    

    Issues:

    • UPGRADINGPRODUCTCODE is set during the RemoveExistingProducts action, so any custom actions that you run prior will not know it's an upgrade https://docs.microsoft.com/en-us/windows/desktop/Msi/upgradingproductcode
    0 讨论(0)
  • 2020-11-22 06:53

    I used Custom Action separately coded in C++ DLL and used the DLL to call appropriate function on Uninstalling using this syntax :

    <CustomAction Id="Uninstall" BinaryKey="Dll_Name" 
                  DllEntry="Function_Name" Execute="deferred" />
    

    Using the above code block, I was able to run any function defined in C++ DLL on uninstall. FYI, my uninstall function had code regarding Clearing current user data and registry entries.

    0 讨论(0)
  • 2020-11-22 06:55

    The biggest problem with a batch script is handling rollback when the user clicks cancel (or something goes wrong during your install). The correct way to handle this scenario is to create a CustomAction that adds temporary rows to the RemoveFiles table. That way the Windows Installer handles the rollback cases for you. It is insanely simpler when you see the solution.

    Anyway, to have an action only execute during uninstall add a Condition element with:

    REMOVE ~= "ALL"
    

    the ~= says compare case insensitive (even though I think ALL is always uppercaesd). See the MSI SDK documentation about Conditions Syntax for more information.

    PS: There has never been a case where I sat down and thought, "Oh, batch file would be a good solution in an installation package." Actually, finding an installation package that has a batch file in it would only encourage me to return the product for a refund.

    0 讨论(0)
  • 2020-11-22 07:01

    There are multiple problems with yaluna's answer, also property names are case sensitive, Installed is the correct spelling (INSTALLED will not work). The table above should've been this:

    enter image description here

    Also assuming a full repair & uninstall the actual values of properties could be:

    enter image description here

    The WiX Expression Syntax documentation says:

    In these expressions, you can use property names (remember that they are case sensitive).

    The properties are documented at the Windows Installer Guide (e.g. Installed)

    EDIT: Small correction to the first table; evidently "Uninstall" can also happen with just REMOVE being True.

    0 讨论(0)
  • 2020-11-22 07:08

    You can do this with a custom action. You can add a refrence to your custom action under <InstallExecuteSequence>:

    <InstallExecuteSequence>
    ...
      <Custom Action="FileCleaner" After='InstallFinalize'>
              Installed AND NOT UPGRADINGPRODUCTCODE</Custom>
    

    Then you will also have to define your Action under <Product>:

    <Product> 
    ...
      <CustomAction Id='FileCleaner' BinaryKey='FileCleanerEXE' 
                    ExeCommand='' Return='asyncNoWait'  />
    

    Where FileCleanerEXE is a binary (in my case a little c++ program that does the custom action) which is also defined under <Product>:

    <Product> 
    ...
      <Binary Id="FileCleanerEXE" SourceFile="path\to\fileCleaner.exe" />
    

    The real trick to this is the Installed AND NOT UPGRADINGPRODUCTCODE condition on the Custom Action, with out that your action will get run on every upgrade (since an upgrade is really an uninstall then reinstall). Which if you are deleting files is probably not want you want during upgrading.

    On a side note: I recommend going through the trouble of using something like C++ program to do the action, instead of a batch script because of the power and control it provides -- and you can prevent the "cmd prompt" window from flashing while your installer runs.

    0 讨论(0)
  • 2020-11-22 07:10

    EDIT: Perhaps look at the answer currently immediately below.


    This topic has been a headache for long time. I finally figured it out. There are some solutions online, but none of them really works. And of course there is no documentation. So in the chart below there are several properties that are suggested to use and the values they have for various installation scenarios:

    alt text

    So in my case I wanted a CA that will run only on uninstalls - not upgrades, not repairs or modifies. According to the table above I had to use

    <Custom Action='CA_ID' Before='other_CA_ID'>
            (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")</Custom>
    

    And it worked!

    0 讨论(0)
提交回复
热议问题