The CLR includes a default host that enables you to use application configuration files to specify which version and build type of the CLR to use and in which garbage collection mode to run. If you don't need to customize the settings for
The configuration file used to customize the default CLR host enables you to list more than one version of the CLR with which to run the application. If multiple versions are listed, the order of the versions in the list determines their priority. However, it's always safest to list only the version of the CLR your application was built with because that's the version that offers the most predictable behavior at run time. If you choose to list additional versions, make sure you've completed sufficient testing to ensure that the other versions are backward compatible from the perspective of your application.
Finally, if you've determined that your application can run on a machine that has only .NET Framework 1.0 installed on it, you must include extra statements in your configuration file to upgrade these references manually because the CLR that shipped with .NET Framework 1.0 does not automatically upgrade all references to the .NET Framework assemblies to match the CLR.
Chapter 5. Using Application Domains Effectively
If you're writing an extensible application or have reason to unload code without shutting down an entire process, you'll likely want to take advantage of application domains. Application domains enable you to isolate groups of assemblies from others running in the same process. Oftentimes, the isolation provided by application domains is used to run multiple applications in the same Win32 process as is done by CLR
This book includes two chapters on application domains. This first chapter introduces the concept of an application domain and gives general architectural guidelines to help you make the most effective use of domains within your application. Topics covered in this chapter include the role of application domains, a discussion of their internal structure, guidelines to follow when partitioning a process into multiple application domains, the relationship between domains and threads, and the use of application domains to unload code from a running process. In Chapter 6, I describe the various ways you can customize application domains to fit your particular scenario.
The Role of Application Domains
To meet the reliability and security required by modern computing environments, operating systems and other software platforms must provide a means to isolate unrelated applications from one another at run time. This isolation is necessary to ensure that code running in one application cannot adversely affect another, thereby exploiting a security vulnerability or
The Microsoft Windows operating system uses processes to enforce this isolation. If you've used Microsoft Internet Information Services (IIS) and ASP in the past, you probably recall two configurations you could select that controlled where your application code ran relative to the code for IIS itself. In one configuration you chose to run your application in the same process as IIS. Your other option was to run your application in a different process than IIS. The
One of the reasons process boundaries
Another factor in the cost of using processes as an isolation boundary is the expense of switching the context associated with execution from one thread to another. All threads in the Windows operating system are process-relativethey can run only in the process in which they are created. When processes are used to isolate IIS from your application, the transfer of control between the two processes involves a thread context switch. This thread switch, along with the cost of translating memory address and some overhead associated with communication between processes, accounts for most of the expense associated with using processes to achieve application isolation.
Type Safety and Verification
In contrast, the CLR can statically determine how a given process will access memory before it is actually run. This is possible primarily because the Microsoft Intermediate Language (MSIL) is at a higher level and much more descriptive than native processor instructions such as x86 or Intel Architecture 64 (IA-64). The CLR relies on this more descriptive instruction set to tell whether a given assembly is type safe, that is, whether all accesses to memory made from within the assembly go through public entry points to valid objects. In this way, the CLR can ensure that a given assembly cannot access invalid
Given code that is
Figure 5-1. One process can contain multiple application domains.
Not all managed assemblies are verifiably type safe, however. If an assembly cannot be verified to be type safe, you cannot rely on application domains for isolation. Such code can make an invalid memory access just as unmanaged code can, causing the entire process to come down. Whether code is verifiable depends in part on the programming language used to write the assembly. The MSIL instruction set is built to support a wide variety of languages, including those that include support for direct pointer manipulation. As a result, your ability to generate verifiable code depends on whether the language you are using exposes some of the unsafe aspects of MSIL. For example, all code generated by the Microsoft Visual Basic.NET compiler is type safe. Visual Basic.NET exposes no language features that let you write unverifiable code. In contrast, C++ is typically not verifiable because C++ code often makes
To leverage the full benefits of application domain isolation, you must ensure that only code that can be verified to be type safe is allowed to run in your process. You do this using the CLR's Code Access Security (CAS) system. I've talked about verifiability mostly from the perspective of reliability, but the ability to verify that code won't make random memory accesses is critical for security reasons as well. Verification can be relied on to make sure that code won't
Application domains form a boundary around the types that can be called from code running in that domain. Assemblies are always loaded into a process within the context of a domain. If the same assembly is loaded by code in two domains, the assembly is loaded twice.
Assemblies loaded into the same domain can
Each application domain has a configuration file that can be used to customize everything from the local search
Applications domains can be used to further scope the CAS permissions granted to code running within the domain. This further scoping must be set up explicitly by the extensible applicationit is not enabled by default. Permission grants can be customized by the extensible application in two ways. The first is to define custom CAS policy for the domain. This policy maps a notion of code identity (called evidence) to a set of permissions that the code is granted. Examples of evidence include the location from which the code was loaded, its strong-
The second way to configure security for an application domain is to set security evidence on the domain itself. This evidence is evaluated by policy just as the evidence from an assembly is. The union of these two evaluations forms the final set of permissions that are granted to the assembly. If the grants from the domain are less than the grants from the assembly, the domain
The CAS system is so extensible that an entire chapter is dedicated to it later in the book. In Chapter 10, I describe how to implement both application domain CAS policy and domainlevel security evidence.
Access to Static Data and Members
I've discussed how application domains are used to restrict access to types and their members. However, static data and members don't require instantiation of a type, but still must be isolated per domain to prevent either accidental or malicious leaking of data across domain boundaries. This behavior usually
Runtime Concepts Not Isolated by Application Domains
Not all resources that are isolated to a process in Win32 are isolated to an application domain in the CLR. In some cases, isolating these resources to an application domain isn't necessary because of type safety or other checks implemented by the runtime. In other cases, resources aren't isolated to an application domain because they are built on the underlying Win32 primitives, which, of course, have no notion of application domains.
Examples of resources not isolated to an application domain include the garbage collection heap, managed threads, and the managed thread pool. In the case of the garbage collection heap, the notion of type safety can be relied upon so that different heaps need not be
The .NET Framework libraries provide a number of classes that enable add-ins to create and use synchronization primitives such as events and
The other important aspect of process management that hasn't yet been scoped to the application domain level is debugging. In Win32 when a thread hits a breakpoint in one process, all threads in that process are