Windows XP Technical Articles
How To Build and Service Isolated Applications and Side-by-Side Assemblies for Windows XP
 

RoseMarie FitzSimons
Henry Borys
Microsoft Corporation

August 24, 2001
(updated October 2004)

Summary: This article details the steps of creating, distributing, and safely servicing isolated applications and side-by-side assemblies for Microsoft Windows XP—a solution that helps eliminate versioning conflicts in Windows-client applications. (31 printed pages)

Contents

Introduction
The Windows XP Solution: An Overview
Creating an Isolated Application
Creating a Side-by-Side Assembly
Servicing Shared Assemblies and Applications
Notes on How to Correctly Consume a Side-by-Side Component in an Application
Conclusion
Appendix: Should You Provide Shared Components?

Introduction

Dynamic Link Libraries (DLLs) versioning conflicts have presented a complex and often frustrating set of problems (DLL hell) for Microsoft® Windows® application developers. Microsoft® Windows® XP addresses this problem by allowing application developers to build isolated applications and side-by-side assemblies. These help eliminate the effects of DLL versioning conflicts on the development, deployment, and maintenance of applications.

Windows XP enables applications to be isolated, so that an application can consistently get the version of a component with which it was tested or the latest service release of the same provided by the component publisher. New applications consume new feature versions of components, while old applications may continue to use older versions. Application authors and administrators can use manifests to manage the sharing of side-by-side assemblies after deployment, on either a global or per-application basis. Isolated applications and side-by-side assembly sharing can be used to develop applications that safely share Win32® assemblies provided either by Microsoft or other component authors. Developers can also use this technology to correct DLL versioning conflicts caused by an incompatible version of a shared assembly.

Windows XP offers a much more complete solution for creating and servicing isolated applications and side-by-side assemblies than that first introduced with Microsoft® Windows® 98 Second Edition and Microsoft® Windows® 2000. It is recommended that this older feature NOT be used in conjunction with the XP support. This article details these advances made by Windows XP, and the steps of creating, distributing, and safely servicing isolated applications and side-by-side assemblies for Windows XP.

The Windows XP Solution: An Overview

The goal of the Windows XP solution is to enable developers to create isolated applications unaffected by changes to the system—to leverage the value of sharing DLLs while enabling this sharing to happen in a safe way. An additional goal is to enable existing applications to use these shared components, without having to recode the entire application. To achieve these objectives, Windows XP incorporates the following features:

  • Support for side-by-side assemblies. Unlike previous versions of Windows, where a single version of a shared component is available, side-by-side assembly support allows multiple versions of an assembly to be installed and run at the same time. Components making up these assemblies are self-described, isolated, self-contained, and must be authored so that they do not conflict with each other. Side-by-side assemblies are never updated. Instead, new versions of the assemblies are installed with older versions on the same system. Thus, applications can rely on using the same version used for testing. Assemblies may be private to the application—that is, used exclusively by the application—or they may be shared assemblies.
  • Isolated applications. Applications are isolated to likewise eliminate the negative effects of sharing components. Instead of randomly sharing assemblies, developers choose the version of a side-by-side assembly that the application should use. The configuration used when the application is tested can be duplicated on any destination computer. The application's dependency on specific versions of side-by-side assemblies is described in the application's manifest file. This information is used by the operating system to ensure that the appropriate versions of components are provided to the application. Applications built with different versions of the same assembly may run simultaneously without affecting each other's execution. The installation, removal, or upgrading of other applications on the system will not affect a fully isolated application. An application is fully isolated if all of its components are side-by-side assemblies. It is partially isolated if it uses some assemblies, and some shared components that are not side-by-side assemblies. Most applications will be partially isolated.
  • Publisher & application configuration. In contrast with static linking to DLLs, Windows XP offers a model for servicing shared assemblies and applications. The publisher of a shared assembly can issue a publisher configuration, which can redirect all applications on the system to a specific version of an assembly. A publisher would only issue this type of configuration when it is safe to do so (that is, in the case of a bug fix that is backward compatible), or when it is imperative that all applications use the latest version, for example a security fix for a component. The system administrator has the option to override publisher configuration with application configuration. This model also provides flexibility for applications and administrators to update the configuration after deployment, without having to reinstall the application. Thus isolated applications are not tied to the shipping schedule of their side-by-side assemblies.

In addition to protecting an application from changes to other applications, and from system changes to system assemblies, isolated applications offer the following advantages:

  • Developers can fix bugs and add new features to assemblies used by an application, without risk of breaking other applications.
  • Rebooting when an application is installed may no longer be necessary.
  • Developers can easily install or replicate an application using an install mechanism such as XCOPY.
  • An application can take advantage of the servicing model for shared assemblies, without requiring a recompile or reinstall.
  • An application that relies on a version of a shared component other than what is available on the system may be fixed on a case-by-case basis, by providing the application access to the appropriate version.

The Windows XP approach to isolated applications and side-by-side assemblies represents a significant advance over the side-by-side sharing and DLL redirection introduced in the Windows 98 Second Edition/Windows 2000 timeframe. These earlier implementations recommended that developers build new components supporting the simultaneous execution of multiple versions of that component. This still relied upon using the registry, so components were not fully isolated. In the event of a registry conflict, applications could still be broken.

With Windows XP, the assembly is described by a manifest; the registry is no longer relied upon for storing and accessing the COM activation data. This allows components to now be isolated. Shared assemblies can also be serviced, so that applications can use the latest version. The table below illustrates the advances with Windows XP in this regard over Windows 2000. (For more information on side-by-side component sharing in Windows 2000, see Implementing Side-by-Side Component Sharing in Applications (Expanded)).

Feature Windows 2000 Windows XP
Using Registry for COM activation data Yes, component can be impacted by updates No, the assembly is described by a manifest, allowing for isolation of components
Capable of sharing multiple versions No, only one shared version, or each application has a private version Yes, installed side-by-side; can be shared globally
Fully isolated, self-contained. No Yes
Shared component can be serviced globally Not easily, if installed per application Yes

Table 1. Comparison of side-by-side sharing in Windows 2000 and Windows XP

