Welcome to MSDN Blogs Sign in | Join | Help

Everyone in the Windows Embedded CE community, I would like to say hello from the Setup team! I have recently joined the CE team and have been able to get my feet wet with the CE6 R3 release. It is my goal to help improve the developer’s experience around the setup process. This is going to be even more important with the next full release of our product. After reading a few comments on Mike Hall’s blog I noticed some people already having problems with Setup in one shape or another. I would like to get some general feedback for your current issues with the CE6 installation at this point.

Help me help you!

As most of you already know, the Windows Installer XML Toolset (WiX) is made to flexible. Most of the tools have the ability to be extended by using your own custom assembly. This is great when you have a custom action, specialized UI, preprocessor or anything else you might need in order to get your product out the door. There is documentation in the WiX help file on how to get going with other extensions but I noticed Heat didn’t have anything of the sort. Here is my stab at getting all of you developers up and running.

Architecture

The first point to know about Heat would be around the architecture. The entire engine is written to be extensible. The core, which contains one harvester, has its extension set during the command line switch parsing.

Heat Class Diagram

Heat loads all the extensions from the heat.exe.config file and then parses the command line for additional extensions. Each extension is passed the command line switches that were given to Heat so they can determine if they should become the active harvestor extension.

Extension Loading Order

Getting Started

The examples I will be using are going to be via C#. Create your new extension project using the language of your choice. This should be a Class Library project. I have decided to use the .NET 2.0 Framework to keep in line with the Windows Installer XML Toolset (WiX) and to keep from requiring a higher version of .NET on the machine (this is your choice however).

Create Your Project

Setting Up The Project

Project DependenciesNow that you have created your project you will need to add a few dependencies. The main one is to the wix.dll which can be found in the same directory as Heat.exe. If you are looking to write your own directory harvester you will want to add a reference to the WixUtilExtension.dll if you don’t want to rewrite all the work done to harvest an individual file. The FileHarvester has some great functionality around harvesting self-registering files.

Now that your project has its core dependencies added it is time to create a shell class that will become your harvester extension. For my example I will be calling my extension "MyCustomExtension".

namespace MyHeatExtension
{
    using System;
    using System.Collections.Generic;

    using Microsoft.Tools.WindowsInstallerXml.Tools;

    public class MyCustomExtension : HeatExtension
    {
    }
}

Next you will need to add a reference to your extension in the AssemblyInfo.cs file. This helps the extension loader to find out which class in your Assembly should be treated as your harvester.

using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

using MyHeatExtension;
using Microsoft.Tools.WindowsInstallerXml.Tools; [assembly: AssemblyTitle("MyCustomExtension Heat Extension")] [assembly: AssemblyDescription("This is an extension to heat.exe which is part of the WIX Toolset.")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("MyCompany")] [assembly: AssemblyProduct("MyHeatExtension")] [assembly: AssemblyCopyright("Copyright © MyCompany 2009")] [assembly: AssemblyCulture("en-us")] [assembly: AssemblyVersion("0.5.0.0")] [assembly: AssemblyFileVersion("0.5.0.0")] [assembly: ComVisible(false)]
[assembly: AssemblyDefaultHeatExtension(typeof(MyCustomExtension))]

Overrides

At this point I like to do a build on the project to make sure everything is working as expected. Once your compile has worked and your assembly built it is time to start coding. Going back to class diagram and looking at the HeatExtension class there are a couple exposed properties and methods. Two of these are pinnacle to our successful create of the extension; CommandLineTypes and ParseOptions.

CommandLineTypes

/// <SUMMARY>
/// Gets the supported command line types for this extension.
/// </SUMMARY>
/// <VALUE>The supported command line types for this extension.</VALUE>
public override HeatCommandLineOption[] CommandLineTypes
{
    get
    {
        return new HeatCommandLineOption[]
        {
            new HeatCommandLineOption("mydirext", "harvest a directory my way")
        };
    }
}

ParseOptions

