MSI Components Cardinal Rule #6

Rule: Do not change an existing MSI component in any of the following ways:

  • Any change that would make the MSI component incompatible with previous versions of the component.
  • A change in the name or target location of any file, registry key, shortcut, or other resource in the component.
  • The addition or removal of any file, registry key, shortcut, or other resource in the MSI component.
  • Transforming a 32-bit MSI component into a 64-bit component.

Explanation: In my experience, I have seen this rule violated in more cases than any of the other MSI component rules. A very large number of the installation, uninstallation and coexistence bugs encountered in the field are directly related to violations of this rule. Another way of expressing this rule is: “Once published, MSI components are mostly immutable.” The use of the word “mostly” to qualify this rule is necessary to allow for certain changes that commonly occur to the content of MSI components. If MSI component were completely immutable, they would cease to be useful entities, as that would force install authors to refactor the entire set of components with every new release.

The following sections provide a field-by-field description of what may or may not be changed in the various tables that define MSI components and their contents:

Component Table

  • Component – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match.
  • ComponentId – This is the GUID that identifies the component. Changing this would in effect define a new component.
  • Directory_ – since this generally defines the target location of the MSI component resources, the rule prohibits this from changing. Certain resources such as registry entries do not use this field, so if a component consists solely of such entries, it could technically be changed without causing problems, but there really should be no reason to make such a change.
  • Attributes – This field is a mixed bag. Some attributes can be changed without causing serious problems, while other attributes must never be changed. At the time of this writing, the attributes that should not be changed include those that alter the location to look for the keypath resource (msidbComponentAttributesRegistryKeyPath or msidbComponentAttributesODBCDataSource), and the bitness attribute (msidbComponentAttributes64bit). For the remaining attributes, there is still the potential that problems may be caused if they are changed. The Windows Installer documentation is not particularly clear in this area, so the best bet is to avoid changing component attribute values if possible, and rigorously test it when such changes are unavoidable.
  • KeyPath – This should never be changed.

AppId Table

  • AppId – Since this would change the target location of registry keys, it cannot be changed.
  • RemoteServerName – This field controls the value of a registry entry, and is thus ok to change. *
  • LocalService – This field controls the value of a registry entry, and is thus ok to change. *
  • ServiceParameters – This field controls the value of a registry entry, and is thus ok to change. *
  • DllSurrogate – This field controls the value of a registry entry, and is thus ok to change. *
  • ActivateAtStorage – This field controls the value of a registry entry, and is thus ok to change. *
  • RunAsInteractiveUser – This field controls the value of a registry entry, and is thus ok to change. *

Class Table

  • CLSID – Since this would change the target location of registry keys, it cannot be changed.
  • Context – Since this would change the target location of registry keys, it cannot be changed.
  • Component_ – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual component (as identified by its GUID) that is associated.
  • ProgId_Default – Since this would change the target location of registry keys, it cannot be changed.
  • AppId_ – Since this would change the target location of registry keys, it cannot be changed.
  • FileTypeMask – Since this would potentially create new registry keys or entries, it cannot be changed.
  • Icon_ – Since this would potentially create new registry keys or entries, it cannot be changed.
  • IconIndex – This field controls the value of a registry entry, and is thus ok to change. *
  • DefInprocHandler – Since this would potentially create new registry keys or entries, it cannot be changed.
  • Argument – This field controls the value of a registry entry, and is thus ok to change. *
  • Feature_ – This field can be changed if the COM server is moved to a different feature.
  • Attributes – As of this writing, there is only one attribute flag defined (msidbClassAttributesRelativePath). This attribute controls the value of a registry entry, and is thus ok to change. *

ComPlus Table

  • Component_ – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual component (as identified by its GUID) that is associated.
  • ExpType –

CreateFolder Table

  • Directory_ – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual folder to be created.
  • Component_ – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual component (as identified by its GUID) that is associated.

DuplicateFile Table

  • FileKey – This is simply an internal database primary key. It can be changed so long as it uniquely identifies the record.
  • Component_ – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual component (as identified by its GUID) that is associated.
  • File_ – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual file that is associated.
  • DestName – This field cannot be changed.
  • DestFolder – This field cannot be changed.

Environment Table

  • Environment – This is simply an internal database primary key. It can be changed so long as it uniquely identifies the record.
  • Name – This field cannot be changed.
  • Value – This field controls the value of an environment variable, and is thus ok to change. *
  • Component_ – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual component (as identified by its GUID) that is associated.

Extension Table

  • Extension – This field cannot be changed.
  • Component_ – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual component (as identified by its GUID) that is associated.
  • ProgId_ – This field controls the association with a ProgID, and cannot be changed.
  • MIME_ – This field controls the association with a MIME type, and cannot be changed.
  • Feature_ – This field can be changed if the component is moved to a different feature.

File Table

  • File- This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match.
  • Component_ – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual component (as identified by its GUID) that is associated.
  • FileName – This field cannot be changed.
  • FileSize – This field is typically changed automatically by the install authoring tool each time an installer is generated.
  • Version – This field is typically changed automatically by the install authoring tool each time an installer is generated.
  • Language – This field can be changed.
  • Attributes – File attributes can be changed as needed.
  • Sequence – This field is typically changed automatically by the install authoring tool each time an installer is generated.