These concepts of isolating applications, usage of assemblies, manifests, explicit version management, and servicing with configuration are also fully supported in the .Net framework. The Common Language Runtime provides a richer set of services than the standard Win32 operating system. For example, with development environments such as Microsoft® Visual Studio®, manifests are automatically created as part of code development. As with the Windows XP implementation, the operating system enforces requests for specific versions by providing a default version, or by honoring publisher overrides per assembly and administrator overrides per system. For more information on side-by-side sharing in the .NET framework, see Simplifying Deployment and Solving DLL Hell with the .NET Framework.

Windows XP Example

Figure 1 depicts an application with an application manifest. The manifest describes the application's dependency on private assembly windows.samples.draw, and on a specific version of the shared assembly.

Figure 1. Partially isolated application

For the purposes of this example, multiple versions of shared assemblies are installed side-by-side in the side-by-side assembly cache (WinSxS folder in the Windows folder). These are available to the application to reference a dependency on a specific version. The application may also use other shared components not referenced in the manifest. These files would be are installed globally, for example in the system32 folder.

Creating an Isolated Application

Creating an isolated application entails five steps:

  1. Determine the version of the shared assemblies your application will use
  2. Create private assemblies for components private to your application
  3. Create an application manifest
  4. Create your install package
  5. Test

Step one: Determine the version of the shared assemblies your application will use

Create a list of the shared assemblies your application uses and the versions of each that are most appropriate for your application. When specifying dependencies on assemblies provided with the operating system, it is recommended you choose the version of the assembly provided with the base platform, unless your application includes a merge module to install a specific version of the assembly.

For example, GDIPlus 1.0.0.0 ships with Windows XP. GDIPlus 1.0.1.0 is a compatible bug fix shipped later. (This version is provided on Windows Update.) Because the Quick Fix Engineering (QFE) version may or may not be available on a client machine, your application would describe a dependency on version 1.0.0.0, and if the later version is installed with publisher configuration, it would automatically get that version.

If shared assemblies are not included as part of the base platform, then the application install should include the Windows Installer merge modules for the assembly install. The information on dependencies will be included in the application manifest.

Step two: Create private assemblies for components private to your application

A private assembly is only used by the application where it is installed. It is authored as a side-by-side assembly, and should be tested to ensure it works well as a side-by-side assembly. If the same private assembly is used by multiple applications, then it needs to be installed private per application. Otherwise, to enable sharing, it could be created and installed as a shared assembly. Creating a private assembly removes the reliance on the registry and enables XCOPY installs.

Note: Private assemblies can only be serviced by updating the application. So private assemblies are not recommended if there are central servicing needs.

We'll discuss the steps of creating private and shared assemblies in the section, "Creating an Assembly" below.

Step three: Create an application manifest

The application manifest is an XML file that:

  • Uniquely identifies the application
  • Describes dependencies on specific versions of side-by-side assemblies

You may use Notepad or any standard XML editor to create your manifest. Both InstallShield & Wise Solutions will fully support the creation of application manifests in future releases of their products.

The application manifest name is based on the name of the executable followed by ".manifest". For example, the mysampleapp.exe would have the manifest "mysampleapp.exe.manifest".

The application manifest may be installed as a separate file in the same folder as the executable, or it may be added as a resource to the application in the .rc file.

You would add it as a resource as follows:

#define MANIFEST_RESOURCE_ID 1 
MANIFEST_RESOURCE_ID RT_MANIFEST "mysampleapp.exe.manifest"

The assemblyIdentity

Each application has a unique assemblyIdentity that is described in the application manifest. The assemblyIdentity identifies the name of the application, the version, the type of application, and the processor architecture on which it was intended to run. For example:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
    version="1.0.0.0"
    processorArchitecture="x86"
    name="Microsoft.Windows.mysampleapp"
    type="win32"
/>

name: Uniquely names the application. The recommendation is to use the following format for the name: Organization.Division.Name. For example: Microsoft.Windows.mysampleapp.

type: Specifies the application type. The value must be win32, all lower case.

processorArchitecture: Specifies the processor of the target platform. The valid values are x86 for 32-bit Windows, and ia64 for 64-bit Windows.

version: Specifies the application version. Use the four-part version format: mmmmm.nnnnn.ooooo.ppppp. Each of the parts separated by periods can be 0-65535 inclusive—left-to-right, the major, minor, build, and revision parts separated by periods.

The description element contains an optional description of the application.

In the dependency section, include the assemblyIdentity for the specific side-by-side assembly(s) used by your assembly. For example, if your assembly has dependencies on both GDIPlus and a private assembly Foo, you would include the following:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
    version="1.0.0.0"
    processorArchitecture="x86"
    name="Microsoft.Windows.mysampleapp"
    type="win32"
/>
<description>Your app description here</description>
<dependency>
    <dependentAssembly>
        <assemblyIdentity
            type="win32"
            name="Microsoft.Windows.GdiPlus"
            version="1.0.0.0"
            processorArchitecture="x86"
            publicKeyToken="6595b64144ccf1df"
            language="*"
        />
    </dependentAssembly>
</dependency>
<dependency>
    <dependentAssembly>
        <assemblyIdentity
            type="win32"
            name="Microsoft.Windows.Foo"
            version="6.0.0.0"
            processorArchitecture="x86"
        />
    </dependentAssembly>
</dependency>
</assembly>

Step four: Create an install

You may use any valid install technology to install your application. Note that the application manifest must be installed in the same folder as the application executable. If you are installing shared assemblies, then your install must incorporate the Windows Installer merge module in accordance with the redistribution licensing agreement.

Step five: Test

Validate that the application manifest is consistent with the application manifest schema and is well-formed XML. Use the manifest validation tool Manifestchk.vbs (available in the Platform SDK).

Test the application to ensure that multiple versions running do not impact each other. For example, start two versions on the application and test each of the components, using scenarios with side-by-side sharing to confirm that they do not conflict with each other.

Validate that the application loads assemblies appropriately. For example, if your application should use a given private assembly, test to confirm that it does use that assembly. Use a tool such as Depends.exe (available in the Platform SDK), or any debugger, to check from where the assembly is being loaded.

Use the event log to track reasons for failures.

