Sunday, April 13, 2008

Windows Installer, I will defeat you: How to remove files on application uninstall

One man's quest to understand Windows Installer.

As I mentioned in my last post, I am working on a small project that I plan on releasing to the public in 2-3 months. I originally planned on deploying this project using ClickOnce, but that turned out to have a large set of challenges and is a topic for another post. I then changed my deployment strategy to make use of Visual Studio 2008's nice little deployment project builder. While even more challenging than ClickOnce, this strategy was far more flexible, so I've stuck with it. But I quickly found that Windows Installer is one serious beast.

I'll be the first to admit that I knew very little about application deployment before I started playing around with Windows Installer. I had used an older version of InstallShield back when I was a co-op at the company I'm currently employed at, but my experience with that was pretty limited.

But anyway - enough background. This post is entirely about one challenge with Windows Installer - removing files in your application's install directory when your app is uninstalled. I figured this would be pretty easy - and it *sort of* is, but just because something in the end is easy doesn't mean that the road to the end is easy as well. There is no way to configure this behavior from within VS2008's deployment project editor, so what was I to do?

Here is how I ended up solving the problem. First, install Orca. Orca is a small utility that Microsoft distributes with each platform SDK, and the setup can be found under the following path:

(typically Program Files)\Microsoft SDKs\Windows\v6.0A\bin\Orca.msi

Now, build your deployment solution (if you already haven't) from within VS2008. Run Orca. Open your built .msi file. On the left side of the Orca interface you'll see a list view labeled "Tables". Scroll down to 'Remove File'. Click on that, and on the right side of the interface, you'll see a dataview with the following column headers:

FileKey, Component, FileName, DirProperty, InstallMode

Your project most likely already has one entry. We'll use that entry as a mock-up for our file removal entries. This part is tricky - the MSDN documentation on RemoveFile is not particularly clear on how to do this, so I'll show you what I ended up doing.

To remove files in your application's directory, add an entry to the table with a unique name in the FileKey column. I called mine 'RemoveAppFiles'. For the Component column, copy and paste the original entry in the RemoveFile table's Component, since you can be pretty sure that component will be removed on uninstall. The trick to remember here is that an entry in the RemoveFile table will only be executed when the Component ID you use in the Component column is installed or uninstalled (more on this in the next paragraph).

For the FileName column, add a wildcard for the type of files you want removed - say, *.dll. The MSND documentation says any wildcard will work, but in practice, I've found that *.* or * do nothing, so you may end up needing several entries in the RemoveFile table for each file type you want removed. Under the DirProperty column, enter "TARGETDIR" if the files are in the application's directory - most people say to use "INSTALLDIR", but I've found that doesn't work either. And finally, for the InstallMode column, you can enter 3 possible numbers:

1 indicates that Windows Installer should remove this entry's files on component install.
2 indicates that Windows Installer should remove this entry's files on component uninstall.
3 indicates that Windows Installer should do both #1 and #2.

And one final note - if you want to remove the application directory, add yet another entry, but leave the FileName column empty. That tells Windows Installer to delete the directory.

So while this post didn't have anything to do with .NET, this was something that took me awhile to figure out, and if it helps anyone else out there, I'm glad. Good luck!


Unknown said...

You made my day with this post. I spent hours on this and only solved with your post. What good is all this installer table stuff if you can't find out how to add a row to a table? I don't understand why MS doesn't plug these holes sooner...this is a major one that wastes a lot of time.

Anyway - you helped me tremendously, even though this is a manual kludge that is difficult to accept as something I have to do every time I do a build!

Rob said...

I'm so glad to hear it helped! I know how frustrating it was for me - it wasted a ton of my time. Good luck with it, and thanks for the comment!