First, a warning. When you read about the AppDomainManager class in the Microsoft
documentation, don't accidently focus on a different AppDomainManager, Microsoft.VisualStudio.Tools.Applications.Runtime.AppDomainManager.
This is an internal sealed class for use by Office tools. We need System.AppDomainManager,
which, as we have just said, is new for .NET 2.0. Let's take a look at the methods
we shall use first.
It would be reasonable to assume that the CreateDomain method is used to create
AppDomains, but this is not the case. AppDomains are, in fact, created by the AppDomainHelper
class. While this may seem counterintuitive, it is an important design pattern.
.NET code may call CreateDomain, but our AppDomainManager might choose to override
this request. For example, our AppDomainManager might maintain a pool of existing
AppDomains. Based on some criteria, perhaps security credentials, our manager might
decide to return a reference to an existing AppDomain when client code requests
the creation of a new one. If the code in CreateDomain decides that the creation
of a new AppDomain is required, that code calls AppDomainHelper, which actually
creates the new AppDomain and returns a reference to it.
Our custom AppDomainManager must also make security decisions. The CreateDomain
method expects a reference to an Evidence object (System.Security.Policy.Evidence)
as one of its arguments. The security information carried by this object will
establish the so-called "top-of-stack" permission set, which will place limits on
what code loaded by the AppDomain is permitted to do. Let's take a look at code
for a test AppDomainManager, which doesn't add any new functionality but illustrates
how a custom AppDomainManager is built and applied.
1 using
System;
2 using
System.Reflection;
3 using
System.Runtime.Hosting;
4 using
System.Security;
5 using
System.Security.Policy;
6 using
System.Threading;
7
8 namespace
AppDomainManager02
9 {
10
public sealed
class AppDomainManagerTest
: AppDomainManager
11
{
12
// (1)
13
// Only fully-trusted code can be loaded
in an AppDomainManager constructor
14
public AppDomainManagerTest()
15
: base()
16
{
17
Report(".ctor",
ConsoleColor.Red );
18
return;
19
}
20
21
// (2) We don't do any work, just pass
the request to the base class
22
public
override AppDomain CreateDomain(string
friendlyName, Evidence securityInfo,
AppDomainSetup appDomainInfo)
23
{
24
Report("CreateDomain",
ConsoleColor.DarkGreen);
25
return base.CreateDomain(friendlyName, securityInfo, appDomainInfo);
26
}
27
28
// (3) A CLR host can provide a custom
security manager to fine-tune the
29
// security behavior of an AppDomain.
This propery provides programmatic access
30
// to the HostSecurityManager. In this
example, we simply return a reference
31
// to the a new instance of the default
HostSecurityManager.
32
public
override HostSecurityManager HostSecurityManager
33
{
34
get
35
{
36
Report("HostSecurityManager",
ConsoleColor.DarkYellow);
37
return
new HostSecurityManager();
38
}
39
}
40
41
/*
42
// CreateAppDomainHelper actually creates a new AppDomain
43
// this allows CreateDomain to either return an exisiting AppDomain
from a
44
// pool or call CreateDomainHelper to create an actual new AppDomain
45
protected static AppDomain CreateDomainHelper (
46
string friendlyName,
47
Evidence securityInfo,
48
AppDomainSetup appDomainInfo)
49
{
50
51
}
52
53
public AppDomainManagerInitializationOptions InitializationFlags
{ get; set; }
54
55
public override void InitializeNewDomain(AppDomainSetup appDomainInfo)
56
{
57
Console.Write("Initialize new domain called:
");
58
Console.WriteLine(AppDomain.CurrentDomain.FriendlyName);
59
InitializationFlags =
60
AppDomainManagerInitializationOptions.RegisterWithHost;
61
}
62
63
64
*/
65
66
67
// Report() is only for learning purposes
68
// Report() displays the current AppDomain
location for whatever thread calls it
69
private
void Report(string location,
ConsoleColor color)
70
{
71
ConsoleColor
original = Console.ForegroundColor;
72
Console.ForegroundColor
= color;
73
Console.WriteLine("AppDomain: {0}, AppDomainManagerTest::{1}",
AppDomain.CurrentDomain.FriendlyName, location);
74
Console.ForegroundColor
= original;
75
}
76
private
void Report(string location)
77
{
78
Console.WriteLine("AppDomain: {0}, AppDomainManagerTest::{1}",
AppDomain.CurrentDomain.FriendlyName, location);
79
}
80
}
81 }
82
Figure 5.
Section (1) is the constructor. In this example, we only note the fact that the
constructor executes. If you want to load assemblies in the constructor, it is imperative
that these assemblies be fully-trusted, and the CLR will insist on it. To be loaded
in the AppDomain constructor, an assembly must either be in the GAC or be marked
as fully trusted in an array of strong-names provided by the invoking code.
Section (2) is where we actually create the new managed AppDomain object. This method
will be called by the CLR when a request for a new AppDomain is made by some executing
managed code. We have not yet discussed how the CLR knows to do this; this is coming
up shortly.
In section (3) we provide a HostSecurityManager object when the CLR requests one.
We could fine-tune our security be providing a custom HostSecurityManager.
The dll containing our AppDomainManager must be strongly named. Clearly, .NET needs
to trust code that controls all security within all AppDomains. A key file is included
in the zipped source files; you may wish to use this file for consistency.
As is usually the case, we will need a test platform to see if our code works. In
this case we will use a "HelloWorld" program, to keep things as simple as practical.
We will integrate a custom AppDomainManager with our progammable CodeDom application
later on. Here is the source for our HelloWorld test:
1 using
System;
2
3 public
class HelloWorld
4 {
5
public static
void Main()
6
{
7
Console.WriteLine("HelloWorld::Main()
has begun.");
8
9
// say hello in this domain
10
new HelloWorldAux().HelloWorld();
11
12
// create a new domain and say hello over
there
13
AppDomain newDomain =
AppDomain.CreateDomain("Second AppDomain");
14
HelloWorldAux remote = newDomain.CreateInstanceAndUnwrap(typeof(HelloWorldAux).Assembly.GetName().Name,
typeof(HelloWorldAux).Name)
as HelloWorldAux;
15
remote.HelloWorld();
16
return;
17
}
18 }
19
20 public
class HelloWorldAux
: MarshalByRefObject
21 {
22
public void HelloWorld()
23
{
24
AppDomainManager currentManager
= AppDomain.CurrentDomain.DomainManager;
25
if (currentManager ==
null)
26
Console.WriteLine("No Managed AppDomainManager");
27
else
28
Console.WriteLine("AppDomainManager: " + currentManager.GetType().Name);
29
30
Console.WriteLine("Hola,
mundo");
31
}
32 }
33
Figure 6.
Just as before, we must create a class in anticipation of launching it in a new
AppDomain. In this case, the class is called HelloWorldAux.
Before we can test our application, we must find some way of telling the CLR to
use our AppDomainManager. Ultimately, we will want to program our unmanaged host
to specify the manager and obtain a reference to it. Alternatively, we could change
entries in the Windows system registry so that the CLR will always load our custom
manager. Obviously, this is not the wisesst choice for the early stages of testing.
The last alternative is to set environment variables in a command console. Any CLR
loaded up by a process launched from that window will use our custom AppDomainManager.
Others will not.
A batch file to set the environment variables is included in the zipped example
files. If you prefer typing it yourself, it looks like this:
SET APPDOMAIN_MANAGER_ASM=AppDomainManager02,Version=1.0.0.0,Culture=neutral, PublicKeyToken=931530800db9d27a,processorArchitecture=MSIL
SET APPDOMAIN_MANAGER_TYPE=AppDomainManager02.AppDomainManagerTest
Obviously, each of the SET commands must be on a single line. The value for the
public key presumes that you use the strong key file included with the project files.
If you create your own key file, you will need to substitute your new public key.
By default, the CLR will not probe for the AppDomainManager assembly. This means
that there are only two choices. The assembly must be located in the same folder
as the exe file, or the assembly must be placed into the GAC.
After you copy HelloWorld.exe and AppDomainManager02.dll into the desired folder,
we are ready to test. Open a command prompt window and run the SET statements above
or run the envvars.bat batch file. Then type "HelloWorld" and hit <enter>
like you normally would. You should see something like this:

Figure 7.
Now we need to understand exactly what we are seeing. After Windows recognizes HelloWorld
for a managed executable, it loads the CLR, which creates a default AppDomain. The
CLR then creates an instance of our AppDomainManager for that new AppDomain, and
we see the constructor in our object report its execution in red. The CLR then makes
several requests for HostSecurityManager objects; we see these in yellow. The CLR
then loads and runs HelloWorld.exe. We see the execution of Main(). Main() the reports
the AppDomainManager for its AppDomain, the default AppDomain.
The code in Main() then requests the creation of a new AppDomain by calling the
CreateDomain() method of the AppDomain class. Note that Main() is not calling
our AppDomainManager. The CLR calls the CreateDomain method of the AppDomainManager
in the default AppDomain, giving it a chance to specify the characteristics of the
new AppDomain about to be created. Indeed, as mentioned our custom AppDomainManager
might even decide not to grant the request for a new AppDomain, and return a reference
to an appropriate existing AppDomain instead. The CreateDomain() method reports
its execution in green in the console. Only after this, if our managed
code requests it, does the CLR create a new AppDomain and load into it a new instance
of our AppDomainManager class. We see the instantiation of this class in the red
report in the console. Once again, the CLR requests HostSecurityManager objects.
At this point, the new AppDomain is ready for use and the HelloWorld() method executes
in the new AppDomain. It is worth nothing that the HelloWorldAux class must be loaded
in both AppDomains, or there would be no way for the default AppDomain to create
a proxy to run the code in the second domain. We may ultimately wish to consider
alternatives to this duplication.
Congratulations on getting this code running. In the next installment, we will add
some security features, making a real custom AppDomainManager, and integrate this
into our unmanged host application.
Source Code
The source code for all examples
can be found here .
About the author:
Dan Buskirk had been a research scientist for many years when he left the university
labs to participate in a startup venture to design and build databases for medical
science. Since that time he has managed a consulting practice specializing in database
design and development. Dan balances this with training on Microsoft SQL Server,
Microsoft Analysis Services, and .NET programming. His interests include mathematical
methods for data mining and computing methods for advanced data categorization on
Windows clusters.