Note   In some cases, existing applications can be updated into partially isolated applications without having to rewrite or recompile the application code—simply by providing an application manifest and testing. For example, many applications may start using the updated Microsoft® Windows Common Control and get the new Windows look, without having to recode. For more information on using the updated Windows Common Control, see Using Windows XP Visual Styles.

Creating a Side-by-Side Assembly

An assembly is an atomic unit of code that is isolated, self-contained, and self-described by a manifest. It consists of one or more files that will always be provided to applications and customers as a unit. The assembly is the unit of naming, binding, versioning, and deployment.

Recall that one of the key characteristics of an assembly is that it is immutable, meaning that once shipped, the assembly never changes. If a bug fix needs to be issued for an assembly, or a new feature is added to it, a new version of the assembly must be provided, and is installed side-by-side with previous versions.

Assemblies are authored such that multiple versions of the assembly can be installed side-by-side and run at the same time in the same process. Again, creating assemblies removes the reliance on the registry for COM activation. The developer does the work to ensure the assembly can run safely side-by-side with another version. Windows XP provides the framework to support the installation and loading of these versioned assemblies.

There are two general categories of side-by-side assemblies: shared and private. A shared assembly is an assembly available for use by multiple applications. Shared side-by-side assemblies are not registered globally on the system, but are globally available to applications that specify a dependency in manifests.

A private assembly is only used by the application where it is installed. If the same private assembly is used by multiple applications, then it needs to be installed as private for each application. Since private assemblies cannot be centrally serviced, it is recommended that assemblies that need to be used by multiple applications be developed as shared assemblies.

The steps for creating a private assembly are identical to creating a shared assembly with two notable exceptions:

  • A private assembly doesn't have to be cryptographically signed, and publickeyToken is not required in the assemblyIdentity in the assembly manifest.
  • Private assemblies are installed in the application folder and can be installed with any install technology (that is, there is no requirement to use Windows Installer).
Note   Even if you plan to eventually provide an assembly as a shared assembly, you can begin the process and test it as a private assembly, and so avoid the steps of signing it and creating it as a merge module until after it's tested.

Creating a side-by-side assembly entails seven steps:

  1. Decide what resources to include in the assembly
  2. Design and author DLLs so that multiple DLLs may run safely
  3. Consider your localization strategy
  4. Create an assembly manifest
  5. Sign the assembly with your certificate
  6. Determine how to install your assembly
  7. Test

Step one: Decide what resources to include in the assembly

A typical assembly is a single DLL described by an assembly manifest. For example, the Microsoft Windows Common-Controls assembly contains the DLL COMCTL32.dll. An assembly may contain multiple files. In cases where components have tight interdependencies such that if you make changes to one component the other would also change, it makes sense to include the group of files in the same assembly. A good example of this is the Microsoft Visual C++ Runtime assembly, which includes atl.dll, mfc42.dll, mfc42u.dll, and msvcp60.dll.

When deciding what to include in your assembly, keep in mind how you intend to manage these into the future, and how you want applications to use them. For example, if you include two files in an assembly, each time you ship a new version of the assembly, you would include both files, whether or not both have changed. If you have two files with a strong interdependency, and you decide to ship as separate assemblies, you need to be aware that customers have a choice of mixing versions. If you update both assemblies, a customer may choose to use the new version of one and the older version of the other, which may or may not be compatible.

Figure 2. Types of assemblies

Step two: Design and author DLLs so that multiple DLLs may run safely

The following are guidelines for authoring a DLL to work well as a side-by-side assembly.

  1. DLLs need to be authored so that multiple versions can run at the same time in a process without impacting each other. Many applications are hosts for a myriad of plug-ins, which may have requirements for different versions of one component in the same process. A developer of a side-by-side assembly needs to ensure that the component has been authored and tested to behave as expected when multiple versions run at the same time in a process.
  2. If you plan to provide your component as a shared component on platforms other than Windows XP, then for these platforms you will continue to install the component as a single-instance shared component. For this reason, you will need to ensure your component is backward compatible; otherwise, you risk breaking applications.
  3. Evaluate the usage of objects when more than one version of your assembly is run on the system. Determine whether different versions of the assembly require separate data structures, such as memory-mapped files, named pipes, registered Windows messages and classes, shared memory, semaphores, mutexes, and hardware drivers. Any data structures used across assembly versions must be backward-compatible versions. Decide which data structures can be used across versions, and which data structures must be private to a version. Determine whether shared data structures require separate synchronization objects, such as semaphores and mutexes.
  4. Assign versions to all data structures.
  5. Your DLL should not depend upon sharing across versions that may not exist—for example, upon shared memory sections that are not shared across different versions of your assembly.
  6. By default, some objects are uniquely named per process, for example, window classes and Win32 Atoms. Objects such as window classes should be versioned per assembly using the manifest. For objects such as Win32 Atoms, use version-specific identifiers unless you plan to share across versions. If you opt for version-specific identifiers, use the four-part versioning number.
  7. Include no self-registering code in any DLL.
  8. Define all version-specific names in your DLL with #define statements. This enables all registry keys to be changed from one location. When you release a new version of your assembly, you will only need to change this #define statement. For example:
    #define MyRegistryKey "MyAssembly1.0.0.0"
    
  9. Store any nonpersistent data in the TEMP directory.
  10. Do not put user data into global locations. Keep application data separate from user data.
  11. Assign all shared files a file version that depends upon the application's name.
  12. Assign all messages and data structures used across processes a version to prevent unintentional cross-process sharing.
  13. If you add new functionality that does not follow the binary interface compatibility contract of the original DLL, you must assign a new CLSID, ProgId, and file name. Future versions of your side-by-side assembly will then be required to use this CLSID, ProgId, and file name. This prevents a conflict when a version of the DLL that is not side-by-side is registered.
  14. If you reuse the same CLSID or ProgId, test to ensure that the assembly is backward compatible.

