BadIdeaException
BadIdeaException

Reputation: 2124

WiX Toolset: ICE03 Invalid identifier with RemoveFolderEx

I'm trying to create a .msi installer for a Node.js project using the WiX Toolset. I want to remove the node_modules folder during uninstall. This is the relevant excerpt from my .wxs file:

<Property Id='APPLICATIONFOLDER'>
    <RegistrySearch Id='ApplicationFolder' Root='HKLM' Key='Software\[ProductName]' Name='InstallDir' Type='raw'/>
</Property>

<DirectoryRef Id='TARGETDIR'>
    <Component Id='RemoveFolder' Guid='3e2231d9-61b3-41a2-b407-8df015be537e'>
        <util:RemoveFolderEx Property='[APPLICATIONFOLDER]\node_modules' On='uninstall'/>
        <RegistryValue Root='HKLM' Key='Software\[ProductName]' Name='InstallDir' Type='string' Value='INSTALLDIR' KeyPath='yes'/>
    </Component>
</DirectoryRef>

I thought this would read the value from the registry, assign it to the property APPLICATIONFOLDER, and then in the RemoveFolderEx tag append the string \node_modules to that property's value.

But when I try to run this through light.exe, I get ICE03: Invalid identifier; Table: WixRemoveFolderEx, Column: Property, Key(s): wrf1A1445CB13E8BF98989EA24E3514470A.

Clearly, it is unhappy with my Property attribute, but what do I need to fix it? I've seen plenty of guides using this general pattern, but they always read the install dir value from the registry and then nuke that entire folder. That seems a little crass - if the user has added files to the directory I don't want to delete them. How can I specify a subfolder to delete?

Upvotes: 0

Views: 106

Answers (2)

BadIdeaException
BadIdeaException

Reputation: 2124

I've solved the problem. Because I personally had a hard time figuring it out - the guides I've seen on RemoveFolderEx only all cover the same use case of nuking a folder saved to the registry, not a subfolder; and I find the WiX documentation to be less than beginner-friendly in most cases -, I am recording the solution here in case someone else should ever stumble upon the same problem.

As was pointed out already, Property does not work in the way I presumed in my question - it really does just accept the id of a property holding the path to the directory to delete. So you will definitely need to set a property in some way. (Some guides on the internet do not make this very clear.)

The next obvious path to investigate would be to assign the property a modified result of the registry search. However, this is impossible with WiX (to the best of my knowledge).

The answer lies in SetProperty. This, finally, allows to set a new property based on the one read from the registry.

It is worth noting that doing this still prevents the installation folder from being deleted, even if it empty. This, however, can easily be remedied by adding a RemoveFolder (without the Ex). This will delete the folder, but only if it is empty.

The relevant code that got it working for me is below.

<!-- Read the InstallDir from the registry and assign it to property APPLICATIONFOLDER -->
<Property Id='APPLICATIONFOLDER'>
    <RegistrySearch 
        Id='ApplicationFolder' 
        Root='HKLM' 
        Key='Software\[ProductName]' 
        Name='InstallDir' 
        Type='directory' 
        Win64='yes'/>
</Property>
            
<!-- Set new property NODE_MODULES_FOLDER by appending node_modules\ to APPLICATIONFOLDER -->
<SetProperty 
    Id='NODE_MODULES_FOLDER' 
    Value='[APPLICATIONFOLDER]node_modules\' 
    Before='CostInitialize'/>

<Component Id='RemoveFolders' Guid='...'>
    <!-- Remove the node_modules subfolder, using the property we created above -->
    <util:RemoveFolderEx Property='NODE_MODULES_FOLDER' On='uninstall'/>
    <!-- Remove main installation directory now, but only if it is empty. -->
    <!-- That's why we're using RemoveFolder instead of RemoveFolderEx here. -->
    <RemoveFolder Id='RemoveMainFolder' Property='APPLICATIONFOLDER' On='uninstall'/>
    <RegistryValue 
        Root='HKLM' 
        Key='Software\[ProductName]' 
        Name='InstallDir' 
        Type='string' 
        Value='[INSTALLDIR]' 
        KeyPath='yes'/>
</Component>

Upvotes: 1

Bob Arnson
Bob Arnson

Reputation: 21886

I thought this would read the value from the registry, assign it to the property APPLICATIONFOLDER, and then in the RemoveFolderEx tag append the string \node_modules to that property's value.

It won't. As the doc says, Property is:

The id of a property that resolves to the full path of the source directory.

The easiest way to make that happen is to write the exact path you want to nuke to the registry.

Upvotes: 0

Related Questions