/// <SUMMARY>
/// Parse the command line options for this extension.
/// </SUMMARY>
/// <PARAM name="type">The active harvester type.</PARAM>
/// <PARAM name="args">The option arguments.</PARAM>
public override void ParseOptions(string type, string[] args)
{
    bool activated = false;

    if (String.Equals(type, "mydirext", StringComparison.OrdinalIgnoreCase))
    {
        this.Core.Harvester.Extension = new MyHarvesterExtension();
        activated = true;
    }

    if (activated)
    {
        //TODO: Parse the command line if needed and add mutators if needed.
    }
}

Summary

You are now at the point to start diving in and looking into what it takes to actually make your HarvesterExtension and MutatorExtension if you need it. As my schedule allows I plan on adding other parts to this to help in the creation of a HarvesterExtension and MutatorExtension. I realize that much of this is undocumented so I hope that you find these blogs of use.

As always, comments and feedback welcome.

Over the years I have seen this question come up a few times. Although there are some good examples inside the source code of the Windows Installer XML Toolset (WiX) itself, it can be hard to find. The question is “how do I read data from a MSI Binary table?” The easiest way I have found is to use the Windows Installer XML Toolset (WiX) and some of the great libraries that ship with it. If you haven’t taken a look at them yet, start scanning the WcaUtil and DUtil libraries for some really great APIs that will make your life much less complex.

const UINT COST_BINARY_READ_TRANSACTION = 10000;
//Extract the Binary Id to a memory stream as an immediate custom action.
//
__declspec(dllexport)
UINT __stdcall GetMyBinaryData(MSIHANDLE hInstall)
{
    HRESULT hr = S_OK;
    UINT er = ERROR_SUCCESS;
    LPWSTR pwzStreamToString = NULL;
    BYTE* pbData = NULL;
    DWORD cbData = 0;

    hr = WcaInitialize(hInstall, "GetMyBinaryData");
    ExitOnFailure(hr, "failed to initialize GetMyBinaryData");

    //Extact the Id in the Binary table to a stream.
    hr = 
ExtractBinary
(L"MyBinaryTableId", &pbData, &cbData); ExitOnFailure(hr, "failed to extract binary data"); //Write the stream to a string to be passed as CustomActionData hr =
WcaWriteStreamToCaData
(pbData, cbData, &pwzStreamToString); ExitOnFailure(hr, "failed to write the stream to a string"); hr = WcaDoDeferredAction(L"IAlwaysCodeForRollbackToHandleFailures", L"Rollback", 0); ExitOnFailure(hr, "Failed to schedule IAlwaysCodeForRollbackToHandleFailures"); //Send it over to a deferred custom action if you need to do "special" things with it hr =
WcaDoDeferredAction(L"IAmUsingThisAsAMemoryStream", pwzStreamToString, COST_BINARY_READ_TRANSACTION)
; ExitOnFailure(hr, "Failed to schedule IAmUsingThisAsAMemoryStream"); LExit: ReleaseStr(pwzStreamToString); ReleaseMem(pbData); if (FAILED(hr)) { er = ERROR_INSTALL_FAILURE; } return WcaFinalize(er); }

Next, as the custom action reads, we will be opening this in a deferred action as a memory stream.

//Read the stream from the CustomActionData property.
__declspec(dllexport)
UINT __stdcall 
IAmUsingThisAsAMemoryStream
(MSIHANDLE hInstall) { HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; LPWSTR pwzCustomActionData = NULL; LPBYTE pbData = NULL; DWORD_PTR cbData = 0; hr = WcaInitialize(hInstall, "IAmUsingThisAsAMemoryStream"); ExitOnFailure(hr, "failed to initialize IAmUsingThisAsAMemoryStream"); hr =
WcaGetProperty
(L"CustomActionData", &pwzCustomActionData); ExitOnFailure(hr, "failed to get CustomActionData"); //Get the stream for the custom action data. hr =
WcaReadStreamFromCaData
(&pwzCustomActionData, &pbData, &cbData); ExitOnFailure(hr, "failed to read file contents from custom action data"); LExit: ReleaseStr(pwzCustomActionData); ReleaseMem(pbData); if (FAILED(hr)) { er = ERROR_INSTALL_FAILURE; } return WcaFinalize(er); }

