A little while back, I was looking into an odd scenario reported by a fellow Microsoft employee where they tried to install the final release of Visual Studio 2005, but it reported that the beta version of the .NET Framework 2.0 needed to be uninstalled first. When they tried to uninstall the .NET Framework 2.0 beta, the uninstall was blocked and displayed a dialog that looked like this:
Based on my previous knowledge working on setup for the .NET Framework 1.0 and 1.1, I knew this error would occur if the following registry value was set on the system:
- Key name: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft \.NETFramework\v2.0.50727\SBSDisabled
- Value name: Uninstall
- Data type: REG_DWORD
- Value data: 1
However, at the time that I investigated this issue, I had never seen that registry value being set on a system outside of some of the test cases I used to run when I tested .NET Framework setup. I dug into this issue further and found out that WinFX Runtime setup creates this value in order to prevent users from accidentally uninstalling the .NET Framework 2.0 out from under the rest of the WinFX runtime (because the WinFX runtime will not function properly without the .NET Framework 2.0 installed).
The interesting thing to me about this “feature” of WinFX runtime setup is that it uses logic that we built into .NET Framework setup back in the late stages of the .NET Framework 1.0 in a way that we never really intended or envisioned that it would be used back when we added it.
The .NET Framework has always been designed to support side-by-side installation, usage, and uninstallation. Towards the end of the .NET Framework 1.0 ship cycle, we started worrying that we may be missing some order-of-installation bugs that could cause install and uninstall of one version of the .NET Framework to break when in the presence of future versions. This type of scenario was very hard to test for back in the .NET Framework 1.0 timeframe because we did not have any other versions to use for side-by-side testing. As a result of these fears, we added what we called “hooks” and “blocks” to the .NET Framework 1.0 setup.
The “hooks” are a set of custom actions that run at the end installation and uninstallation and attempt to run an executable named netfxsbs10.exe. We did not ship a file with that name in the .NET Framework 1.0, but these custom actions provide us the option to ship a file with that name in future versions in case we find install/uninstall problems that we could not address purely with changes in setup for the later version. We did end up finding this type of issue later on for .NET Framework 1.0 setup, and if you install the .NET Framework 1.1 or 2.0, you will notice that it installs the file %windir%\Microsoft.NET\Framework\netfxsbs10.exe. That EXE will run if you install or uninstall the .NET Framework 1.0 after installing 1.1 or 2.0, and it will replace some registry data that will otherwise be deleted or incorrectly updated by 1.0 setup.
The “blocks” are a set of custom actions that run at the beginning of installation and uninstallation. They search for specific registry values and block the install or uninstall if the registry value exists on the system. There are separate registry values so that we could independently block install and/or uninstall if we found it necessary to do so in the future. These custom actions were intended at the time to be a last resort so that if we (for example) found an issue with .NET Framework 1.0 setup that was so bad that even the “hook” custom actions could not fix it, we could install these registry values in setup for later versions of the .NET Framework and prevent .NET 1.0 from being installed or uninstalled if the later version was already installed on the system.
We ended up carrying the hooks and blocks forward in setup for the .NET Framework 1.1 and 2.0, partially to retain this type of safety net, and partially because we figured out pretty early in the .NET Framework 1.1 product cycle that we needed to use the hooks that we built into .NET 1.0 setup and that humbled and scared us even more.
It appears that the WinFX setup team ended up finding out about these hooks and blocks and repurposed the blocks to prevent uninstall of the .NET Framework 2.0. It is a bit strange that we do not take measures like this to prevent users from uninstalling the .NET Framework out from under Visual Studio (because similar to WinFX, Visual Studio will not function properly without the matching build of the .NET Framework on the system). The main difference I can see between the WinFX Runtime scenario and the Visual Studio scenario is that WinFX is a runtime product as opposed to a developer tool, and therefore it is much more likely to be on non-developer systems. This makes it more appealing to add an extra safeguard against unintentional damage to this product.
All in all, I thought that this investigation was an interesting example of unintended consequences of an initial design. Although it seems to behave fine in the WinFX scenario that it is being used for, and it has encouraged me to be more conscious of documenting features like this so that they are not misused by future products.