In addition, adhere to the following guidelines for the storage of the state:

  1. Design state storage to be forward & backward compatible. Expect the versions to be used in any random order, for example v1, then v3, then v2.
  2. Initialize and set the default settings for the assembly in the assembly code. Do not save the defaults settings in the registry.
  3. Assemblies commonly store state information in registry keys. Author a set of header files and helper functions to provide an easy way to version registry keys containing the assembly state.
  4. Registry settings must be written on an individual version basis to isolate multiple assembly versions that may be run at the same time. Design your side-by-side assembly to correctly store and handle the state of the assembly during side-by-side sharing scenarios.
  5. Any assembly-state information saved in the registry must be isolated from other versions of the assembly. State settings stored in the registry must be saved in individual version sections of the registry. This is required in both the HKLM and HKCU parts of the registry. For example, store HKCU state settings for assembly version XXXX under the following registry key:
    HKCU\MyCompany\MyComponent\VersionXXXX\ 
    
  6. Any state information stored in the registry by shared assemblies must be saved in individual version sections of the registry. For example, a state setting called EnableSuperCoolFeature might have a value of TRUE or FALSE. Store the value for a shared side-by-side assembly as follows:
    HKEY_CurrentUser\Software\MyCompany\MyComponent\
    Version01.01\EnableSuperCoolFeature = TRUE
    
  7. Make shared-state settings stored in the registry private to the assembly context that runs. You can use the GetModuleFileName function to set up a virtual root. This should be done for HKLM and HKCU branches.
  8. Ideally, you should support a persistence model, so that the application takes responsibility for persisting your state and does not alter the registry. An application should not need to directly touch the component's registry entries. Instead, the assembly should offer API functions that save or restore settings that are side-by-side compatible.
  9. Assemblies may save state settings in stores outside the registry to enable the assembly to interact with the global state. Side-by-side assemblies may use the following side-by-side compatible stores:
    • A protected store, or pstore
    • A WinInet cache
    • A Microsoft SQL Server

Step three: Consider your localization strategy

If you are creating a component with user interface, you may want to consider a resource-only satellite DLL that will include the resources for your assembly DLLs. For more information on creating resource-only satellite DLLs, refer to Internationalizing Software and associated pages on MSDN.

These resource DLLs should be provided as side-by-side assemblies and described with assembly manifests.

Step four: Create an assembly manifest

Assemblies are described with manifests, and each assembly must have a manifest file associated with it. The manifest is an XML file that:

  • Uniquely identifies the assembly
  • Describes the files included in the assembly
  • Describes Win32 Com activation information—COM classes, interfaces, and type libraries traditionally stored in the registry
  • Describes window classes
  • Specifies dependencies on specific versions of other side-by-side assemblies

Side-by-side assemblies are not registered on the system, but are available to applications and other assemblies on the system that specify dependencies in manifest files. The operating system understands manifests, and uses the metadata in the manifest at the time of installation of the assembly and also at runtime.

When CreateProcess is called, it checks for the existence of a manifest. The metadata in the manifest is used by the DLL loader to redirect the loading of a DLL to the specific version included in the assembly. The COM server data is used to instantiate a COM object based on the manifest, and window classes will be versioned per assembly.

You may use Notepad or any standard XML editor to create your manifest. Tools will be available in the Platform SDK to assist in the creation of manifests.

The manifest may be included as a resource in private assemblies. For shared assemblies they must be separate files.

For private assemblies the assembly name should be the same name as that specified in the assemblyIdentity. For example:

<assemblyIdentity
          type = "win32"
          name = "Microsoft.Windows.SampleAssembly"
          version = "2.0.0.0"
          processorArchitecture = "x86" />
<file name = "foo.dll">

In this example, the name in the assemblyIdentity is "Microsoft.Windows.SampleAssembly". The assembly name would be "Microsoft.Windows.SampleAssembly".

For shared assembly manifests, you may use a similar approach, or you may use any file name. Upon install, the manifest will be automatically renamed by the operating system, and this name will be generated based on the assemblyIdentity.

The assemblyIdentity

Each assembly has a unique assemblyIdentity described in the assembly manifest. As in the case of the assemblyIdentity discussed earlier with reference to creating an application manifest, the assemblyIdentity described in the assembly manifest similarly identifies the name of the assembly, the version, the type of assembly, and the processor architecture on which it was intended to run.

In the case of globally shared assemblies, the assemblyIdentity must include the publickeyToken, which is a 16-character hexadecimal string representing the last 8 bytes of the SHA-1 hash of the public key under which the assembly was signed.

All references to the assembly are made using the assemblyIdentity. Using the assemblyIdentity as a reference, an application developer can specify the exact version of an assembly that the application should use.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
    version="1.0.0.0"
    processorArchitecture="x86"
    name="Microsoft.Windows.mysampleAssembly"
    type="win32"
    publickeyToken = "<insert value if this is a shared assembly>"
/>

name: Uniquely names the assembly. The recommendation is to use the following format for the name: Organization.Division.Name. For example, Microsoft.Windows.mysampleAssembly.

type: Specifies the assembly type. The value must be win32, all lower case. This is used to differentiate between Windows binaries and other technologies such as .NET runtime.

processorArchitecture: Specifies the processor of the target platform. The valid values are x86 for 32-bit Windows, and ia64 for 64-bit Windows.

version: Specifies the assembly version. Use the four-part version format: mmmmm.nnnnn.ooooo.ppppp. Each of the parts separated by periods can be 0-65535 inclusive—left-to-right, the major, minor, build, and revision parts are separated by periods. Adopt a method for numbering bug fixes for your assembly. Change the major or minor number of an assembly when doing significant feature releases that may risk compatibility with existing applications or when a change is introduced that is known to be incompatible with earlier versions. Change only the build and revision parts for backward-compatible service releases such as bug fixes and security fixes to the assembly. For example, a developer might adopt a numbering method in which all 1.0.0.* version numbers refer bug-fix versions to assembly version 1.0.0.0.

language: Identifies the language of the assembly. If the assembly is language-specific, specify the DHTML language code. If the assembly is for worldwide use (language neutral) omit this attribute.

publickeyToken: A 16-character hexadecimal string representing the last 8 bytes of the SHA-1 hash of the public key under which the assembly is signed. Use Pktextract.exe (a tool provided in the Platform SDK) to extract the publickeytoken from the certificate file of your public key. This must be the same key you will use for signing the assembly catalog, and the key must be at least 2048 bits or greater.

