Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / XML

.NET Application Boot Strapping

4.98/5 (16 votes)
20 Aug 2014CPOL11 min read 29.7K   28  
This article explains the boot strapping process of .NET Application.

Demo

The intention behind writing this article is to share one of the best practices to be followed before starting with any new technology, i.e., to know the basics behind the technology. Also, a reminder to all .NET developers to look back and see what they are missing. The intended audience of this article are the ones who are interested in knowing boot strapping process of .NET Application. Of course, developers are very smart and will be aware of these concepts. If you are already aware, it will be a point of reference or else, it will be added to your knowledge database.

Introduction

We always start learning any new technology with our buddy “Hello World” program :). If program executes, then we conclude that all pre requisites which consist of any configuration with respect to technology are all set properly and we are set to dive in. With this, we also understand the basic life cycle of a program. All developers are desirous of executing program and observe the output but are least bothered what happens internally. Of course, you can develop systems without knowing its internals as well but it’s always incomplete. Developer can always provide a fool proof solution only when he is aware of its complete life cycle. Let’s start our journey.

start

Background

Bootstrapping usually refers to the process of loading the basic infrastructure into the memory of a computer which will then take care of loading other software as needed. The process involves a chain of stages, in which at each stage a smaller simpler program loads and then executes the larger more complicated program of the next stage. When these statements are compared with our .NET application, bootstrapping includes loading of basic infrastructure (CLR) into process in which CLR further creates AppDomain (Default AppDomain) and loads all required as well as referenced assemblies to AppDomain. Let’s brush up what is CLR, AppDomain and assemblies. We will not go in depth of these statements since the intention of this article is only bootstrapping of these elements and for further information on these elements, you can refer to other articles or MSDN knowledge bank. Below mentioned are the three important entities responsible for running .NET application.

  • CLR (Common Language Runtime): It is the one responsible for managing and executing the code contained in assemblies. The core features of the CLR (such as memory management, assembly loading, security, exception handling, and thread synchronization) are available to any and all programming languages that target it.
  • AppDomain: An AppDomain is a unit of isolation and a logical container for a set of assemblies.The first AppDomain created when the CLR is initialized is called the default AppDomain; this AppDomain is destroyed only when the Windows process terminates.
  • Assembly: It’s a basic unit of deployment and versioning. Assembly will have one or more managed modules.

Using the Code

The code snippet presented here just demonstrates getting hold of current AppDomain, creating a new AppDomain and loading assembly to newly created AppDomain. The article further explains in depth about all these concepts and the one who is responsible for creating all these entities.

C#
static void Main(string[] args)
{
    // Get a reference to the AppDomain that the calling thread is executing
    AppDomain currentDomain = Thread.GetDomain();

    // Display the name of the current AppDomain.
    Console.WriteLine("Current AppDomain name :" + currentDomain.FriendlyName);

    // Create an instance of Test class
    Test testObj = new Test();

    // Call the display method from current Domain.
    // Here, the current Domain is the Default domain that is created by CLR.
    // The friendly name of the current AppDomain is always the name of the executing assembly.
    testObj.Display("Hi, Display method called from Current AppDomain");

    // Create a new AppDomain
    AppDomain domain = AppDomain.CreateDomain("MyNewDomain");

    // Display friendly name of the newly created app domain.
    // The friendly name is the one that is passed as parameter to the CreateDomain method.
    Console.WriteLine("Name of the newly created domain: " + domain.FriendlyName);

    // Load assembly Sample Test
    Assembly assemblyObj = domain.Load("SampleTest");

    Console.WriteLine("SampleTest assembly loaded in the new App Domain(MyNewAppDomain)");

    // Get the type
    Type myType = assemblyObj.GetType("SampleTest.Test");

    // Create an instance of the type.
    var type = Activator.CreateInstance(myType);

    // Invoke the display method from the newly created AppDomain
    myType.InvokeMember("Display", BindingFlags.InvokeMethod, null, type, new object[]
             { "Hi, Display method called from newly created AppDomain" });

    Console.ReadKey();
}