There is a quick and very dirty example on how to read from a Binary table. There are easy ways of abusing this which you, the developer, should avoid at all cost. Writing the streams to disk and leaving them there is one of the most common I have seen.

With the Windows Installer XML Toolset (WiX) v3.0 on the verge of hitting production more and more people are starting to take a look at it and see what new, cool tools we have. One, of many, new ones is Heat.exe. This tool is the replacement (in many ways) to tallow.exe from the previous release. The tool had a complete rewrite and is able to offer some great new functionality.

As with every new tool, once someone gets a hold of it they want it to do more. In the case I don’t disagree with them.

  • Was Heat designed to help automate the creation of WiX source? Yes.
  • Was the intention to have Heat gather thousands of files quickly for Setup authors? Yes.
  • Is Heat ready to be placed into an automated build system and generate patch compliant MSI packages? No.

This is where I am seeing the initial pain points. Setup authors that have installations with 1,000+ files don’t want to be hand coding or manipulating the output in order to fit their needs. I, being one of those authors (200,000+ files currently), completely agree. This is why, as the current feature owner, it pains me to say that Heat is not ready for this type of primetime work straight out of the box.

There are solutions, however, that can help us large setup authors out. Before I go into details on that, take a read over a response I made to someone that asked a few of these questions.

Hey Nick,

I understand your frustration, in fact have expressed them myself. Bottom line is no, Heat currently isn't setup to handle this volume of files in a fully Windows Installer compliant way. This is a known design limitation for the v3.0 release. However, I wouldn't throw the tool away. The general issue around large installations is not just getting the files onto the machine but then servicing the installation "in-place". This would mean not doing a full uninstall and reinstall for each new release. In order to handle this functionality many things need to stay the same for Windows Installer to correctly build patches for an MSI. Component key files must say the same and pretty much cannot be deleted, Feature/Component/File Ids must say the same, Component GUIDs must stay the same according to their key, DiskId's must say the same and I might be missing one or two things from that.

> Is Heat not designed to reduce the task of including 3000 files to adding a <ComponentGroupRef> (or whatever) line to a "Feature" ?

Yes and no. What you are seeing with the 1600 components in Candle/Light is actually thrown in Heat as well as a warning. It is very possible this should be upgraded to an error but I thought that was too strong. It is really up to the developer to decide that risk. However, with over 20,000 files one would probably not even see the warning. There are some ways to work around this if you are able to break up your directly harvesting into blocks of less than 1600 files.

> Why does the Heat output have one (blank, and duplicated) DirectoryRef each time there is a component in the one directory?  Wouldn't this degrade performance...

These actually aren't blank, they are correctly fragmented. You can use an "-sfrag" command line switch to get rid of this functionality. This has been done to allow components to be pulled into other ComponentGroups/Features without taking all other Directory references. This is in line with with how WiX can be authored for flexibility

> Is there a way to tell Heat that the path to the files is ..\Whatever, NOT a variable, NOT the silly string it uses by default?

The answer is no. The use of SourceDir\ was decided to remove root directory path dependencies. This is especially useful in large build environments where the *.wxs files can be copied between machines. The use of the "-b" command line switch in Light allows for root directories to be substituted in place of the SourceDir. It also allows Heat to generate the Directory/Component/File Ids in a repeatable manner to help enable patching.

The technical challenges for all of this are actually not extremely difficult. However, they aren't easy either. Much thought has been done in the WiX v3.0 timeline in order to being addressing many of these issues and make this high level of generation possible. Using Extensions to Heat there are ways to do this correctly. I am currently working on doing this for a product that is deploying over 200,000 files which we would like to perform "in-place" patching on once released. I am currently using an internal prototype to deliver this functionality and hope to be able to get this extension onto the WixContrib site within the next few months. Again, automating the initial gathering of files is not the challenge. It is the doing it again for the patch that will come out after your RTM.