The description element contains an optional description of the assembly. In the dependency section, include the assemblyIdentity for the specific side-by-side assembly(s) used by your assembly.

For example, if your assembly has dependencies on Microsoft GDIPlus and on Microsoft Common-Controls, you would include the following:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
    version="1.0.0.0"
    processorArchitecture="x86"
    name="Microsoft.Windows.mysampleapp"
    type="win32"
publickeyToken = "<insert value if this is a shared assembly>"
/>
<description>Your app description here</description>
<dependency>
    <dependentAssembly>
        <assemblyIdentity
            type="win32"
            name="Microsoft.Windows.GdiPlus"
            version="1.0.0.0"
            processorArchitecture="x86"
            publicKeyToken="6595b64144ccf1df"
            language="*"
        />
    </dependentAssembly>
</dependency>
<dependency>
    <dependentAssembly>
        <assemblyIdentity
            type="win32"
            name="Microsoft.Windows.Common-Controls "
            version="6.0.0.0"
            processorArchitecture="x86"
            publicKeyToken="6595b64144ccf1df"
            language="*"
        />
    </dependentAssembly>
</dependency>
</assembly>

Each of the files included in the assembly should be listed. For example:

    <file name="sampleu.dll"/>
    <file name="bar.dll"/>
       
Note   For shared assemblies, the manifest will be updated with the hash of the file (a hexadecimal value that represents the hash of the file) and the hashalg value (the algorithm used to create the hash) when you sign the files in step 5 below.

Include COM data as appropriate per file. If the file exports COM classes, also include the comClass element, which has the following attributes:

  • description: The class name.
  • clsid: The GUID that uniquely identifies the class.
  • threadingModel: The threading model used by in-process COM classes.
  • tlbid: The GUID for the type library for this COM component.
  • progid: The version–dependent programmatic identifier associated with the COM component.

For example:

    <file name="sampleu.dll">
        <comClass description="Font Property Page"
    clsid="{0BE35200-8F91-11CE-9DE3-00AA004BB851}"
          threadingModel = "Both"
             tlbid = "{44EC0535-400F-11D0-9DCD-00A0C90391D3}"/>
        <comClass description="Color Property Page"
    clsid="{0BE35201-8F91-11CE-9DE3-00AA004BB851}" 
    progid="ABC.Registrar"/>
    </file>
    

If the file contains type-library information, include the typelib element with the following attributes:

  • tlbid: The unique ID of the type library.
  • version: The two-part version number of the type library.
  • helpdir: The directory where the Help file for the types in the type library is located.
  • resourceid: The hexadecimal string representation of the locale identifier (LCID).
  • flags: The hexadecimal representation of the type library flags for this type library. These are the values of the LIBFLAGS enumeration, and are the same flags specified in the uLibFlags parameter to ICreateTypeLib::SetLibFlags. These flags cannot have leading zeros or the 0x prefix. For example:
   <file name="sampleu.dll">
       <typelib tlbid="{44EC0535-400F-11D0-9DCD-00A0C90391D3}"
       version="1.0" helpdir=""/>
    </file>

For most automation interfaces, such as those derived from IDispatch, the default proxy-stub implementation is adequate. However, the interface proxy stub, like all other external proxy-stub interface implementations, must be listed in <comInterfaceExternalProxyStub> elements under the top-level <assembly> tag.

If a file in your assembly implements a proxy stub, the corresponding <file> tag must contain a child element <comInterfaceProxyStub> whose attributes are the same as for a <comInterfaceExternalProxyStub>. If you omit some of the comInterfaceExternalProxyStub dependencies for your component, marshalling interfaces between processes and threads may not work as expected.

The attributes are as follows:

  • iid: The IID of the interface for which the proxy is being declared.
  • proxyStubClsid32: The CLSID of the proxy. If this is omitted, the IID is used as the CLSID. The value should be in the form: "{clsid}".
  • file: The path to the external file that implements the proxy. This attribute is optional. Use environment variables to refer to paths within the Windows directory. For example: "%SystemRoot%\system32\oleaut32.dll".
  • baseInterface: The IID of the interface from which the one described by the iid attribute is derived.
  • numMethods: The number of methods implemented by the interface.
  • name: The name of the interface as it would appear in code. For example, "IViewObject". This should not be a descriptive string.
  • tlbid: The type library that contains the description of the interface specified by the iid attribute.

For example:

<comInterfaceExternalProxyStub 
  name="IAxWinAmbientDispatch" 
    iid="{B6EA2051-048A-11D1-82B9-00C04FB9942E}"
      proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"
    numMethods="35" 
  baseInterface="{00000000-0000-0000-C000-000000000046}"/>

The windowClass

The windowClass is the name of the window class that is to be versioned.

<file name="comctl32.dll">
        <windowClass>ToolbarWindow32</windowClass>
        <windowClass>ComboBoxEx32</windowClass>
        <windowClass>msctls_trackbar32</windowClass>
       

If you are providing resource-only satellite DLLs (most usefully in the case of language-dependent resources for globalization), do so as a satellite assembly (which will include the DLLs), and describe with a manifest.

The assemblyIdentity will be similar to the main assembly manifest. The only differences will be

  • name: Should be the same as the base assembly with the addition of "Resources". For example, if the name is "Microsoft.Tools.VisualCPlusPlus.Runtime-Libraries" in the base assembly, the name in the resource assembly will be "Microsoft.Tools.VisualCPlusPlus.Runtime-Libraries.Resources".
  • language: Identifies the language of the resource assembly.
  • file: Includes the list of files that are resource DLLs.

The following is an example of the manifest for Microsoft.Tools.VisualCPlusPlus.Runtime-Libraries resources assembly.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <assemblyIdentity
        type="win32"
        name="Microsoft.Tools.VisualCPlusPlus.Runtime-Libraries.Resources"
        version="6.0.0.0"
        processorArchitecture="X86"
        language="DE"
        publicKeyToken="6595b64144ccf1df"
        />
    <file name="mfc42deu.dll"/>
</assembly>

