When generating an MSI installer with Visual Studio 2005 through a Web Setup Project, there is no possibility to specify the physical path of the Virtual Directory that is created. The location defaults to a subdirectory below the root of the website (typically, this home directory is 'C:\Inetpub\wwwroot').
The only fields provided by the Installation Address dialog box are 'Site' and 'Virtual Directory':
I decided to enhance this dialog box so that it has the option to specify a different physical location for the Virtual Directory.
The easiest way to accomplish this would be to create a regular Setup Project in Visual Studio, and then implement a custom Installer class that creates the Virtual Directory in the proper location (by passing it the
TARGETDIR from the Setup Project). Finally, I would then add the Installer class as a Custom Action in the Setup Project and compile it.
However, there were two reasons why I decided to implement this functionality in a Web Setup Project instead:
- I already spent a considerable amount of time creating the Web Setup Project, and I didn't want to start all over again.
- I did not want to lose the option to select a website, or implement this in a regular Setup Project.
The implementation cannot be carried out solely by using Visual Studio. These are the steps that needed to be taken:
- Create the MSI by compiling the Web Setup Project in Visual Studio 2005.
- Use Orca.exe (from the Microsoft Platform SDK) to extend the Installation Address dialog box.
- Save the extension as an MSI transform and then apply this transform to the original MSI file.
The compilation of the Web Setup Project in Visual Studio is easy. Let's elaborate a bit on the last two steps.
Modifying the MSI with Orca.exe
Orca.exe provides a graphical user interface which lets you edit Windows Installer tables directly. It is part of the Microsoft Platform SDK for Windows 2003 Server R2. First, install the SDK, then run Orca.msi which is in the /Bin directory of the SDK.
Start Orca.exe. Before editing any tables, make sure to mark the checkbox 'Copy embedded streams during 'Save As'' in 'Tools > Options > Database'. If you forget this step, you will get a nasty "Error 2356: Couldn't locate cabinet in stream" error when running your modified MSI.
In Orca, you'll need to add some rows to the 'Control' and 'ControlEvent' tables and make some slight modifications to existing rows. The changes then need to be saved as an MSI Transform file, so that they can be easily re-applied to any subsequent MSIs that are built with Visual Studio.
First, select 'Transform > New Transform' from the menu, to start recording changes for our Tranform. Now, let's start with the modifications to the Control table. The dialog we want to modify is named 'WebFolderForm', and we want to add a Label, a TextBox, and a Browse button to it. This is achieved by adding the following three rows to the table (Ctrl+R):
(Note: Due to size restrictions, I split all table screenshots in two parts.)
The only other modification in this table should be to change the 'Control Next' cell of the row that specifies the VDirEdit textbox. Change the value from 'Cancel' to 'InstallLabel':
These changes causes the Installation Address dialog box to look like this:
The Browse button is not yet functional. For that, we need to modify the ControlEvent table. Add the following two rows to this table:
The SelectFolderDialog is already available in the MSI database. The above two rows make sure that the Browse button fires up this dialog and that the selected folder-location is stored in the
TARGETDIR property. These are all modifications. Now, save the Transform to disk by selecting 'Transform > Close Transform'. Orca saves a file with a .mst extension that can be used in future to extend MSI packages that are created by Visual Studio.
It is very easy to apply a Transform to an MSI database. Just open the MSI with Orca, then apply the transformation by choosing 'Transform > Apply Transform...' from the menu. To finish, save the modified MSI by choosing 'File > Save Transformed As...'. To avoid some problems, it would be a good idea to specify 'Transform Properties...' from the 'Transform' menu before saving the transformed MSI. Check the following three checkboxes: 'Same Language', 'Same Product Code', and 'Same Upgrade Code'.
The contents of the MSI tables is generated by Visual Studio when creating the installer. There might be some variations in the generated code which require additional steps to make the solution work properly.
Visual Studio may insert a couple of Custom Actions, which reset the
TARGETDIR to its original location. They are run when clicking the 'Next' button on the WebFolderForm. If this is the case, you'll see the following row in the ControlEvent table:
This specifies that the Custom Action
WEBCA_EvaluateURLsMB should be run on clicking 'Next'. In the 'Ordering' column, this action has a higher order number than
SetTargetPath which sets the
TARGETDIR. Changing the ordering, however, is not sufficient, because in the
InstallExecuteSequence, there is a call for the Custom Action
WEBCA_EvaluateURLs, and in the
InstallUISequence table finally resides the
WEBCA_EvaluateURLsNoFail Custom Action. Removing these from all three tables will fix the problem. Another possibility is to remove these actions altogether by removing the corresponding rows from the
I am not sure what the function of these Custom Actions is, since they are undocumented. They are inserted by Visual Studio in a binary stream in the MSI named
MSVBDPCADLL (contains MSVBDPCA.DLL). Removing them did not negatively influence the installation process as far as I could see.
Specify the default installation directory
After the removal of the Custom Actions, the default installation directory is no longer initialized to a path below your web server. It is easy to install to a directory like [ProgramFilesFolder][Manufacturer]\[ProductName] by adding a couple of extra rows to the MSI (which is the default for regular Setup projects).
Add this row to the CustomAction table:
and then the following rows to both the InstallExecuteSequence and InstallUISequence tables:
The last column in this row contains the installation sequence, which is sequenced to occur just before the