Behind the Scenes

start

We all know the fact that any windows/ .NET application runs in its own process. This is the awesome facility given by Windows Platform to secure applications and its data. From the above figure, you can notice that application is under a single process. One process can consists of many AppDomains. Each Process has its own copy of assemblies loaded into it. And also, you can notice that even though both the AppDomains have System.dll, there is no provision for sharing the common assemblies except Domain Neutral assemblies such as MsCorlib.dll since the concept of AppDomain is to provide complete isolation. Only those DLLs that are so integral to the .NET Framework get loaded as domain neutral fashion to maximize the resource utilization. Process performs all these functions with the help of Main thread created. Process gets its life only through the Main thread created, otherwise it's just lifeless. This is just an overall glance of the system where you can relate Process, CLR, AppDomains and assemblies.

Note: The main drawback of domain neutral assemblies is that it can never be unloaded until the application shuts down.

Summary

  • .NET/Windows application runs in its own process.
  • Each process can have more than one AppDomain.
  • No assembly can be shared between AppDomains except Domain Neutral Assemblies.
  • No assembly can be unloaded from any AppDomain, the only way to unload assembly is to unload the complete AppDomain.

Application Boot Strapping Process

Till now, you came across the actual snapshot of a .NET application with respect to process. Now let us continue how all the required components shown in the above figure get loaded into the process and how application gets its life. In .NET, all the executable applications such as Console UI Applications, Windows Forms Application, and WPF applications are all examples of self hosted applications and have managed EXE files. We know that any windows managed or unmanaged executable application should adhere to PE (Portable Executable) file format. Operating System doesn’t differentiate between .NET assemblies and Win32 executable binaries; for it, they are same normal PE files. But actual differentiation takes place only after reading the contents available in PE Header where Windows gets educated with many details like what kind of process to be created (32 OR 64), Whether assembly is managed or unmanaged?, type of file and all the required information. PE file format is actually in the form of sections.

start

It's a standard protocol that any Windows platform executable should adhere to. Each section behaves as a reference data for Windows Process to take necessary action (In short, it can be as a metadata). They all have, for default, three sections: .text, .reloc, .rsrc. The .text section contains the Import Table, the Import Address Table, and the .NET Section. The .reloc is just there to relocate the address which the Entry Point instruction jumps to (it's the only address contained in the IAT). The .rsrc section contains just the main icon for an executable, since all other resources are in the .NET Section. The .NET section consists of CLR header which provides all the information related to CLR such as version, location, size, entry point, metadata that makes assembly a managed module.

Using this data, Windows process can perform all required initializations and loading of data properly. Here, we don't walk through each section but we only consider sections required for .NET application. To make the discussion more interesting, we will draw a flowchart for the entire process which gives a clear picture.

Note: The process of boot strapping is still more complex, but I have taken out only the flavour of overall process and keeping in mind only the process related to .NET application. There exists many more intermediate stages which is not in the scope of this article.

start

The basic boot strapping process of any .NET executable

Summary of Steps Involved in Boot Strapping

  1. User double clicks .exe(Executable) file.
  2. Windows validates PE file. The standard Windows PE file header, which is similar to the Common Object File Format (COFF) header. If the header uses the PE32 format, the file can run on a 32-bit or 64-bit version of Windows. If the header uses the PE32+ format, the file requires a 64-bit version of Windows to run. So based on this information, Windows creates an appropriate process to proceed further execution.
  3. Next, it examines the 15 directory of .text section which gives information whether the executable is managed or unmanaged.
  4. If it is managed executable, then Process loads MSCorEE.dll(Shim). It is also called as Microsoft Component Runtime Execution Engine.
  5. Process examines CLR Header to find version of CLR to be loaded and educates Shim with CLR information.
  6. Shim creates instance of CLR and CLR gets initialized. It is the heart of the .NET Framework since it is the one responsible for creating CLR instance.
  7. CLR creates Default AppDomain. Post this process, CLR is also responsible for initializing memory, thread synchronization and so on.
  8. CLR loads all required as well as referenced DLLs to AppDomain. Note: Referenced DLLs are loaded on a need basis. It means all referenced DLLs are not loaded at once, it gets loaded only when data related to the respective DLL is accessed. Once loaded, it cannot be unloaded.
  9. CLR invokes Main method and the application gets launched.