The base assembly describes an optional dependency on the resource assembly. So in this example, if a user is running Windows with the locale designated as German, an application using the Microsoft.Tools.VisualCPlusPlus assembly will have the text displayed in German.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <assemblyIdentity type="win32" 
    name="Microsoft.Tools.VisualCPlusPlus.Runtime-Libraries"
    version="6.0.0.0" processorArchitecture="x86"
    publicKeyToken="6595b64144ccf1df"/>

    <dependency optional="yes">
        <dependentAssembly>
            <assemblyIdentity type="win32" 
    name="Microsoft.Tools.VisualCPlusPlus.Runtime-Libraries.Resources" 
    version="6.0.0.0" processorArchitecture="x86" 
    publicKeyToken="6595b64144ccf1df" language="*"/>
        </dependentAssembly>
    </dependency>
Note   Refer to the Platform SDK for assembly schema.

Step five: Sign the assembly with your certificate

Note   This step applies only to shared assemblies.

The certificate used for signing the assembly must be at least 2048 bits or greater. Signing the assembly generates a secure envelope around the assembly for verification of the assembly's integrity. Before installing the shared assembly to the global side-by-side store (WinSXS), the operating system verifies the assembly signature. The publicKeyToken in the assemblyIdentity must match the public key of the certificate used for signing. This makes it difficult to spoof the publisher.

Most of the work is done with three tools—Mt.exe from the Platform SDK, and MakeCat.exe and signcode.exe from the CryptoAPI SDK. Mt.exe has two major roles: it generates hashes for each of the files in the assembly, and updates the corresponding <file> tags to include both the resulting value and the algorithm used. It creates a catalog description file (.cdf). This file is used by MakeCat.exe to create a security catalog based on the assembly manifest.

Here's an example of the contents of a candidate manifest file before running Mt.exe:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <assemblyIdentity 
        type="win32" 
        name="Microsoft.Windows.MySampleAssembly" 
        version="1.0.0.0" 
        processorArchitecture="x86"         
        publicKeyToken="6b95b9abf345378f"/>
    <file name="myfile.dll"/>
</assembly>

Note that The publicKeyToken value is already included. The assemblyIdentity is complete, and the assembly contains only one file, myfile.dll. For the tool to work correctly, the assembly files must be in the same directory as the manifest that's being worked on. The default method of running Mt.exe is as follows (refer to the Platform SDK for more details):

c:\ MySampleAssembly>mt.exe 
        -manifest MySampleAssembly.manifest -hashupdate -makecdfs
Microsoft (R) Side-By-Side Manifest Tool 1.0.0.0
Copyright (C) Microsoft Corporation 2000-2001.  All Rights Reserved.

Here's what the same manifest looks like after running Mt.exe:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <assemblyIdentity 
        type="win32" 
        name=" Microsoft.Windows.MySampleAssembly" 
        version="1.0.0.0" 
        processorArchitecture="x86"         
        publicKeyToken="6b95b9abf345378f"/>
    <file name="myfile.dll"
hash="a1d362d6278557bbe965a684ac7adb4e57427a29" hashalg="SHA1"/>
</assembly>

Notice that the SHA-1 hash of the file has been added. Do not change this value. In addition to updating your file, the "-makecdfs" option generates a file called "MySampleAssembly.manifest.cdf" that describes the contents of the security catalog that will validate the manifest.

The next step is to run MakeCat.exe (available in the CryptoAPI) over the generated .CDF to create the security catalog for the assembly, as follows:

c:\MySampleAssembly>makecat MySampleAssembly.manifest.cdf
Succeeded
c:\MySampleAssembly>

The final step is to use Signcode.exe to cryptographically sign the catalog file with the certificate used to generate the public key token.

Note   Signcode.exe only works with "Software Publisher Certificates." See the CryptoAPI SDK for more details on .SPC files and how to generate them from certificate files.
c:\MySampleAssembly>signcode -spc mycompany.spc -v mycompany.pvk -i 
http://www.mycompany.com/MySampleAssembly -t 
http://timestamp.verisign.com/scripts/timstamp.dll 
MySampleAssembly.cat
Succeeded.

c:\MySampleAssembly>

With these three steps, you can generate a signed assembly consisting of the assembly manifest, its verification catalog, and the assembly files. Changing the contents of the manifest after the generation of the assembly catalog will invalidate the catalog and the assembly. If you must update the manifest after creating/signing the catalog file, rerun these steps to update the manifest again. Likewise, if the assembly files of the assembly change, you will need to rerun these steps.

Step six: Determine how to install your assembly

Shared assemblies must install with a Windows Installer package merge module (unless installed as part of an operating system install). Windows Installer version 2.0 or later is required to install shared assemblies. (For more information, see the Windows Installer SDK.) The assembly will be installed in the Winsxs folder, a new folder in the Windows folder. Files installed into this folder per machine will be protected by Windows File Protection.

Private assemblies install with any valid installer. They must be installed private to the application. Shared assemblies should not be installed to the application folder on Windows operating systems older than Windows XP. These older operating systems have no support for central servicing. Instead the application should consume the component from the system folder.

Step seven: Test

Validate that the assembly has been installed as expected and is available for use. For example, if you have created and deployed a shared assembly, this is the version that should be used. Use a tool such as Depends.exe (available in the Platform SDK) or any debugger to check where the assembly is loaded.

Review guidelines for authoring side-by-side assemblies, and test that the assembly behaves well as a side-by-side assembly. For example, start two instances on the application, and test each of the components using scenarios to ensure that it is a valid side-by-side assembly. In other words, ensure that multiple versions running at the same time do not conflict with each other.

Use the event log to track reasons for failures.

Validate that the assembly manifest is consistent with the assembly schema, and is well-formed XML. Use the manifest validation tool Manifestchk.vbs, provided in the Platform SDK.

Servicing Shared Assemblies and Applications

Unlike statically linking DLLs, the Windows XP configuration enables assembly publishers, application publishers, and administrators to change side-by-side assembly dependencies after deployment. It provides a model for safely servicing assemblies and managing which side-by-side assemblies an application uses.

The application manifest describes an application's dependencies on specific versions of side-by-side assemblies. The assembly manifest describes the assembly's dependencies on specific versions of side-by-side assemblies.

