Visual Studio and .NET Framework SDK setup and the concept of "vertical integration"
Hey all, I was talking to someone earlier this week about ways to detect whether or not the .NET Framework SDK is installed on a user's machine. They had seen my blog entry about detecting the .NET Framework redist and were curious about whether or not a similar method exists for the SDK. As I was looking into it I stumbled upon a type of detection strategy that Visual Studio and the .NET Framework SDK use internally in their MSIs that I really wanted to explain in more detail because I haven't seen other setups do this or even use this term (plus now I can write a separate post about detecting the SDK and refer to this article for more details about one of the methods).
Before I start I wanted to define a couple of terms I'm going to use later on:
- Version - I will use this to refer to a specific family of products, for example - Visual Studio .NET 2002, Visual Studio .NET 2003
- Edition - I will use this to refer to one of the "flavors" of a product within a specific version; each edition represents one distinct product available in its own box at the store or via web download; for example - Visual Studio .NET 2002 Professional English, Visual Studio .NET 2003 Enterprise Developer German;
- SKU (or stock-keeping unit) - same as edition; this is the common name for products internally at Microsoft in my experience
The various editions of Visual Studio (such as Pro, Enterprise Developer, Enterprise Architect, etc) and the .NET Framework SDK ship many common components that can be installed to directories that the user can change in setup UI. For example - the core IDE bits are the same for all editions of Visual Studio (within the 2002 version family and 2003 version family respectively of course). Also, you can install the .NET Framework SDK tools, samples, etc by using the standalone SDK setup.exe or by choosing the .NET Framework SDK items in the Visual Studio setup selection tree. You are allowed to install any edition of Visual Studio and/or the .NET Framework SDK on the same machine, in any order, with uninstalls mixed in also.
In these multi-edition scenarios, Visual Studio and .NET Framework SDK setup do some internal detection to determine whether or not any of the common components are already installed on the user's machine. If they are, setup detects what path they are installed to, and automatically changes the install path in the setup UI for the feature(s) that contain the common component(s) to match the ones that were previously installed, and also prevents the user from changing these paths by graying out the browse button or via some other means.
We use the term vertical integration to encompass this process of performing detection, updating paths if necessary and blocking future changes to these paths in setup UI. Side note - I don't know for sure but it appears that we may have invented this term for this functionality on the VS setup team, I did some web searches and couldn't find any setup-related uses of this term (everything that came up for me was banking or financial analysis stuff).
Behind the scenes, vertical integration is implemented using some standard MSI constructs and in some cases some custom code in the Visual Studio external UI handler. Here is an outline of how things are implemented in the Visual Studio .NET 2003 family (which includes the .NET Framework SDK 1.1). I'm going to use the example of how we vertically integrate the .NET Framework SDK with Visual Studio .NET and refer to the MSI data for this, but there is similar data in the MSIs for other shared components as well. You can see all of this data by using the Orca tool in the MSI Platform SDK to open the vs_setup.msi or netfxsdk.msi and view the data:
- There is a vertical integration component in the Component table, in this case named VIntegration_dotNetSDK. It has a directory keypath and writes a registry value named [PRODUCTRELEASE] under HKLM\SOFTWARE\Microsoft\VisualStudio\SxS\FRAMEWORKSDK. In this case the value name evaluates to 7.1, and the value data is equal to the .NET Framework SDK folder path.
- The vertical integration component is included in the MSI for all editions of the product that share the bits that we want to vertically integrate.
- There is an entry in the AppSearch table that searches for the vertical integration component, in this case it is called FRAMEWORKSDK.3643236F_FC70_11D3_A536_0090278A1BB8_RO (note the RO on the end, which represents "read-only")
- The appsearch uses an entry in the CompLocator table to search for the component GUID of the vertical integration component in the component table.
- There is a type 35 custom action in the Custom Action table that sets the value of the install path for the component that we want to force to have the same path as the previously installed component. It uses the property set in the AppSearch table (which uses the signature defined in the CompLocator table). In this example, the custom actions CA_Vintegration_Init_FrameworkSDK and CA_Vintegration_Exec_FrameworkSDK change the path of the directory FRAMEWORKSDK.3643236F_FC70_11D3_A536_0090278A1BB8 to match the value data in the registry under HKLM\SOFTWARE\Microsoft\VisualStudio\SxS\FRAMEWORKSDK. This custom action makes sure that the path of the second and subsequent editions installed that have these shared, vertically integrated components will install to the same directory by default (even if the original installation directory is a non-default path updated by the user in setup UI)
- In the case of the .NET Framework SDK, we use standard Windows Installer UI. In this MSI there is an entry in the ControlCondition table that causes the Browse control on the InstallPoint dialog to be hidden if the appsearch finds the vertical integration component installed. This prevents the user from changing the install point in setup UI.
- In the case of Visual Studio, we use an external UI handler so we had to write custom code to prevent the user from changing the install point in setup UI. In a nutshell, what happens is that when Visual Studio setup is first launched, it opens vs_setup.msi and runs through the actions listed in the custom table named InitializationSequence. This will run all of the AppSearches and the type 35 custom actions to change paths for vertically integrated components. Then there is code that finds all directory tokens that have "_RO" on the end, and hides the browse button for the features displayed in the selection tree UI that they are associated with.
This vertical integration strategy has caused us some headaches in the past. In Visual Studio .NET 2002 and 2003 and the .NET Framework SDK 1.0 and 1.1, it is not transparent to the user that setup changes the default install paths on its own and prevents users from changing the paths of the second and subsequent editions that are installed on the machine. There are odd scenarios where you can install one of the MSDN Quarterly documentation sets and then if you try to install Visual Studio or the .NET Framework SDK you cannot change the path and have no idea why not. In many of the internal scenarios that I have seen, we have had to resort to using MSI API calls to enumerate installed products and then checking against a known list to determine what other product prevents the Visual Studio path from being changed because there is not any good logging to track this type of issue down.
The worst case I saw of this was caught before we shipped Visual Studio .NET 2003 - someone had installed Visual Studio .NET 2002 and an MSDN Quarterly, and then when they installed a beta of Visual Studio .NET 2003 the path was redirected onto the VS 2002 install location and the user could not change it. In this particular case, the user didn't even notice it and ended up destroying their copy of VS 2002 (which should ordinarily install side-by-side with VS 2003) because all shared files were upgraded to the 2003 version and VS 2002 could not launch with a mismatch of some 2002 and some 2003 files. This issue was caused by the MSDN Quarterly picking up AppSearch entries that looked for some shared components within VS 2002's MSIs and some shared components within VS 2003's MSIs. We did a couple of last-minute tweaks to the VS 2003 vertical integration detection logic to avoid that issue when VS 2003 shipped.
Fortunately, in Visual Studio 2005 (codename Whidbey), this has been improved greatly - there is now visual indication in the setup UI that the path is not allowed to be changed and we generate a message on the fly indicating what product(s) we detected that are causing this behavior.
I know this post is long and detailed but I wanted to give some insight into how one of the features of Visual Studio and .NET Framework SDK setup works and also help illustrate some of the complicated engineering problems that have to be solved by setup. As Rob Mensching says in the header of his blog, setup isn't just an xcopy (in most cases....) :-)