Introduction
Process was the basic unit of execution and isolation for running applications in Traditional Windows application development. The advantage with the model was that each application running was loaded into a separate process that automatically isolated each application from the other. For example, if an error occurred in an application, that would not affect the other applications running on the system. Also, process is defined as a security boundary that prevents applications from talking directly with each other.
This worked fine with the Traditional windows development. Things have changed a bit with the arrival of .NET runtime. The concept is much more enhanced with a new entity called "Application Domain." Application Domains are much like processes. The Application Domain is designed to act like a security boundary and confines errors and faults to a specific domain. Although, it is similar to the process, the Application Domain has some different characteristics.
What are Application Domains?
Let us understand the Application Domains (By differentiating them with a process wherever necessary).
·Application Domain is much lighter than the traditional Windows based processes.
·Application Domains are perfect for application scenarios as they require isolation without the heavy overhead associated with the traditional application process.
·The Application Domain is a virtual process that isolates applications. This guarantees that each application is independent and is not able to directly access resources within another domain.
·Application domain can have a domain level
Application Domains and CLR Hosts – Loading Default Domains
The Common Language Runtime (CLR) is the foundation upon which the Microsoft .NET strategy is built. The CLR provides an execution environment that manages running code and provides services that make software development easier. In order to start running managed code in a process, the CLR must be loaded and initialized. The host is responsible for loading the CLR. Most hosts consist of both unmanaged and managed code.
·The unmanaged code is responsible for loading the CLR into a process and converting the program into managed code.
·The managed portion of the host is responsible for creating and managing the application domains.
There are mainly 2 reasons for hosts to have managed code and unmanaged code. The primary reason is a performance issue. There is a performance associated with calling a managed code from unmanaged code or vice versa. Hence, it is a better to call the unmanaged code and then go ahead with managed code (instead of transitioning from unmanaged host code to managed user code). It is also easy to implement/manage.
These are some of the hosts:
· ASP.NET
· Internet Explorer
· Shell Executables
· SQL Server 2005
Unmanaged Portion of CLR Hosts
Let us discuss more about the unmanaged code portion of the hosts before we go into the managed portion of code. All hosts must start with an unmanaged stub. The .NET Framework provides a set of unmanaged API's the host can use to get the CLR running.
As discussed above, the unmanaged portion of CLR hosts is responsible for loading the CLR into the process. Hosts use the CorBingToRunTimeEx API to load the CLR into the process. The CorBingToRunTimeEx API takes four parameters for loading the CLR. Let us discuss each parameter in detail. The code shown below is the unmanaged API CorBindToRuntimeHost.
Listing 1
HRESULT CorBindToRuntimeHost (
[in] LPCWSTR pwszVersion,
[in] LPCWSTR pwszBuildFlavor,
[in] LPCWSTR pwszHostConfigFile,
[in] VOID* pReserved,
[in] DWORD startupFlags,
[in] REFCLSID rclsid,
[in] REFIID riid,
[out] LPVOID FAR *ppv
);· Version - As shown in Listing 1, the pwszVersion is the parameter that determines which version of Common Language Runtime is to be loaded.
· Server versus workstation - As shown in Listing 1, pwszBuildFlavor specifies whether to load the server or the workstation build of the Common Language Runtime.
· Concurrent GC - StartupFlags specifies a set a predifined flags that control garbage collection.
1. STARTUP_CONCURRENT_GC - Specifies that concurrent garbage collection should be used.
· Loader optimization - Controls whether assemblies are loaded domain-neutrally. A domain neutral assembly is an assembly that can be shared by different application domains.
1. STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN - No assemblies are loaded as domain-neutral. [Except mscorlib] This setting is called single domain because it is commonly used when the host is running only a single application in the process.
2. STARTUP_LOADER_OPTIMIZATION_MULTI_DOMAIN - All assemblies are loaded as domain-neutral.
3. STARTUP_LOADER_OPTIMIZATION_MULTI_DOMAIN_HOST All strong-named assemblies are loaded as domain-neutral.
Managed Portion of CLR Hosts
Now, the unmanaged portion of CLR host has loaded the CLR into the process. A default domain is created automatically by the CLR after it is loaded into the process. This default domain will be unloaded only after the process shuts down. This is the reason that most hosts do not run user code in the default domain as it cannot be shut down independently of the process. The ICorRuntimeHost is the interface that exposes some methods for creating application domains and unloading the same. Let us discuss some of the important methods of ICorRuntimeHost in detail. This will help us to understand ICorRunimeHost in a better manner.
Method Name
Description
CreateDomain
Creates an application domain. Gets an interface pointer of type System._AppDomain. Takes a friendly name of the Application domain.
CreateDomainEx
Creates an application domain. This method extends the functionality of the CreateDomain method by allowing the caller to pass the instance of IAppDomainSetup. IAppDomainSetup provides properties that allow the host to configure an AppDomain before calling ICorRuntimeHost::CreateDomainEx to create it. It has properties like ApplicationBase, Application Name, LicenseFile, etc.
Start
Starts the common language runtime.
Stop
Unloads the common language runtime from the current process.
UnloadDomain
Unloads the application domain represented by pAppDomain from the current process. Takes the reference of the application domain as the parameter.
EnumDomains
Returns an enumerator for the domains in the current process.
CurrentDomain
Returns an interface pointer to the AppDomain running on the current thread.
NextDomain
Returns an interface pointer to the next domain in the enumeration. Takes the EnumDomains as the parameter.
GetDefaultDomain
Returns an interface pointer of type _AppDomain, representing the default domain for the current process.
ICLRRunTimeHost is the interface developed for the CLR version 2.0. This interface is the superseded interface of ICorRuntimeHost. This interface has a new method called as SetHostControl. SetHostControl must be called before the common language runtime is initialized, that is, before a call to the Start method. It is always recommended to call SetHostControl immediately after a call to CorBindToRuntimeEx [Unmanaged API].
Creating Application Domains and Executing User code
The host can create application domains to run user code with security, isolation and unloading. It is not a better idea to run user code in the default domain. Let us list down the reasons for the same:
· A default domain cannot be unloaded until the process exists
· Security Isolation
Now that the host has created an application domain, we can execute user code in the domain. All code that is run in the CLR must be part of an assembly. An assembly is the deployment unit for types and resources and is the primary unit of execution in the CLR. The manner in which assemblies are loaded for execution depends largely on the host's specific scenario.
In general, there are two options:
· The first option is to load precompiled assemblies from a disk.
· The second option is to create assemblies on the fly using the API's in the System.Reflection.Emit namespace.
The code shown in Listing 2 is for creating an ApplicationDomain and executing an assembly into it. The code is written in C#. The Assembly that is loaded is an exe file.
Listing 2
ObjAppDomain = AppDomain.CreateDomain("My Application 1");
ObjAppDomain.ExecuteAssembly(@"C:\APPDOMAIN_Test.exe");The code shown below is for unloading the Application domain from the memory.
Listing 3
AppDomain.Unload(ObjAppDomain);
Code Access Security and Application Domains
All the managed code is subject to code access security. It is granted a set of code access permissions that determines what resource types it can access and what types of privileged operations it can perform. The Microsoft .NET Framework security system uses evidence to authenticate code to grant permissions. Evidence is used by the .NET Framework security system to identify assemblies. Code access security policy uses evidence to help grant the right permissions to the right assembly. Evidences can be classified of 2 types. Location related evidences and Author related evidences. The location related evidences can have a URL the assembly is obtained from or a directory the assembly is obtained from.
Code access security policy is configured by administrators and it determines the permissions granted to the assemblies. There are four types of policies identified for code access security.
· Enterprise - Used to apply Enterprise wide policy.
· Machine - Used to apply machine level policy.
· User - Used to apply Per-user policy.
· Application domain-level policy - ASP.NET code access security process is an example of Application domain level policy.
Execution of an Assembly by loading it into Application Domains
An assembly is said to be domain-neutral when its code can be shared by all domains in the process. The runtime host decides whether to load assemblies as domain-neutral when it loads the runtime into a process.
There are three options for loading domain-neutral assemblies:
Load no assemblies as domain-neutral, except Mscorlib, which is always loaded domain-neutral. This setting is called single domain because it is commonly used when the host is running only a single application in the process.
Load all assemblies as domain-neutral. Use this setting when there are multiple application domains in the process, all of which run the same code.
Load strong-named assemblies as domain-neutral. Use this setting when running more than one application in the same process.
In case of loading assemblies as domain-neutral, memory use and performance are at stake. The performance of a domain-neutral assembly is slower if that assembly contains static data or static methods that are accessed frequently. Access to static data or methods is slower because of the need to isolate applications. Each application domain that accesses the assembly must have a separate copy of the static data or method to prevent references to objects in static variables from crossing domain boundaries. As a result, the runtime contains additional logic to direct a caller to the appropriate copy of the static data or method. This extra logic slows down the call.
An assembly is not shared between domains when it is granted a different set of permissions in each domain. This can occur if the runtime host sets an application domain-level security policy. Assemblies should not be loaded as domain-neutral if the set of permissions granted to the assembly is to be different in each domain.
Conclusion
The CLR is the foundation on which the Microsoft .NET has been built. The CLR will be loaded into the memory by the host. A host can be an ISAPI filter, internet explorer or a Database engine. This is based on the type of the application. Hosts call the CorBindToRuntimeEx API to load the CLR into a process. There are four values a host can set when calling CorBindToRuntimeEx. These settings control which CLR gets loaded and how basic functions like garbage collection and class loading will behave in the process. The four settings are: version, server versus workstation, concurrent GC, and loader optimization. This will be he unmanaged portion of the host.
In order to begin running managed code, a host must obtain a pointer to an application domain. In many cases, this is the default domain within the process, but the host can also create additional domains as well. The GetConfiguration method on ICorRuntimeHost provides access to an interface called ICorConfiguration that can be used to configure specific aspects of the CLR that will be loaded into the process, or to register for additional events.
References