When the application runs, the operating system computes the assemblies required by the application by traversing the chain of dependencies starting with the application manifest, and reviewing each of the dependent assembly manifests. The assembly versions specified in the assembly and application manifests comprise the default configuration that the application should use at activation time. Figure 3 depicts the configuration hierarchy.

Figure 3. Configuration hierarchy

The operating system looks to find the version of the assembly that the application needs. By default, each application will request and receive the version of the assembly used at build and test time. The exact version of the assembly is required; otherwise, the load will fail.

Applications and administrators can override the default assembly configuration by installing a publisher configuration or per-application configuration.

For example, let's assume you are an assembly publisher, and you find a fatal security flaw in your assembly. You make a new version of the assembly available and want all applications to start using the new version. Rather than requiring all applications to recompile against the new version—and have customers go through the process of re-installing applications—you can issue a new version of your assembly. At the same time, you can issue a publisher configuration to redirect all applications to use this version. Publisher configuration files override the default configuration specified by the application manifest and assembly manifests.

Typically, a publisher configuration would be installed during a service pack installation containing an assembly update. For example, an administrator might deploy an assembly update redirecting all applications and assemblies on the system that use assembly version 1.0.0.0 by default to use version 1.1.0.0 instead. In this case, the new assembly version must be backward compatible.

Figure 4. Example of servicing assemblies

Let's take the case of a publisher issuing a publisher configuration for a specific assembly that redirects all applications and assemblies having a default dependence on one version of a side-by-side assembly to use another specified version. If an application relies on a bug in the previous version of the assembly, the application author or network administrator has the option, on a per application basis, of overriding the default configuration and publisher configuration. This redirects the dependence of a specific application from one version of a side-by-side assembly to another specified version of the assembly. Issuing an application configuration should be the exception, as it offers the application a way of overriding the publisher configuration, in the event the publisher redirects to an incompatible version, or if the application only functions with an older version. Overriding publisher configuration should be used with caution and be done only by administrators. It is not recommended that application authors ship with overrides in the application configuration file, as they may bypass valuable security updates installed by the administrator centrally.

Note   The publisher configuration applies only to shared assemblies.

Providing the Publisher Configuration

Providing the Publisher Configuration entails four steps:

  1. Create a new version of a side-by-side assembly
  2. Create the publisher configuration manifest
  3. Sign the publisher configuration
  4. Provide as an install package

Step one: Create a new version of the side-by-side assembly

Begin by following the steps to create a side-by-side assembly as outlined in the section above, "Creating an Assembly." Once you've created the new assembly, test to ensure it is backward compatible.

Next, change the assembly version number. It is recommended that you change only the build and revision portions of the number for backward-compatible changes to the assembly.

Finally, sign the assembly with the same certificate used for the previous version of the assembly.

Step two: Create the publisher configuration manifest

The publisher configuration describes on a per assembly basis the versions of an assembly that should be redirected. In this case, the manifest is an XML file that:

  • Uniquely identifies the assembly to be redirected
  • Describes the version or range of versions to be redirected
  • Is installed and managed as an assembly into the Policies folder in WinSxS; the publisher configuration file is renamed (to a name based on the assembly identity) upon install to this folder

You may use Notepad or any standard XML editor to create your manifest. Save the file with any valid name.

The following publisher configuration file example redirects binding from version 2.0.0.0 of the Microsoft.Windows.SampleAssembly to version 2.0.1.0.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
   <assemblyIdentity type="win32-policy" publicKeyToken="75e377300ab7b886"
   name="policy.2.0.Microsoft.Windows.SampleAssembly" version="2.1.0.0"
   processorArchitecture="x86"/>
   <dependency>
      <dependentAssembly>
         <assemblyIdentity type="win32"
   name="Microsoft.Windows.SampleAssembly"  processorArchitecture="x86"
   publicKeyToken="75e377300ab7b886"/>
         <bindingRedirect oldVersion="2.0.0.0" newVersion="2.0.1.0"/>
      </dependentAssembly>
   </dependency>
</assembly>

The assemblyIdentity

Multiple publisher configuration assemblies may exist on the system. The operating system will choose the appropriate version based on the application manifest. Each publisher configuration assembly has a unique assemblyIdentity.

name: Uniquely names the assembly. It must follow the format policy.major.minor.name. It must begin with policy, then major and minor version, then the name of the assembly being redirected.

The major and minor version refers to the version of the assembly that should be affected. For example, if you want to affect binds to the x86 Microsoft.Windows.foo 6.0.0.0 worldwide assembly, the name would appear in the manifest like this:

   <assemblyIdentity 
     type="win32-policy" 
     publicKeyToken="75e377300ab7b886" 
     name="policy.6.0.Microsoft.Windows.Foo" 
     version="2.1.0.0" 
     processorArchitecture="x86"/>

Again, type specifies the assembly type. The value must be win32-policy, all lower case. publickeyToken, version, language, and processorArchitecture follow the same rules as for assemblies noted earlier.

In the dependency section, include the assemblyIdentity for the specific side-by-side assembly to which the configuration applies. Note that the version number is not included in the assemblyIdentity in this case.

   <dependency>
      <dependentAssembly>
         <assemblyIdentity 
type="win32" 
name="Microsoft.Windows.SampleAssembly"  
processorArchitecture="x86"
publicKeyToken="75e377300ab7b886"/>
         <bindingRedirect oldVersion="2.0.0.0" newVersion="2.1.0.0"/>
      </dependentAssembly>
   </dependency>
</assembly>

The bindingRedirect

oldVersion: Specifies the assembly version being overridden and redirected. It can be a single version or a range of versions. Specify a range of versions by a dash with no spaces. For example: 2.14.3.0 or 2.14.3.0–2.16.0.0.

newVersion: Specifies the replacement assembly version.

Both should follow the conventions for four-part version syntax nnnnn.nnnnn.nnnnn.nnnnn that applies to assemblies.

Step three: Sign the assembly with your certificate

Follow the same procedure as applies to signing a shared assembly, and use the same certificate used for the assembly.

Step four: Provide as an install package

Provide for install as either a Windows Installer package or an operating system install (for example, Windows Update, or OS service pack).

