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.