Thoughts about setup and deployment issues, WiX, XNA, the .NET Framework and Visual Studio
All postings are provided AS IS with no warranties, and confer no rights. Additionally, views expressed herein are my own and not those of my employer, Microsoft.
Hey all, as I promised a while back, I wanted to walk through some examples of how I approach reverse engineering a setup package to learn how it works, make any necessary changes, figure out command line switches, etc. I apologize for not posting anything in a while, I was spending some time with my family who was in town from Texas and then I managed to get sick right after they left. But I'm back now :-)
I'm going to start with a relatively straight-forward reverse engineering example - the .NET Framework 1.1 package. I chose this first because that setup is very simple - no setup data files, no configurable setup UI options, etc. Yet it is complicated enough to be interesting and it is a setup that a lot of people want to include in their setup packages and/or have had issues trying to install. I'm going to try to approach this from the perspective of someone who is familiar with different types of setup technologies in a broad sense, but who has not yet seen this particular setup before (I may get a little off track on this part because I'm so familiar with this setup from my previous work, but I'll try.....)
Step 1 - figure out what packaging technology is being used
When I download the .NET Framework 1.1 I see that it is a single EXE named dotnetfx.exe. When I double-click on it, setup asks me if I want to install and when I say Yes it proceeds to extract files to the %temp% folder on my machine. Then a EULA appears. When I go to %temp% after the EULA appears, I see a folder named ixp000.tmp. This tells me that the package in question is an IExpress self-extracting EXE. Since I know it is IExpress, I can now use some well-known IExpress command lines to extract the package to a folder of my choosing. I will run dotnetfx.exe /t:c:\aaron /c to create a folder named c:\aaron on my machine and unpack the .NET Framework files to this folder. The /t flag specifies the folder to extract to, and the /c flag specifies that I want to extract the files only and not run the setup after extraction.
Side note for setup developers - I found some IExpress docs on the web here, and you can also find iexpress.exe in %windir%\system32 on an XP Professional machine. It is a useful tool for packaging up multiple files into a single package but it is also a fairly old technology and has some limitations that may not make it practical to use, depending on your scenario.
Step 2 - figure out what setup/installation technology is being used
Now that I have extracted the files, I will go to the c:\aaron folder that I created above.
Step 3 - figure out the basics of how the setup works
After I look at all of this, I go ahead and step through the UI and install the .NET Framework 1.1, and then re-open the log file dotnetfx.log. I see that it lists the command line that is passed to the MSIInstallProduct function - and I know that this is an API call for an application to install a product using Windows Installer. Then I see a return code for that API call - fortunately it is 0 in this case to tell me that the .NET Framework 1.1 installed correctly. Then I see another call to StopDarwinService, so this install.exe application is stopping the Windows Installer service again after installation is complete - kind of strange.
Step 4 - figure out details about how the setup works
Now that I have a basic understanding of how the .NET Framework 1.1 setup works, I'm going to use my Windows Installer expertise to dig a little deeper. The first thing I will do is install Orca, then right-click on netfx.msi and choose Edit with Orca to view the contents of the MSI. There is a ton of data to wade through in an MSI and it is overwhelming at first, so I will only look at a few key things here:
There are many more things that we could look at in an MSI, but the above are good starting points. Often for other setups you will see items in tables that will lead you on trails through other tables. Most commonly, if there are launch conditions that depend on the existence of other applications, you will be led to the AppSearch table to figure out exactly what file or registry data the setup is looking for when deciding whether or not to block.
Step 5 - install with verbose logging and look at the MSI log file
Reading an MSI log file is more of an art than a science but can often be useful when reverse engineering a setup. For the .NET Framework 1.1, we know that because it is an MSI we can set the verbose logging flags before running setup - HKLM\Software\Policies\Microsoft\Windows\Installer, Debug (REG_DWORD = 7) and Logging (REG_SZ = voicewarmup!). These will give us a verbose log named msi*.log in %temp% where * is a randomly generated set of characters.
I also noticed when running install.exe /? it says that if you pass a /l flag it will generate netfx.log in %temp%. Since I also noticed that the log created by running install.exe was named dotnetfx.log and not netfx.log, I might be curious what the difference is and run install.exe /l from c:\aaron to see what it does. When I install this way I see that the resulting file %temp%\netfx.log is essentially the equivalent of a Windows Installer verbose log file without needing those registry keys to be set. So now I can take a look at netfx.log (or msi*.log if I choose) and view the step-by-step process of this setup.
With that, the process of reverse engineering the .NET Framework 1.1 setup is done for the common cases. There is of course more detail that may be needed depending on your scenarios, but much of that will require more in-depth analysis of the MSI tables and/or the verbose MSI log files. I will leave that for a later lesson.
Side note for the curious - the reason that the .NET Framework 1.1 setup stops the Windows Installer service is because Windows Installer uses pieces of the .NET Framework to install assemblies to the GAC (the MSIAssembly and MSIAssemblyName tables in the MSI) in Windows Installer 2.0 and higher. This creates a classic chicken-n-egg problem - Windows Installer is going to install the .NET Framework but Windows Installer needs the .NET Framework in order to do so. Because of that, Windows Installer has some special logic to bootstrap the part of the .NET Framework it needs to install assemblies, but since Windows Installer is run as a service, it could already be running from a previous setup and we want to stop the service to unload any older versions of the .NET Framework from memory so that the most recent version will be used.
I hope this post is at least a little bit useful to give some insight into how I approach the process of taking apart a setup and figuring out how it works. I will post more later with a more complicated example of a setup that uses information from data files, etc. Let me know via comments or emails if you have any questions about any of the above.....
Hi
I have a requirement where I want to configure the Target Directory for the MSI being installed. I need to set it when the MSI is actually being installed. The Target directory path is stored in the registry.
Is there some vb script which reads the Target Directory path from the registry and sets it as a parameter for the Installing MSI? Can the script can be added as a Custom Action -> Install?
Thanks
Kaushik
Hi Kaushik_aj - You do not need a custom action to achieve this. You can use a RegLocator to check the registry value and then use it to set a property that is used for the target directory for your application.
Having researched this problem for many hours, your blog has yielded the best advice and hints. Problem: We have many Panasonic Toughbooks which were built on a 2005 corporate Image. We have Net 1.1 Hotfix 1 loaded, XP SP2. We have a couple of new programs which have had problem running under .net. The system will not load Net SP1. After we uninstall everything and starting over we are completely blocked. Where we are now is that we have tried all of the suggestions on the web but none have worked. We consistently get the "1935" error. Loading various diagnostic tools and creating logs have pointed to a file that can not be found: "netfxsbs11.exe". There is a netfxsbs10.exe, which resides in the Framework folder, but the former is nowhere to be found. I suspect it may be part of the "mscoree.dll" file. The ORCA dump has a call for a CA Patch uninstall and install of the netfxsbs11.exe in the Custom Patch table. The "dotnetfx.exe" we are using is 1.1.4322.573 1/14/2002.
If you have any ideas we would be very grateful.
JC
Hi JCOKE - You don't have to worry about the missing netfxsbs11.exe. This is a hook that we put in place in case we needed it later, and we haven't needed it yet, so that action will just result in a no-op.
I would suggest trying to fully clean off the .NET Framework 1.1 using the cleanup tool described at http://blogs.msdn.com/astebner/archive/2006/05/30/611355.aspx and then attempt to re-install it.
If that doesn't help, please use the steps listed at http://blogs.msdn.com/astebner/archive/2005/03/29/help-me-help-you-if-you-have-setup-bugs.aspx to gather a verbose log from the .NET Framework installation that is failing and zip and send it to me so I can take a further look. You can send it to Aaron.Stebner (at) microsoft (dot) com.