What can be done to enable this type of requirement?

  • Layout Your Build Output into Groups of Less Than 1600 Files
    • I am aware that many people will not find this to be a viable options, but it would work.
    • Heat could be run against these smaller grouped directories and easily handle your needs.
    • This would also enable auditing of the files your company is releasing out the door.
      • Why does this matter? In one company I worked for a developer on his way out of the company got upset and wanted to deface a public website with an addition of a new GIF (a Cannabis leaf). The file was named “pot.gif” but that isn’t enough to throw off any major alarms. However, because we had put in change control on what files were allowed to ship we found this through auditing.
  • Write a Custom Heat Extension
    • Heat, like the other tools in Windows Installer XML Toolset (WiX), can be extended.
    • Writing your own Heat extension can be challenging for some but easy for others. If you are up to the task then this could be your quickest solution.
    • If you take this approach, be sure to follow Component rules and all the other fun stuff that is required to remain stable.
  • Wait Until Someone Else Does the Above Mentioned Possibility
    • This could be the simplest but not the most quickly available.
    • If your company isn’t into letting you write your own code, this would be your option.
  • Wait Until WiX vNext
    • The least optimal solution would be to wait until the next release of Heat.
    • Rob and I have tossed around some ideas on how to improve Heat overall in order to make it a product ready build environment tool.
    • When will this happen? It will be a while <grin/>

Some good news with all of this. For my day job, I am looking into doing much of this in order to get our products out the door and patchable. I would say the contribution to WixContrib will be several months off until something is ready, but it IS coming.

Some references:

There has been some recent work to Heat.exe, within the Windows Installer XML Toolset (WiX), which I think will have a significant impact on how people use the application currently and how it will be used in the feature. The features are brought forth from a great deal of user feedback and a general need for some “tender, loving care.” People have been using Heat to harvest various data stores and including the auto-generated .wxs files as part of their projects. This has been a good “start” for many projects but the intent was not to use it for constant re-generation in a build environment for dynamic content.

Although this practice is still recommend against, this last update is a set in the direction of consistent regeneration. In Windows Installer this is a very difficult task to manage as there are many rules that a package must confirm to in order to be patch compliant. Not only must Component GUIDs remain stable, the File ID, Registry ID, Directory ID Media Number and other items must stay consistent. If this does not happen there can be serious implications to the “in-place” servicing of your product.

Several years ago I was working on a way to do what heat.exe is starting to do now, auto-generate dynamic content in a compliant way. My project, WIXML, failed to generate MSIs that were upgrade compliant. It was necessary to do a full uninstall with each new package because it broke all the rules that I mentioned above (additional information from Bob Arnson “Paying for Upgrades”). With the latest update, Heat is getting closer to the ability of being compliant with consistent re-generation.

There has been four significant command line switches added that will help users comply to the rules above.

-cg <GroupName>
-dr <DirectoryRefId>
-srd
-var

The –cg <GroupName> command instructs Heat to harvest a given directory and include all found components in a <ComponentGroup/> with the given name. This will ensure that, when authoring a full product, a developer can be guaranteed a static value to reference using a <CompnentGroupRef/>.

heat.exe dir C:\MyDir -srd -cg 
TestMe


<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> <Fragment> <ComponentGroup Id="
TestMe
"> <Component Id="CO_BEA0A254206A075FE419AECC8F8323C3" Directory="DI_5E971BF1ECC0BC11795785D90F250FC0" ... > <File Id="FI_4273BB2732377498E74980308130C808" ... /> </Component>


The -dr switch takes a value of a DirectoryRef/@Id. This will become the directory which all other directories are rooted under.
In addition, and important to note, this acts as the seed for all the identifier generation.
Changing this value will directly impact the values of ALL the identifiers. When this value is not specified, it will default to "TARGETDIR".

heat.exe dir C:\MyDir -srd -dr 
MyDir


<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> <DirectoryRef Id="
MyDir
"> <Component Id="CO_BEA0A254206A075FE419AECC8F8323C3" Directory="DI_5E971BF1ECC0BC11795785D90F250FC0" ... > <File Id="FI_4273BB2732377498E74980308130C808" ... /> </Component>

The -srd switch suppresses the including the root directory of the path given. Using this switch will also change the values of identifiers being generated because of how they will be rooted.
Change this switch with high caution and review on the impact to your product.

