In my last blog post I covered the most important options for propagating user settings in an MSI. These user settings can basically be registry keys or files. When one wants to distribute files to the different user profiles on a machine, a specific issue crops up, which is the reason for this post.
Let’s set the stage. If you have ever seen the dialog below when your package is repairing or self-healing for a user, you’re in the right place.
So, what’s going on here? Well, basically, Windows Installer is trying to place a file (or files) into the user’s profile and ran into a problem. It cannot find these files.
The reason why this happens is Windows Installer did not cache the file on the local machine. Whenever an MSI gets installed on a machine, Windows Installer places a version of the MSI in a local cache located at %WinDir%\Installer as a reference to use during later operations. However, this MSI is not the same as the original one, because all files get stripped out of it. So while this is fine for referencing registry keys, ini files (from the IniFile table) and other stuff that’s all in the msi tables, as soon as you try to write a file, Windows Installer will revert back to the original install location that you ran the MSI from.
In most deployment scenario’s today, installations are done from a central network location, using LocalSystem credentials. Normal users however, do not always have access to this central location, so when the scenario above arises, the original installer is out of reach. Windows Installer will then get cranky and demand you give it that original MSI (or else..)
REMARK: just thought I’d mention this. In an AD-based environment, it is sufficient to give the Domain Computers group read access to the network location that holds installation media to allow Windows Installer to reach it. Since this is not always possible/desirable though, there is another option: DuplicateFiles
So how do we tackle this problem? To me the most consistently robust and elegant solution is to use the DuplicateFile table in the MSI. The idea behind this technique is to put all files that are to go into the user profile into a local location that’s reachable to the application users (that would probably be the application’s Program Files folder..) and to create ‘copies’ of these central files that go into the profiles.
A quick peek at MSDN teaches us the DuplicateFile table has the following fields:
FileKey: A primary key, a non-localized token, identifying a unique DuplicateFile record
Component_: An external key to the first column of the Component table
File_: Foreign key into the File table representing the original file that is to be duplicated
DestName: Localizable name to be given to the duplicate file. If this field is blank, then the destination file is given the same name as the original file.
DestFolder: Name of a property that is the full path to where the duplicate file is to be copied
This table basically allows you to tell Windows Installer that one file in your package is actually a duplicate of another.
So step by step, the procedure for using this technique is as follows:
- In your package create a folder to hold the user-files (I like to call it “_user”). Make sure regular users can reach this folder.
- Put all the user-files in that folder (preferably in their own specific component)
- For each file, fill out a line in the DuplicateFile table, indicating the original userfile, the component that’s linked to it, if needed a new name for the file once it goes into the profile, and most notably, the destination (some path under [UserProfile])
The result is that next time Windows Installer wants to put those files into the profile, instead of getting annoyed when not finding the file in the cached version of the MSI, it will actually recognise the fact that it should just copy the file that it can find in the folder C:\Program Files\<Your Application>\_user to the correct path in the UserProfile, and do so obediently. Byebye annoying dialog!!
EDIT: After akki's reply to this post, I thought I'd add this to the main article: When using the DuplicateFile table, whether by editing it manually or some other IDE specific means, be sure to verify that the MSI's install sequence contains the DuplicateFiles and RemoveDuplicateFiles standard actions. Otherwise things won't work as expected.