IniFile Table

  • IniFile – This is simply an internal database primary key. It can be changed so long as it uniquely identifies the record.
  • FileName – This field cannot be changed.
  • DirProperty – The value of the property referenced by this field cannot be changed.
  • Section – This field controls the content of an INI file, and is thus ok to change. *
  • Key – This field controls the content of an INI file, and is thus ok to change. *
  • Value – This field controls the content of an INI file, and is thus ok to change. *
  • Action – This field can be changed if needed.
  • Component_ – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual component (as identified by its GUID) that is associated.

IsolatedComponent Table

  • Component_Shared – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual component (as identified by its GUID) that is associated.
  • Component_Application – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual component (as identified by its GUID) that is associated.

MIME Table

  • ContentType – This field cannot be changed.
  • Extension_ – This field controls the association with an extension, and cannot be changed.
  • CLSID – This field controls the association with a class, and cannot be changed.

MoveFile Table

  • FileKey – This is simply an internal database primary key. It can be changed so long as it uniquely identifies the record.
  • Component_ – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual component (as identified by its GUID) that is associated.
  • SourceName – This field cannot be changed.
  • DestName – This field cannot be changed.
  • SourceFolder – This field cannot be changed.
  • DestFolder – This field cannot be changed.
  • Options – This field cannot be changed.

MsiAssembly Table

  • Component_ – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual component (as identified by its GUID) that is associated.
  • Feature_ – This field can be changed if the component is moved to a different feature.
  • File_Manifest – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual file that is associated.
  • File_Application – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual file that is associated.
  • Attributes – This field cannot be changed.

ODBCDataSource Table

  • DataSource – This field cannot be changed.
  • Component_ – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual component (as identified by its GUID) that is associated.
  • Description – This field can be changed.
  • DriverDescription – This field can be changed.
  • Registration – This field cannot be changed.

ODBCDriver Table

  • Driver – This field cannot be changed.
  • Component_ – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual component (as identified by its GUID) that is associated.
  • Description – This field can be changed.
  • File_ – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual file that is associated.
  • File_Setup – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual file that is associated.

ODBCTranslator Table

  • Translator – This field cannot be changed.
  • Component_ – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual component (as identified by its GUID) that is associated.
  • Description – This field can be changed.
  • File_ – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual file that is associated.
  • File_Setup – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual file that is associated.

ProgId Table

  • ProgId – This field cannot be changed.
  • ProgId_Parent – This field cannot be changed.
  • Class_ – This field cannot be changed.
  • Description – It is permissible to change this field.
  • Icon_ – Since this would potentially create new registry keys or entries, it cannot be changed.
  • IconIndex – This field controls the value of a registry entry, and is thus ok to change. *

Registry Table

  • Registry – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match.
  • Root – This field defines the location of the registry key, and thus cannot be changed.
  • Key – This field defines the location of the registry key, and thus cannot be changed.
  • Name – This field defines the name of the registry value, and thus cannot be changed.
  • Value – This field controls the value of a registry entry, and is thus ok to change. *
  • Component_ – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual component (as identified by its GUID) that is associated.

RemoveFile Table

  • FileKey – This is simply an internal database primary key. It can be changed so long as it uniquely identifies the record.
  • Component_ – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual component (as identified by its GUID) that is associated.
  • FileName – This field cannot be changed.
  • DirProperty – This field cannot be changed.
  • InstallMode – This field cannot be changed.

RemoveIniFile Table

  • RemoveIniFile – This is simply an internal database primary key. It can be changed so long as it uniquely identifies the record.
  • FileName – This field cannot be changed.
  • DirProperty – The value of the property referenced by this field cannot be changed.
  • Section – This field controls the content of an INI file, and is thus ok to change. *
  • Key – This field controls the content of an INI file, and is thus ok to change. *
  • Value – This field controls the content of an INI file, and is thus ok to change. *
  • Action – This field can be changed if needed.
  • Component_ – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual component (as identified by its GUID) that is associated.

RemoveRegistry Table

  • RemoveRegistry – This is simply an internal database primary key. It can be changed so long as it uniquely identifies the record.
  • Root – This field defines the location of the registry key, and thus cannot be changed.
  • Key – This field defines the location of the registry key, and thus cannot be changed.
  • Name – This field defines the name of the registry value, and thus cannot be changed.
  • Component_ – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual component (as identified by its GUID) that is associated.

ServiceControl Table

  • ServiceControl – This is simply an internal database primary key. It can be changed so long as it uniquely identifies the record.
  • Name – This field cannot be changed.
  • Event – This field cannot be changed.
  • Arguments – It is permissible to change this field.
  • Wait – It is permissible to change this field.
  • Component_ – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual component (as identified by its GUID) that is associated.