heat.exe dir C:\MyDir -sfrag

<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> <Fragment> <DirectoryRef Id="TARGETDIR">
<Directory Id="dir86787CC870C17D6ABA6D1CDCBD925324" Name="MyDir">
<Directory Id="dirAA5EBADF0D0CC654BAD8E2CEFBF96609" Name="one"> <Component Id="cmp51B22E187A7403EF3A8EB589174E89DC" ...> <File Id="filEA449989D4339EB8C7329B93F83D35FE" ... /> </Component> </Directory> heat.exe dir C:\MyDir
-srd
-sfrag

<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> <Fragment> <DirectoryRef Id="TARGETDIR"> <Directory Id="dir090ACB8E8D9B8C1F26DE6E8300AA9049" Name="one"> <Component Id="cmpE21824A6879A478E4A884BA6B00C33C9" ...> <File Id="filF1BA1271365EB2A130AA3F0BE83E9D6D" ... /> </Component> </Directory>


The -var switch value of the preprocessor variable you want to use "$(<var|env>.MyVar)". Renaming this value will not impact dynamically created identifiers.

heat.exe dir C:\MyDir -srd -sfrag -var 
var.MyVar


<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> <Fragment> <DirectoryRef Id="TARGETDIR"> <Directory Id="dir090ACB8E8D9B8C1F26DE6E8300AA9049" Name="one"> <Component Id="cmpE21824A6879A478E4A884BA6B00C33C9" ...> <File Id="filF1BA1271365EB2A130AA3F0BE83E9D6D" Source="$(
var.MyVar
)\one\test.txt" ... /> </Component> </Directory>

As always, feedback is appreciated! I hope these changes help some people onboard!

As mentioned in my previous blog, there was a user request to enable the Windows Installer XML Toolset (WiX) to run on FIPS enabled machines. This in itself was not a large change. In order to enable it all that needed to be done was to switch the hashing algorithms that we use from a non-compliant one (MD5) to a compliant one (SHA1). The issue came when not wanting to break backwards compatibility with users that have auto generated identifiers now and are using the generated short name feature as well. Second was to ensure that merge modules built with Windows Installer XML Toolset (WiX) and that are embedded in an MSI built with Windows Installer XML Toolset (WiX) do not break (overflow Id length) with FIPS enabled.

This introduces a new command line switch on candle.exe. The argument “-fips” can now be specified in order to switch the hashing algorithms to SHA1. If you are running on a FIPS enabled machine and you do not specify this switch you will now receive an exception clearly telling you that you need to enable this feature. Once this is enabled, it will break the upgrade rules for registry Ids that are auto generated and it will change the values generated for short names;
so please be careful!

Below is what you can expect to see in terms of the output changing.

Registry Id Change
regCF3E4DD8E9FDD1355D6E8ECA2D7DA852
to
regFD39D952EC8973D41843396A15B57055

Short Name Change
djjnosd-.wxs
to
q9_iaha-.wxs

In addition the two static methods in src\wix\CompilerCore.cs have changed to members of that class. Please take note of this if you are using those!

A user of the Windows Installer XML Toolset (WiX) created a bug asking if the we could support FIPS (SFBUG:2545539). FIPS 140 is a U.S. government standard of security for cryptography. Having worked with DHS before at several locations I know that 1) they are strict about security and 2) I would like to see Windows Installer XML Toolset (WiX) more heavily used by our government (and everyone else). The one thing I did hear while working for the fed was that all software that is executed needs to be looked at extensively in order for it to be approved. The good thing about Windows Installer XML Toolset (WiX) is that it is open source and can easily be evaluated in order to adopt.

So, why am I writing this blog? Well, when looking for information on what algorithms are and are not supported I didn’t find much documentation on MSDN. This lead me to look in other locations to find information. I finally stumbled on a good blog called FIPS validated cryptographic algorithms in .NET (it also shows you how to turn FIPS on in Windows there is also KB811833 which describes this in some more detail between Windows 2003/XP and 2008/Vista). Although I figured the information given was correct, I knew there were a few more algorithms available that weren’t listed. So, I went about writing one of my little test apps to see what did and did not work.