A publisher configuration should never be provided as a merge module, for installing a publisher configuration has a global impact on the system. Instead, a publisher configuration may be provided as a Windows Installer MSI.

Microsoft® Publisher Configuration for side-by-side assemblies will typically be installed with operating system service packs or QFEs available on Windows Update. The assembly will be installed in the WinSxS folder in the Policies folder. Windows File Protection will protect files that are installed into this folder on a per machine basis.

Providing the Application Configuration

Providing the Application Configuration entails two steps:

  1. Create an application configuration manifest
  2. Provide as an install package

Step 1: Create the application configuration manifest

Application configuration overrides the publisher configuration and the default application configuration on a per application basis.

Note This bypass requires an entry in the Microsoft Application Compatibility database on Windows Server 2003-based computers. This setting can be added only by administrators or by Microsoft in a software update.

The manifest is an XML file that:

  • Uniquely identifies the assembly to be redirected
  • Describes the version or range of versions to be redirected
  • Is installed into the application folder

The following application configuration file example redirects binding from version 2.1.0.0 of the Microsoft.Windows.SampleAssembly to version 2.0.0.0.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<configuration>
   <windows>
      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
         <assemblyIdentity  
processorArchitecture="X86" 
name="Microsoft.Windows.mysampleapp" 
type="win32" 
version="1.0.0.0"/>
         <dependentAssembly>
 <assemblyIdentity 
type="win32" 
processorArchitecture="x86" 
name="Microsoft.Windows.SampleAssembly"
   publicKeyToken="75e377300ab7b886"/>
    <bindingRedirect oldVersion="2.1.0.0" newVersion="2.0.0.0"/>
         </dependentAssembly>
      </assemblyBinding>
   </windows>
</configuration>

Configuration

windows: Identifies that the redirection applies to Win32 assemblies. This is designed so that in the future, when an application is composed of Win32 and .Net assemblies, both can use the same application configuration manifest. The .NET assemblies will be included under the <runtime> element.

The assemblyBinding is used to include application configuration redirects.

The assemblyIdentity uniquely identifies the application and should match the application configuration.

In the dependency section, include the assemblyIdentity for the specific side-by-side assembly to which the application override configuration applies. Note that the version number is not included in the assemblyIdentity in this case.

For example:

   <dependency>
      <dependentAssembly>
         <assemblyIdentity type="win32" 
name="Microsoft.Windows.SampleAssembly"  
processorArchitecture="x86" 
publicKeyToken="75e377300ab7b886"/>
         <bindingRedirect oldVersion="2.0.0.0" newVersion="2.1.0.0"/>
      </dependentAssembly>
   </dependency>
</assembly>

The bindingRedirect

Specifications for these attributes are consistent with those specified for the publisher configuration.

Step two: Provide as an install package

The application configuration is installed in the same folder as the executable. The name of an application configuration file is the name of the application executable for which it applies, followed by .config. For example, an application configuration file that refers to example.exe would be "example.exe.config".

Notes on How to Correctly Consume a Side-by-Side Component in an Application

  • Don't rename the dll if its part of a side-by-side assembly.
  • If multiple DLLs are part of an assembly ship all of them as a unit with the manifest intact.
  • Do not tamper with the manifest of a side-by-side assembly.
  • Do not ship application configuration with <publisherPolicy apply="no"/>. This is meant for administrators to use if they have a compatibility problem.
  • Ship your application with an application manifest that lists the version of the side-by-side assembly that your application was built or tested with.
  • Always deploy the manifest file of the side-by-side assembly with the side-by-side DLLs, even if you choose to deploy to the application folder.
  • If you install your application on a computer that is running Microsoft Windows 2000 or earlier versions of Windows, do not ship the side-by-side assembly in your application folder to those operating systems. Instead, the side-by side assemblies should be used from the system folder. If you install a shared component to the system folder as part of your setup, make sure that you are not downgrading the version if one is previously present.
  • Do not use the .local feature, also known as DLL/COM Redirection on Windows.
  • Do not call the LoadLibrary function on the side-by-side assembly DLLs with an explicit full path. Instead, use static linking or use the LoadLibrary function with the raw DLL file name. For example, use "Gdiplus.dll" as the file name.

Conclusion

Windows XP provides a standardized way for applications and components to safely share Win32 assemblies and describe the version of an assembly to use at runtime. Most importantly, the operating system enforces this. Still, the path to providing customers with applications safe from the impact of changes to the system will take time and energy on the part of developers. While it may not be feasible for all shared system components to run side-by-side, (for example one wouldn't want two versions of Kernel), many component authors now have a choice of addressing the enormous problem of versioning code by taking advantage of the Windows XP support for managing different versions of assemblies. In future articles, we hope to explore how the infrastructure works, how the loader uses assemblies and manifest metadata, integration with the .Net Framework, and strategies for testing.

Appendix: Should You Provide Shared Components?

Not every shared component will be a suitable candidate for a side-by-side assembly. Here are a few considerations that providers of shared components may use in determining whether their component is a suitable candidate for a side-by-side assembly:

  1. Does your component expose a rich set of APIs that you expect several applications to use? (For example, a component such as MSHTML.)
  2. Is your component already used by several applications? (For example, a component such as COMCTL32.)
  3. Is your component a user mode component (that is, not a device driver)?
  4. Is this a component new to Windows XP?
  5. Does your component coordinate communication between applications? To be a candidate for a side-by-side assembly, it shouldn't. For example, the parts of OLE32 that coordinate communication would not make a good side-by-side candidate, as you wouldn't want two versions on your system.
  6. Is your component responsible for managing a physical or a virtual device for the system? (For example, a component such as a print spooler.) To be a candidate for a side-by-side assembly, it shouldn't.

Component providers who answer yes to questions 1 through 4, and no to questions 5 and 6, are encouraged to provide their component as a shared side-side assembly. A component not meeting these criteria may possibly be re-architected so that some parts of it can be side-by-side.

Contributors

The authors would like to gratefully acknowledge the members of the Windows XP team for their invaluable assistance, and Gary Spradling for his help in preparing the artwork for this article.

Page view tracker