CLR Bootstrapping Process

If you still dig out the activities of CLR, we can find the following operations performed by CLR at the time of its Boot Strapping process. Before the CLR executes the first line of the managed code, it creates three application domains. Two of these are opaque from within the managed code and are not even visible to CLR hosts. They can only be created through the CLR bootstrapping process facilitated by the shim — mscoree.dll and mscorwks.dll.

System Domain: The SystemDomain is responsible for creating and initializing the SharedDomain and the default AppDomain. It loads the system library mscorlib.dll into SharedDomain. It keeps track of all the domains in the process and implements functionality for loading and unloading the AppDomains.

Shared Domain: All of the domain-neutral code is loaded into SharedDomain. Mscorlib, the system library, is needed by the user code in all the AppDomains. It is automatically loaded into SharedDomain. SharedDomain also manages an assembly map indexed by the base address, which acts as a lookup table for managing shared dependencies of assemblies being loaded into DefaultDomain and of other AppDomains created in managed code. Fundamental types from the System namespace like Object, ValueType, Array, Enum, String, and Delegate get preloaded into this domain during the CLR bootstrapping process.

Default Domain: DefaultDomain is an instance of AppDomain within which application code is typically executed. While some applications require additional AppDomains to be created at runtime, most applications create one domain during their lifetime. All code that executes in this domain is context-bound at the domain level. If an application has multiple AppDomains, any cross-domain access will occur through .NET Remoting proxies.

start

The basic boot strapping process of CLR

Some Facts about CLR

  • Microsoft implemented CLR as COM server contained inside a DLL.
  • The COM server representing CLR is registered in the Windows Registry just as any other COM server.
  • The actual code of CLR is contained in a file called MSCorWks.dll for Versions: 1.0, 1.1, 2.0, and for version 4.0 it is contained in the file Clr.dll.
  • To create CLR instance in a process, you can call CLRCreateInstance function defined in MSCorEE.dll which returns ICLRMetaHost interface. A host can call GetRuntime function of ICLRMetaHost interface specifying the CLR version. This shim then loads the desired version of CLR into hosts process. In this way, you can even take care of loading CLR with required configurations.
  • The CLR can be disabled in an application which stops only managed code in execution but unmanaged code can still continue running.
  • CLR header provides all the information related to CLR such as version, location, size, entry point, metadata that makes assembly a managed module.

Some Facts about AppDomain

  • The first AppDomain created by CLR is called as Default AppDomain.
  • The default AppDomain is destroyed only when the Windows Process Terminates.
  • Objects created in one AppDomain cannot be accessed directly by code in another AppDomain. To do so, you can use marshalling (By Val or By Reference) for communication.
  • AppDomain can be unloaded. No assembly in AppDomain can be unloaded. To do so, you need to unload the AppDomain itself.
  • Each AppDomain created can be individually configured which includes configurations related to assembly loading, search paths, shadow copying and soon.
  • There is no hard-coded limit to the number of AppDomains that can be running in a single Windows Process.
  • When CLR loading the assembly to destination AppDomain, it uses the configuration settings of that particular AppDomain.
  • FirstChanceException of AppDomain is the one responsible to catch any exception occurred in the AppDomain if any object is listening to it.

Note: The bootstrapping process is very vast and complex. I have tailored it to give you a basic understanding of the process. There are many stages but have considered only the main stages responsible for launching a .NET application.

Points of Interest

It was really interesting writing this article. There is not even a single data store where I could get sequential information of bootstrapping process. There are many articles on this topic, but it was very difficult for me to map the actual flow. It was very interesting to know the concepts in depth while trying to collect this information. Hope you have enjoyed reading this article. Please do suggest if there is anything to be looked upon.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)