The other thing you can derive from this is that we are looking to make the Windows Installer XML Toolset (WiX) FIPS compliant. It currently is not due to the way we generate our identifiers. The toolset itself is not highly worried about security implications as the use of hashes are just for uniqueness. I hope to write more about making the toolset FIPS compliant in the near future.

Before running this on your machine make sure to ENABLE FIPS. Otherwise, you will get all ‘Y’ for ‘Compliant’.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Reflection;

namespace FIPS
{
    class Program
    {
        static void Main(string[] args)
        {
            Assembly core = Assembly.Load(
                "System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
            Assembly mscorlib = Assembly.Load(
                "mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");

            Type[] subclasses = new Type[]
            {
                typeof(SymmetricAlgorithm),
                typeof(HashAlgorithm),
                typeof(AsymmetricAlgorithm)
            };

            Print(mscorlib, subclasses);
            Console.WriteLine();
            Console.WriteLine();
            Print(core, subclasses);
            
            Console.Read();
        }

        private static void Print(Assembly asm, Type[] subclasses)
        {
            string columnFormat = "{0,-35}{1,-15}{2}";
            Console.WriteLine("FIPS Compliant in {0}", asm.GetName());
            Console.WriteLine(columnFormat, "Name", "Compliant", "Subclass");

            foreach (Type type in asm.GetTypes())
            {
                foreach (Type subclass in subclasses)
                {
                    if (type.IsSubclassOf(subclass))
                    {
                        if (!type.IsAbstract)
                        {
                            string isCompliant = null;
                            try
                            {
                                Activator.CreateInstance(type);
                                isCompliant = "Y";
                            }
                            catch (TargetInvocationException)
                            {
                                isCompliant = "N";
                            }
                            catch (Exception e)
                            {
                                Console.WriteLine(e.Message);
                            }
                            finally
                            {
                                Console.WriteLine(
                                    columnFormat, type.Name, isCompliant, subclass.Name);
                            }
                        }
                    }
                }
            }
        }
    }
}

The result can be seen below.

FIPS Compliant in mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Name                               Compliant      Subclass
DESCryptoServiceProvider           Y              SymmetricAlgorithm
DSACryptoServiceProvider           Y              AsymmetricAlgorithm
HMACMD5                            N              HashAlgorithm
HMACRIPEMD160                      N              HashAlgorithm
HMACSHA1                           Y              HashAlgorithm
HMACSHA256                         N              HashAlgorithm
HMACSHA384                         N              HashAlgorithm
HMACSHA512                         N              HashAlgorithm
MACTripleDES                       Y              HashAlgorithm
MD5CryptoServiceProvider           N              HashAlgorithm
RC2CryptoServiceProvider           N              SymmetricAlgorithm
RIPEMD160Managed                   N              HashAlgorithm
RSACryptoServiceProvider           Y              AsymmetricAlgorithm
RijndaelManaged                    N              SymmetricAlgorithm
SHA1CryptoServiceProvider          Y              HashAlgorithm
SHA1Managed                        N              HashAlgorithm
SHA256Managed                      N              HashAlgorithm
SHA384Managed                      N              HashAlgorithm
SHA512Managed                      N              HashAlgorithm
TripleDESCryptoServiceProvider     Y              SymmetricAlgorithm


FIPS Compliant in System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Name                               Compliant      Subclass
AesCryptoServiceProvider           Y              SymmetricAlgorithm
AesManaged                         N              SymmetricAlgorithm
ECDiffieHellmanCng                 Y              AsymmetricAlgorithm
ECDsaCng                           Y              AsymmetricAlgorithm
MD5Cng                             N              HashAlgorithm
SHA1Cng                            Y              HashAlgorithm
SHA256Cng                          Y              HashAlgorithm
SHA256CryptoServiceProvider        Y              HashAlgorithm
SHA384Cng                          Y              HashAlgorithm
SHA384CryptoServiceProvider        Y              HashAlgorithm
SHA512Cng                          Y              HashAlgorithm
SHA512CryptoServiceProvider        Y              HashAlgorithm

As a side note, I would like to add that Windows Live Writer does not log into spaces when FIPS is enabled on your machine. You get an exception message, something similar to this. “An unexpected exception has occurred … please check your proxy server or firewall configuration.” Obviously this is incorrect and should probably be fixed.

Summary

The Windows Installer error code 2908 with sub errors 1402 and 1450 has been quite eluding until recently. While working on a shipping product this error occurred on a few machines within the internal population. At first the error was disregarded as not reproducible. However, it soon became a small internal epidemic. The original thoughts were that there had been an issue with the MSI package itself.

The error code 2908 (http://msdn.microsoft.com/en-us/library/aa372835(VS.85).aspx) simply says "Could not register component [2]." When doing some searching around the web there are a couple good hits that come back with answers that are not unique to the sub errors of 1402 and 1450. One link that had a good grasp on the problem was from the Add-in Express Blog. This tracked the error down to the following registry key and then the current user's profile inside of it.

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\<user-sid>\Components

The error is easily reproducible with a Per-User install of an MSI where there are no other Per-User installations on the machine and more then roughly 1,000 components being installed. Below is a sample output from a Windows Installer verbose log file.

MSI (s) (5C:C8) [11:28:38:420]: Executing op: ComponentRegister(ComponentId={AC763451-B00C-4E4C-9E0D-9F3A0921AF47},KeyPath=C:\Program Files\My App\file_100000.exe,State=3,,Disk=1,SharedDllRefCount=0,BinaryType=0)
1: {3C7EE564-C47A-4E1E-8B57-CFF4A7F07EC9} 2: {AC763451-B00C-4E4C-9E0D-9F3A0921AF47} 3: C:\Program Files\My App\file_100000.exe

MSI (s) (5C:C8) [11:28:38:430]: Note: 1: 1402 2: UNKNOWN\Components\154367CAC00BC4E4E9D0F9A39012FA74 3: 1450

DEBUG: Error 2908: Could not register component {AC763451-B00C-4E4C-9E0D-9F3A0921AF47}.
MSI (s) (5C:C8) [11:29:04:427]: Product: My App -- The installer has encountered an unexpected error installing this package. This may indicate a problem with this package. The error code is 2908. The arguments are: {AC763451-B00C-4E4C-9E0D-9F3A0921AF47}, ,
MSI (s) (5C:C8) [11:29:04:447]: Executing op: ComponentRegister(ComponentId={243DF9B9-C1A6-450C-8F64-0CCFF18C834D},KeyPath=C:\Program Files\My App\file_100001.lib,State=3,,Disk=1,SharedDllRefCount=0,BinaryType=0)

MSI (s) (5C:C8) [11:29:04:447]: Note: 1: 1402 2: UNKNOWN\Components\9B9FD3426A1CC054F846C0FC1FC838D4 3: 1450

MSI (s) (5C:C8) [11:29:04:447]: Product: My App -- The installer has encountered an unexpected error installing this package. This may indicate a problem with this package. The error code is 2908. The arguments are: {243DF9B9-C1A6-450C-8F64-0CCFF18C834D}, ,

Cause

The errors 1402 and 1450 are return values from calls made by the Windows Installer team's call to RegOpenKeyEx. The error being returned from this call does not make sense either. After some tracking down it was discovered to be a bug in that call. On a Per-User installation there are typically not enough child keys to change the type of the parent key. When the parent key's type changes it has an error handling one or more child keys after it has had several hundred keys in it's hive. 

Resolve

In most cases, the one child key left over is the one listed below. Removing this key and/or any other key(s) in the use's profile will keep the error from happening an allow for the installation of the product. It is also possible to install the MSI for all users. 

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\<user-sid>\Components\AD95649F068525549B26938D7D18FEA7
"B3414A45B4B628042B8446B35265C1BC"="C?\\WINDOWS\\system32\\rgb9rast_2.dll"

Applies To

Windows XP SP3 and below
Windows Vista is not affected

 

 
Page view tracker