ServiceInstall Table

  • ServiceInstall – This is simply an internal database primary key. It can be changed so long as it uniquely identifies the record.
  • Name – This field cannot be changed.
  • DisplayName – It is permissible to change this field.
  • ServiceType – This field cannot be changed.
  • StartType – It is permissible to change this field.
  • ErrorControl – It is permissible to change this field.
  • LoadOrderGroup – It is permissible to change this field.
  • Dependencies – It is permissible to change this field.
  • StartName – It is permissible to change this field.
  • Password – It is permissible to change this field.
  • Arguments – It is permissible to changet this field.
  • Component_ – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual component (as identified by its GUID) that is associated.
  • Description – It is permissible to change this field.

Shortcut Table

  • Shortcut – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match.
  • Directory_ – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual directory that is associated.
  • Name – This field defines the filename for the shortcut, and thus cannot be changed.
  • Component_ – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual component (as identified by its GUID) that is associated.
  • Target – This field may be changed if necessary.
  • Arguments – It is permissible to change this field.
  • Description – It is permissible to change this field.
  • Hotkey – It is permissible to change this field.
  • Icon_ – It is permissible to change this field.
  • IconIndex – It is permissible to change this field.
  • ShowCmd – It is permissible to change this field.
  • WkDir – It is permissible to change this field.
  • DisplayResourceDLL – It is permissible to change this field.
  • DisplayResourceId – It is permissible to change this field.
  • DescriptionResourceDLL – It is permissible to change this field.
  • DescriptionResourceId – It is permissible to change this field.

TypeLib Table

  • LibID – This is simply an internal database primary key. It can be changed so long as it uniquely identifies the record.
  • Language – This field cannot be changed.
  • Component_ – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual component (as identified by its GUID) that is associated.
  • Version – This field will affect the path for registry keys, and thus cannot be changed.
  • Description – This field controls the value of a registry entry, and is thus ok to change. *
  • Directory_ – This is simply an internal database key used for joining records. As such, it can be changed, so long as any joined fields in other tables are changed to match. Note that it is not permissible to change the actual directory that is associated.
  • Feature_ – It is ok to change this field if the typelib component has been moved to a different feature.
  • Cost – It is permissible to change this field.

Since the restrictions associated with the rule defined in this post prevent many types of changes to existing MSI components, there must be some other way to change the installer. The way to do that is by removing the existing MSI component, and replacing it with a new component. This is often not quite as simple as it sounds, because the install author has to be very careful to make sure that the new MSI component does not violate any of the MSI component rules.

Example: For an example, assume that there is an existing MSI component {75B09972-8B3F-4FEE-BD43-FA45EBC50BC8} containing two files (“MyApp Lite.exe” and Help.chm) and a registry entry (HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run – MyApp=”[#MyAppLite.exe]”). Product Marketing has decided to change the product name to “MyApp Personal Edition”, and would like the program name to match. If the file name in the existing MSI component were changed, that would violate the rule in this post. So, a new MSI component must be created to replace the existing one.

To start with, we could create a new MSI component {1C354F6E-82B5-4076-B612-917850DCF0A0} that is a copy of the existing one. Then we could just change the name of the program to “MyApp Personal Edition.exe” in the new MSI component. However, this would violate the MSI Components Cardinal Rule #1, which prohibits two MSI components installing the same resources to the same locations. In this case, both the existing MSI component and the new MSI component would install the file Help.chm and the Run registry entry to the same locations.

To avoid violating rule #1, it is necessary to change the name and/or target location of every other resource in the MSI component. For Help.chm, we have the choice of changing the filename or install path. Either one of these is going to force changes in our application, which is unfortunate. The only way to have avoided this would have been to have the foresight to have originally put Help.chm into its own component. While it is to late for that now since the original MSI component has already been released, the same mistake can be avoided in the future by creating a new MSI component now for the help file. We will still have to change the filename and/or install location to avoid conflicting with the original MSI component, but this should be the last time we will have to do that. So, we now create a third MSI component {2777AE41-1E79-42AF-BCD9-EFA47B5B5427}, and move the help file into it from the {1C354F6E-82B5-4076-B612-917850DCF0A0} component. We also change the help file name to “MyAppHelp.chm” to fix the rule #1 violation. The application will also need to be modified to be able to locate the new help file, and the help file projects and build process modified to produce the help file under this new name.

For the Run registry entry, avoiding the rule #1 violation is a little easier. The name of the registry entry “MyApp” is really arbitrary. Nothing else is referencing that value, and Windows doesn’t care what we name it. So in this case, we just leave it in the new MSI component {1C354F6E-82B5-4076-B612-917850DCF0A0} and change the entry name to “MyAppPersonal”. The Run registry entry is closely related to the MyApp program, so it makes sense to keep it in the same MSI component.

Exceptions: While every attempt should be made to follow this rule whenever possible, not every violation of this rule will result in problems. Here are some conditions under which it is permissible to violate the above rule:

  • The install author can guarantee that different versions of an MSI component will never be installed on a given system at the same time
  • The changes to the existing component have been proven by rigorous testing to be backward compatible with all previous versions of the component.

About Daniel Brannon

Daniel Brannon founded OSoSLO in 2009 to provide software tools and services for business and technology professionals.
This entry was posted in Windows Installer. Bookmark the permalink.

Leave a Reply