Goal
This article gives an overview of the Windows side-by-side assemblies technology, and then presents a set of C++ interfaces you can use to programmatically retrieve the side-by-side relevant information out of a manifest embedded in any image file.
Introduction
Since Windows XP, a new feature is available in the Operating System which enables different versions of a dynamic link-library to co-exist on a system without conflicting with one another. This feature, which is called side-by-side assemblies, resolves a problem which was known as "DLL Hell" where one library could overwrite another version (newer or older) regardless of whether or not any application would still need the previous one.
Ever noticed the size of the "\Windows\WinSxS" folder? Depending on the type of version of Windows (Home, Ultimate, Server...) and applications installed, it can grow up to several GB and more, with 20,000 folders and 80,000 files! By taking a closer look at the content of the directories hosted by WinSxs, you will notice that many of them store the same dynamic link-libraries, each having the same name but different paths.
As an example, seven different versions (8.0.7600.xxxxx) of the IEFRAME.DLL library are located in the side-by-side store of my system. All of these can potentially be used by any application built with different versions of this library.
Assembly Cache
The WinSxS directory is sometimes called the assembly cache or the side-by-side store. Starting with Windows Vista and Windows Server 2008, it is named the Component Servicing Infrastructure (CSI) component store.
This store hosts not only side-by-side assemblies (dynamic link-libraries), but also other types of files like all the standard Windows built-in applications, manifests, and even help files.
"All of the components in the Operating System are found in the WinSxS folder - in fact, we call this location the component store. Each component has a unique name that includes the version, language, and processor architecture that it was built for. The WinSxS folder is the only location that the component is found on the system; all other instances of the files that you see on the system are "projected" by hard linking from the component store...."
(http://blogs.technet.com/b/askcore/archive/2008/09/17/what-is-the-winsxs-directory-in-windows-2008-and-windows-vista-and-why-is-it-so-large.aspx)
WinSxS Aware Applications
In order to use components located in the side-by-side store, applications must be compiled with a set of special tags. When an application starts, the Windows loader analyzes the application dependencies declared in the Import Address Table (IAT) of the image file, and then the Side-by-Side Manager tries to locate the referenced dynamic link-libraries.
One of the ways to make an application WinSxS-aware is to embed a manifest inside the image file of the application. The following illustration shows the RT_MANIFEST
of NOTEPAD.EXE when opened in Visual Studio 2008.
When exported, the XML file representing the manifest of NOTEPAD.EXE looks like this:
As shown in the illustration above, NOTEPAD.EXE is WinSxS-aware while it integrates, in its manifest, a <dependency>
block containing a <dependentAssembly>
section.
The <assemblyIdentity>
block contains several parameters (some are mandatory, some not) used to declare (manifest) to the system the expectations of the application regarding specific assemblies of the side-by-side store.
To reference an assembly from the side-by-side store, an application must provide some parameters like name, version, processor architecture, language, and public key token of the library. This kind of decoration allows a very granular selection of the library to consume among any other potentially previously (and future) installed versions of it.
The side-by-side feature is not only available for .NET but also for any standard Windows application and library. An application can be side-by-side dependent of libraries, but a library can also be side-by-side dependent of other libraries. This is a common Windows dynamic link-library issue.
The Import Address Table (IAT), the application manifest, the SxS Manager, and the side-by-side store work together as illustrated below:
When an application is launched, the system goes through the following steps:
- The loader inspects the Import Address Table (IAT) of the application and discovers all directly and indirectly imported libraries. The IAT contains only library names and extensions (e.g., advapi32.dll, gdi32.dll, winspool.drv, ...ntdll.dll) but no path information. The system itself has to find out where the referenced libraries are physically located. This is exactly where the DLL Hell used to begin...and where the side-by-side mechanism jumps into action - if an application contains a manifest.
- The SxS Manager locates and reads the application manifest and constructs the name of the fully qualified path of the libraries referenced by the application. This path is computed based on the information contained in the manifest.
- When an application references a side-by-side library, the loader does not search for it in the typical locations (application folder, Windows system directories, etc...) but exclusively in the side-by-side store.
The illustration below shows the (implicitly) imported libraries of NOTEPAD.EXE, using Dependency Walker (www.dependencywalker.com), containing an entry in its Import Address Table (IAT) which tells the system that it imports the COMCTL32.DLL library.
For those of you who know Dependency Walker, you know that it has an option to show the path of the dependent libraries.
As previously mentioned, no path whatsoever is stored in the image file in regard of the imported library. As you see in the illustrations above, Dependency Walker is showing two different kinds of paths:
- Almost all paths reference the typical System32 folder where the system has found the referenced libraries. If there is no manifest or if no entry has been found for a specific library, the system loads the library from the typical locations (system directory, application directory, etc.).
- One path entry references the WinSxs store where the specific version of the COMCTL32.DLL library (based on its version, name, language - culture, token...) has been found. In the case of NOTEPAD.EXE, only COMCTL32.DLL has been declared as to be retrieved from the side-by-side assemblies store.
How does Dependency Walker obtain the path of this COMCTL32.DLL library? By reading the application manifest, identifying the dependency, and concatenating out of the XML parameters the path of the assembly within the WinSxS directory.
As shown above, the image file of NOTEPAD.EXE contains a manifest that tells the system that it wants to use the Microsoft.Windows.Common-Controls which should be located in the side-by-side component store. From the point of view of the SxS Manager, this will map to COMCTL32.DLL!
The illustration below shows two different versions of the COMCTL32.DLL libraries found in the WinSxS folder of my system:
When NOTEPAD.EXE is launched, the WinSxS Manager reads the manifest of NOTEPAD.EXE and identifies a dependency with one side-by-side assembly. It reads its different parameters and constructs with these fields a path:
Position
|
Parameter
|
Description
|
1
|
type
|
Win32 (x86)
|
2
|
name
|
Microsoft.Windows.Common-Controls
|
3
|
publicKeyToken
|
6595b64144ccf1df
|
4
|
version
|
6.0.0.0
|
5
|
language
|
* (none)
|
For more details about these fields, please look at the description provided by Microsoft at http://msdn.microsoft.com/en-us/library/aa374219(v=VS.85).aspx.
The path where the Windows loader should look for a side-by-side library is based on five parameters that are read from the manifest, each separated with one underscore.
Out of these parameters, the following fully qualified name of the COMCTL32.DLL library has been built as can be seen when running Dependency Walker on NOTEPAD.EXE:
In case a side-by-side library that is referenced by an application through its manifest is missing, the loader will complain that the correct application environment has not been established and the program will not load. This will occur with any missing (implicit) library.
This kind of situation also occurs when you try to analyze (using Dependency Walker) an application whose dependency has not been found in the WinSxS store:
As mentioned in the message box that appears, a look at the Event Log reveals the cause of the error:
The well known quick fix of copying the missing library into the local application directory or the Windows directory will not fix the error. This is because the Windows loader looks only for the side-by-side assemblies used by the application in the WinSxS store. For this reason, any side-by-side assembly must be deployed in the WinSxS store!
A closer look at the error message of the Event Log shows an interesting hint to a utility called "sxstrace". Please try it, and you will see how the Windows probing does its best when trying to locate resources referenced by an application....
The illustration below is an extract of the myapp.txt file that contains the probing activities of the system when locating the libraries to load:
=================
Begin Activation Context Generation.
Input Parameter:
Flags = 0
ProcessorArchitecture = x86
CultureFallBacks = en-US;en
ManifestPath = C:\temp\MyApp.exe
AssemblyDirectory = C:\temp\
Application Config File =
-----------------
INFO: Parsing Manifest File C:\temp\MyApp.exe.
INFO: Manifest Definition Identity is myapp.processorArchitecture="x86",
type="win32",version="5.1.0.0".
INFO: Reference: Microsoft.Windows.Common-Controls,language="*",
processorArchitecture="*",publicKeyToken="6595b64144ccf2df",
type="win32",version="6.0.0.0"
INFO: Resolving reference Microsoft.Windows.Common-Controls,language="*",
processorArchitecture="*",publicKeyToken="6595b64144ccf2df",
type="win32",version="6.0.0.0".
........
ERROR: Cannot resolve reference Microsoft.Windows.Common-Controls,language="*",
processorArchitecture="*",publicKeyToken="6595b64144ccf2df",
type="win32",version="6.0.0.0".
ERROR: Activation Context generation failed.
End Activation Context Generation.
Sample
The sample application provided here presents a set of C++ classes, developed with Visual Studio 2008, that you can use to programmatically retrieve the different items of any side-by-side assembly referenced in the manifest file of an application.
When working on a project that handles portable executable files and their dependencies, I came across on the issue presented here and the fact that the path for WinSxS libraries has to be retrieved at runtime.
Since I did not find any valuable C++ sample showing how to retrieve the fields of side-by-side dependencies referenced in a manifest, I decided to develop it on my own and to make the code available here. Enjoy it.
Benefit
You can use this set of classes presented here to validate applications during the installation of a program, to analyze executable files for their dependencies, to develop a tool that gathers statistical information about applications, or to develop any kind of forensic tool.
Using the code
Using the classes provided is really straightforward. Simply instantiate a CPeManifest
object with the name of the Portable Executable file containing a manifest to analyze:
CString s = "C:\temp\myapp.exe";
CPeManifest manifest(s);
const IPeManifestAssemblyIdentity* pIdentity = m_pPeManifest->GetFirstDependency();
while( pIdentity )
{
m_listDependencies.InsertString( iPos, pIdentity->GetName() );
m_listDependencies.SetItemDataPtr( iPos++, (void*)pIdentity );
pIdentity = m_pPeManifest->GetNextDependency();
}
Later on, you can point the select dependency in your code and retrieve the associated parameters, like this:
void CQueryManifestDlg::OnLbnSelchangeListDependentAssemblies()
{
int iPos = m_listDependencies.GetCurSel();
IPeManifestAssemblyIdentity* pIdentity =
(IPeManifestAssemblyIdentity*)m_listDependencies.GetItemDataPtr(iPos);
if(pIdentity)
{
m_architecture = pIdentity->GetProcessorArchitecture();
m_token = pIdentity->GetPublicKeyToken();
m_type = pIdentity->GetType();
m_language = pIdentity->GetPublicLanguage();
m_version = pIdentity->GetVersion();
}
UpdateData(FALSE);
}
Links
History
- 17 June 2010: First published.