1

I feel like what I'm trying to do here is very basic. The equivalent in NSIS took about 30 seconds to accomplish and was well documented.

My experience with WiX so far has been looking at very old documentation and mailing list posts. I've spent around 5 hours trying to install a single exe with vc_redist 2015.

I need to remove the C:\Program Files\App directory on uninstallation and am trying to use util:RemoveFolderEx to do so. The Property attribute of it requires the path to delete. So far I've tried:

  • Raw directory. Not ideal, but would meet my client's needs for now.
  • INSTALLFOLDER
  • [INSTALLFOLDER]
  • ARPINSTALLLOCATION
  • [ARPINSTALLLOCATION]
  • InstallDir
  • .

After that I read that I need to set a property to [INSTALLFOLDER] to be able to use it elsewhere, so I did that with ARPINSTALLLOCATION. This still didn't work so I worked out how to get logs from the installer. It appears that the variable is getting set after RemoveFolderEx is called. Is there any way to reverse this or get the variable to be defined earlier?

So far I've tried changing the "After" property to a much earlier part of the process but it resulted in the variable never being set. I've also looked into just calling rmdir from a command prompt and the only thing I've been able to find is people saying "don't do it". I would also be open to calling RegistrySearch to grab the variable earlier if that's the only way. I just couldn't find any information on the internet about grabbing information from the Uninstall key when it's a GUID - or any information on how to move the rest of the information from the GUID key over to a Manufacturer/Product subkey

Relevant WXS snippet (the rest of it is essentially just the Visual Studio template):

<Product Id="$(var.ProductId)" Name="$(var.ProductName)" Language="1033" Version="$(var.ProductVersion)" Manufacturer="$(var.ProductManufacturer)" UpgradeCode="$(var.UpgradeCode)">
    <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />

    ...
    <SetProperty Id="ARPINSTALLLOCATION" Value="[INSTALLFOLDER]" After="CostFinalize" />

    <Feature Id="ProductFeature" Title="App Installer" Level="1">
        <ComponentGroupRef Id="ProductComponents" />
    </Feature>

</Product>

<Fragment>
    <Directory Id="TARGETDIR" Name="SourceDir">
        <Directory Id="$(var.PlatformProgramFilesFolder)">
            <Directory Id="INSTALLFOLDER" Name="App" />
        </Directory>
    </Directory>
</Fragment>

<Fragment>
    <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
        ...
        <Component Id="RemoveInstallDir" Guid="7B2FE25A-B0D8-4308-834D-66CCB1F5F229">
            <CreateFolder />
            <util:RemoveFolderEx On="uninstall" Property="ARPINSTALLLOCATION" />
        </Component>
    </ComponentGroup>
</Fragment>

And the relevant log sections with some context lines:

MSI (s) (7C:58) [16:45:13:049]: Invoking remote custom action. DLL: C:\WINDOWS\Installer\MSIFC67.tmp, Entrypoint: WixRemoveFoldersEx
MSI (s) (7C:68) [16:45:13:049]: Generating random cookie.
MSI (s) (7C:68) [16:45:13:050]: Created Custom Action Server with PID 24288 (0x5EE0).
MSI (s) (7C:50) [16:45:13:066]: Running as a service.
MSI (s) (7C:50) [16:45:13:067]: Hello, I'm your 32bit Impersonated custom action server.
Action start 16:45:13: WixRemoveFoldersEx.
WixRemoveFoldersEx:  Entering WixRemoveFoldersEx in C:\WINDOWS\Installer\MSIFC67.tmp, version 3.10.3007.0
WixRemoveFoldersEx:  Error 0x80070057: Missing folder property: ARPINSTALLLOCATION for row: wrfFACE66E9D000ACF7A46AACF6A7C5F32E
CustomAction WixRemoveFoldersEx returned actual error code 1603 but will be translated to success due to continue marking
MSI (s) (7C:68) [16:45:13:072]: Doing action: CostInitialize
MSI (s) (7C:68) [16:45:13:072]: Note: 1: 2205 2:  3: ActionText 
Action ended 16:45:13: WixRemoveFoldersEx. Return value 1.

...

Action start 16:45:13: CostFinalize.
MSI (s) (7C:68) [16:45:13:076]: Doing action: SetARPINSTALLLOCATION
MSI (s) (7C:68) [16:45:13:076]: Note: 1: 2205 2:  3: ActionText 
Action ended 16:45:13: CostFinalize. Return value 1.
MSI (s) (7C:68) [16:45:13:076]: PROPERTY CHANGE: Adding ARPINSTALLLOCATION property. Its value is 'C:\Program Files (x86)\Xearch\'.
Action start 16:45:13: SetARPINSTALLLOCATION.
MSI (s) (7C:68) [16:45:13:076]: Doing action: MigrateFeatureStates
MSI (s) (7C:68) [16:45:13:076]: Note: 1: 2205 2:  3: ActionText 
Action ended 16:45:13: SetARPINSTALLLOCATION. Return value 1.

1 Answer 1

3

I suspect you are assuming that the values of properties are somehow automatically preserved between install and uninstall, but they are not. That's why you see errors like "missing folder property". To preserve the values use the WiX "remember property" pattern. If INSTALLFOLDER corresponds to that location then that's the one to use.

Two asides:

  1. ARPINSTALLLOCATION is set so that APIs can locate the primary install folder, so you or anyone to retrieve it. You'd typically use MsiGetProductInfo or equivalent passing the ProductCode and INSTALLPROPERTY_INSTALLLOCATION.

  2. Normally folders get removed automatically. The underlying problem you're trying to solve seems to be that the folder (and maybe some files in it) is left behind. I think "why is this folder left behind" is the interesting question to ask here.

Sign up to request clarification or add additional context in comments.

3 Comments

I started looking into the remember property pattern earlier. All of the examples I found stored the key at HKLM/SOFTWARE/Microsoft/Windows/CurrentVersion/Uninstall/$(var.Manufacturer)/$(var.ProductName)/. I'd rather have everything be under a single registry key (and there's the default one which is the GUID of the product) but I can't find anything on how to reference it. Can I just do HKLM/SOFTWARE/Microsoft/Windows/CurrentVersion/Uninstall/$(var.ProductGuid)/ or is there some string manipulation involved?
In terms of the other question, I have some log files with dynamic names (in rare cases a subprocess can crash, I dump info to a file that contains the PID - this also allows me to search the main log for context). As I understand calling RemoveFile won't work very well for that.
I ended up just using the "remember property" pattern on a regular, separate registry key. My client wants the directory to be removed regardless of what contents are left (as opposed to making the future maintainer keep track of remaining files and add them to a list of files to remove on uninstall). I was hoping to reuse ARPINSTALLLOCATION in this case, but one extra registry key string shouldn't make a difference.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.