Common Task: Optionally Cleanup User Data on Uninstall
This is a common scenario... Your application, when it runs, creates log files, registry entries, and customized data files on the machine. These are files that were not installed by the installer. Therefore when you go to uninstall, these files would not normally be cleaned up.
It ought to be simple to add code to the installer to tackle removing these files. But... how do you handle it if an uninstall is happening because the user is upgrading? In that case, the user probably wants to KEEP those 'hanging chads'.
So you are left with a situation where you want the removal of items to be conditional - under some conditions remove them, under other conditions don't.
This gets tricky. I've worked up a solution to it. I'm not claiming it's ideal or anything.
Deleting Files and Folders
Before I begin, let me say that as I understand it the best practice is for an application NOT to create data files in its INSTALLDIR. Instead, files like this should go under a Documents and Settings folder either for the current user or under All Users. Further, my understanding is that you don't technically have to remove files under Doc n Settings. However, the requirements I'm usually given say "must remove all traces of product on uninstall". So having said all that...
For this example let's say that the product creates a folder called Logs, under INSTALLDIR. You'll need a Directory table entry for this path, such as INSTALLDIR_LOGS. To cause this folder to get removed on uninstall you add 2 entries to the RemoveFile table. One entry with FileName of *.* and DirProperty of INSTALLDIR_LOGS. This entry causes all files under Logs to get deleted. A second entry with FileName blank and DirProperty of INSTALLDIR_LOGS. This entry causes the (now empty) folder to get deleted. Both of these table entries should be associated with a component that will be uninstalled at uninstall time.
Now let's get tricky and make this conditional. Let's assume that by default you want to KEEP this folder. The reason to keep it by default is so that a silent uninstall (such as when upgrading) will keep the folder.
In the Directory table, create an entry for UNINSTALL_INSTALLDIR_LOGS. Directory_Parent is TempFolder. DefaultDir is ThisFolderShouldNotExist. Yeah, that's a weird name, but it is self-explanatory. We are setting the path to a path which we do NOT expect to actually exist on the target machine - a subdirectory of the Temp folder. The point being, we don't intend for any files to actually get removed at this path.
Go back to RemoveFile table and change the DirProperty for both entries so that they point to UNINSTALL_INSTALLDIR_LOGS.
Now.. when uninstall occurs, it looks in RemoveFile table and tries to delete TempFolder\ThisFolderShouldNotExist\*.*. This folder won't exist and there will not be any errors as a result of that. All is well.
Now we go to Custom Actions. Create a Set Directory custom action that will, based on a flag, set UNINSTALL_INSTALLDIR_LOGS to the REAL path where the log files exist. For example, my cusotm action is called Uninstall_SetLogsDir. The Directory value is [INSTALLDIR_LOGS]. This goes in the Install Exec sequence with a condition of FULLCLEANUP="yes".
So if the FULLCLEANUP property is set to "yes", then our fake path will get reset to the real path, and as a result the log files will get deleted.
Now all you need is a method of setting FULLCLEANUP to yes. In the Property table, create FULLCLEANUP and set it to no, by default. Using whatever method you want, cause a dialog to pop up during uninstall that asks the user if they want to do a full cleanup and if they say yes, then set FULLCLEANUP to yes.
NOTE: When the user uninstalls from Add/Remove Programs using the Remove button, it launches the uninstall with reduced UI, so any custom dialog you write will NOT appear. Therefore, you have to either A) disable the Remove button and force them to use Change button instead or B) display your full cleanup question dialog using some other method via a custom action (InstallScript?).
Deleting Registry Keys
You can apply the same basic methods to conditionally removing a registry key using the Property table (instead of the Directory table) and the Registry table (instead of the RemoveFile table).
Create a Property that holds the path to the registry key. By default set the Property to something that will not exist, for example, a Property called MyRegKey which is set to "Software\ThisKeyWillNotExist".
In the Registry table, create an entry for the key you want to conditionally remove. The fields for this entry are:
Key = "[MyRegKey]"
Name="-"
Value=""
And set the Component to whatever component makes sense. It is the minus sign in the Name column that tells MSI to remove the reg key on uninstall.
In the Custom Actions, create an action that will set the MyRegKey property to the *real* path when FULLCLEANUP="yes". The real path might be something like: "Software\[CompanyName]\[ProductName]\MyCustomRegKey".
NOTE: This only works with whole reg keys, you cannot remove a registry VALUE using this method. In fact, there is not a clean way to remove only a registry value, as far as I know. I find this odd, but true.
There ya go. It appears to work, and it isn't too clunky. Have fun!