I recently needed to test the idea of whether or not I could bootstrap .NET v4.0 onto a WinXP machine, from a USB thumb drive, without an application installer for the target app. The goal was to insert a thumb drive, run the app directly from the root of the thumb drive and have .NET 4 installed onto the system if it wasn’t already there, prior to the application starting up.
The High Level Process
At a very high level, this is fairly simple:
- Check for .NET 4
- If not found, install
- If found, or after install, launch the real app
When I got down into the weeds of doing this, I found a few very helpful items on the interwebs. To check for a specific version of .NET, I found this StackOverflow question, which has an answer that outlines the various registry entries you can look for.
Framework Version Registry Key
1.1 HKLMSoftwareMicrosoftNET Framework SetupNDPv1.1.4322Install
2.0 HKLMSoftwareMicrosoftNET Framework SetupNDPv2.0.50727Install
3.0 HKLMSoftwareMicrosoftNET Framework SetupNDPv3.0SetupInstallSuccess
3.5 HKLMSoftwareMicrosoftNET Framework SetupNDPv3.5Install
4.0 Client Profile HKLMSoftwareMicrosoftNET Framework SetupNDPv4ClientInstall
4.0 Full Profile HKLMSoftwareMicrosoftNET Framework SetupNDPv4FullInstall
The answer goes on to detail other aspects, such as service packs, but this information was sufficient for my needs.
I also grabbed a copy of the stand-alone .NET 4 Redistributable package. To be honest, the less-than 50 meg package size is rather surprising – especially when you see the greater-than 230 meg package size for .NET 3.5!
One other requirement was to prevent the user from having to click a bunch of “Next” and “License Agreement” buttons during the .NET 4 installation. I had a hard time finding documentation for the command line options online, but running the /? option against the redistributable gave me the information I needed. I ended up using “/passive /showfinalerror /promptrestart” as the options. This puts the installer into passive mode, which still displays progress but skips all the user interaction, will display an error message window if any problems occur, and will prompt the user to restart if a restart is required.
Implementing The Bootstrapper
Remember that this is a proof of concept, only, at this point. Given that and my general lack of C++ or Delphi skills (though I was a Pascal programmer back in high school, 15 years ago), I wanted to skip past the hard part of putting together a real bootstrapper executable. So… I chose to go with Windows Script Host and VBScript.
Fortunately, the WSH / VBScript documentation on MSDN is good enough to get me rolling – I haven’t done VBScript since the classic ASP days, and haven’t done VB in any form since 2005! But it all came back to me and with the help of the docs, I was able to produce this script:
Dim WshShell, value
Set WshShell = WScript.CreateObject("WScript.Shell")
ON ERROR RESUME NEXT
Sub Run(ByVal sFile)
Set shell = CreateObject("WScript.Shell")
shell.Run sFile, 1, true
Set shell = Nothing
value = wshShell.RegRead("HKLMSoftwareMicrosoftNET Framework SetupNDPv4Install")
if (Err.Number <> 0) Or (value is nothing) then
Run("dotnetdotNetFx40_Full_x86_x64.exe /passive /showfinalerror /promptrestart")
Yes, that’s an “ON ERROR RESUME NEXT” in the 2nd line! ACK! I know… but we’re limited in VBScript / WSH. The reason this is needed, is the RegRead call. If the key does not exist (and it won’t, on a machine that doesn’t have .NET 4 installed), an exception is thrown. We have to prevent the exception from failing the script and this is the only way I know of to do that. We then check for the presence of an error and run the .net installer if one was encountered.
… I know… using exceptions as logic and flow control is taboo. It is just an prototype / proof of concept, though.
For a long-term solution, I would suggest C++ or Delphi. It tends to look bad when you deliver a product to a customer with a “runme.vbs” file sitting on the thumb drive. Anyone with an ounce of tech knowledge would question the legitimacy of that. This should get you started, though. And hopefully someone else will find this useful, and possibly re-write the core logic of what I’ve provided in a C++ or Delphi app that can run natively.