Application Domain Structure
Internally, application domains are implemented as a set of data structures and runtime behavior
A graphical view of a CLR process and the corresponding application domain data structures are shown in Figure 5-2. Figure 5-2. The internal structure of a CLR process with multiple application domains
As shown in the figure, each application domain holds several elements related to isolation. These include the following:
These elements of the application domain data structures are described in the following sections. Assembly ListEvery assembly is loaded within the context of an application domain. The CLR maintains a list of the loaded assemblies per domain. This list is used for many purposes, one of which is to enforce the visibility rules for types within an application domain. The assembly list contains both assemblies that are written as part of the application and those that are shipped by Microsoft as part of the .NET Framework platform. Security PolicyAs described, security policy can be customized per domain. This policy includes both CAS policy and policy for role-based authorization checks. Application Domain Properties
Each application domain has a list of properties associated with it. These properties fall into two general categories: those properties that are natively
Statics for
|
|
In .NET Framework version 1.0 and .NET Framework version 1.1, the default domain has a second limitation as well: many of the configuration options that can be set on application domains you create yourself couldn't be set on the default domain. Because the default domain couldn't be customized in any way, it wasn't of much use for many extensible applications. Fortunately, the ability to customize the default domain has been added in .NET Framework version 2.0, as discussed later in this chapter. |
It's relatively easy to build a tool to help visualize the relationship among processes, application domains, and assemblies on a running system. The AppDomainViewer tool (appdomainviewer.exe) included with this book shows this relationship in a graphical tree structure. AppDomainViewer is an executable that, when started, enumerates all the processes on the system that are currently running managed code. For each process, AppDomainViewer first enumerates the application domains within the process and then enumerates the assemblies loaded into each application domain.
Figure 5-3 shows the AppDomainViewer running on a machine that runs Microsoft Visual Studio 2005 and a number of purely managed executables.
If you expand the tree view, the first level contains the application domains in the process. Notice that every process has at least one application domain. This is the default domain. When an application domain is created, it is assigned a friendly textual name. This is the name displayed in the
Expanding an application domain in the tree view shows the set of all assemblies loaded into that domain. Looking at the assemblies under each application domain gives me a good chance to expand on the concept of domain-neutral code touched on
The AppDomainViewer uses the CLR debugging interfaces to get application domain and assembly information out of running processes. It might not occur to you to use the debugging interfaces for this purpose, but it's the only technique the CLR provides for inspecting processes other than the one in which you are running. Most of the functionality provided by the debugging interfaces is aimed at debugging, of course, but a set of interfaces referred to as the publishing interfaces can be used to look inside other processes.
The interfaces ( ICorPublish, ICorPublishProcess, ICorPublishAppDomain , and a few others) are all unmanaged COM interfaces. However, the .NET Framework 2.0 software development kit (SDK) includes a set of managed wrappers around these interfaces. The AppDomainViewer is written in managed code and accesses the CLR debugging system through these managed wrappers as shown in Figure 5-4.
Listing 5-1 shows the source for the AppDomainViewer. I've omitted some of the
using System;
using System.IO;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Threading;
// Include the namespaces for the managed wrappers around
// the CLR debugging interfaces.
using Microsoft.Debugging.MdbgEngine;
using Microsoft.Debugging.CorPublish;
using Microsoft.Debugging.CorDebug;
namespace AppDomainViewer
{
public class ADView : System.Windows.Forms.Form
{
// Windows Forms control definitions omitted...
// The background thread used to call the debugging interfaces
private Thread m_corPubThread;
// The list of tree nodes to display in the UI. This list gets populated
// in RefreshTreeView.
private ArrayList m_rootNodes = new ArrayList();
// More Windows Forms setup code omitted...
[STAThread]
static void Main()
{
Application.Run(new ADView());
}
private void ADView_Load(object sender, System.EventArgs e)
{
// Populate the tree view when the application starts.
RefreshTreeView();
}
private void btnRefresh_Click(object sender, System.EventArgs e)
{
// Populate the tree view whenever the Refresh Button is clicked.
RefreshTreeView();
}
private void RefreshTreeView()
{
// The CLR debugging interfaces must be called from an MTA thread, but
// we're currently in an STA. Start a new MTA thread from which to call
// the debugging interfaces.
m_corPubThread = new Thread(new ThreadStart(ThreadProc));
m_corPubThread.IsBackground = true;
m_corPubThread.ApartmentState = ApartmentState.MTA;
m_corPubThread.Start();
}
public void ThreadProc()
{
try
{
MethodInvoker mi = new MethodInvoker(this.UpdateProgress);
// Create new instances of the managed debugging objects.
CorPublish cp = new CorPublish();
MDbgEngine dbg = new MDbgEngine();
m_rootNodes.Clear();
// Enumerate the processes on the machine that are running
// managed code.
foreach(CorPublishProcess cpp in cp.EnumProcesses())
{
// Skip this processdon't display information about the
// AppDomainViewer itself.
if(System.Diagnostics.Process.GetCurrentProcess().Id!=
cpp.ProcessId)
{
// Create a node in the tree for the process.
TreeNode procNode = new TreeNode(cpp.DisplayName);
// Enumerate the domains within the process.
foreach(CorPublishAppDomain cpad in cpp.EnumAppDomains())
{
// Create a node for the domain.
TreeNode domainNode = new TreeNode(cpad.Name);
// We must actually attach to the process
// to see information about the assemblies.
dbg.Attach(cpp.ProcessId);
try
{
// The debugging interfaces (at least for this task) are
// centered on modules rather than assemblies.
// So we enumerate the modules and find out which
// assemblies they belong to. In the general case,
// assemblies can contain multiple modules.
// This code is simpler, however. It assumes one
// module per assembly, which might yield incorrect
// results in some cases.
foreach(MDbgModule m in dbg.Processes.Active.Modules)
{
CorAssembly ca = m.CorModule.Assembly;
// Make sure we include only assemblies in this
// domain.
if (ca.AppDomain.Id == cpad.ID)
{
// Add a node for the assembly under the
// domain node.
domainNode.Nodes.Add(new TreeNode(
Path.GetFileNameWithoutExtension(ca.Name)));
}
}
}
finally
{
// Detach from the process and move on to the next one.
dbg.Processes.Active.Detach().WaitOne();
}
// Add the domain node under the process node.
procNode.Nodes.Add(domainNode);
}
m_rootNodes.Add(procNode);
}
}
// "Notify" the tree view control back on the UI thread that new
// data is available.
this.BeginInvoke(mi);
}
//Thrown when the thread is interrupted by the main thread-
// exiting the loop
catch (ThreadInterruptedException)
{
//Simply exit....
}
catch (Exception)
{
}
}
// This method is called from the MTA thread when new data is
// available.
private void UpdateProgress()
{
// Clear the tree, enumerate through the nodes of the array,
// and add them to the tree. Each of the top-level nodes represents
// a process, with nested nodes for domains and assemblies.
// m_rootNodes is constructed in RefreshTreeView.
treeView.Nodes.Clear();
foreach(TreeNode node in m_rootNodes)
{
treeView.Nodes.Add(node);
}
}
}
}