Now that we've examined how to customize the AppDomain creation process, lets go back to our EchoAppDomainManager and look at some other places the CLR lets a managed host customize its behavior. Looking back at the output of running HelloWorld with the EchoAppDomainManager setup, we see:
It looks like the CLR is grabbing the HostSecurityManager a few times while running HelloWorld. Like AppDomainManager, HostSecurityManager is just a base class from which custom HostSecurityManagers can derive. The base HostSecurityManager provides several virtual properties and methods that work as customization points for the host, in the same way that AppDomainManager's properties and methods allow a host to customize the CLR's behavior. In Whidbey, the customization points are:
HostSecurityManager also has a Flags property, which lets the CLR know which of the above customization points the HostSecurityManager would like to provide. As you can see from the above table, the HostSecurityManager gets to participate in almost every aspect of the CAS system, from providing policy via DomainPolicy, to providing evidence that will be resolved against that policy via ProvideAppDomainEvidence and ProvideAssemblyEvidence, to actually performing policy resolution via ResolvePolicy. Obviously this is a pretty powerful hook into the CLR!
To see what's getting called when, lets create an EchoHostSecurityManager in the same vein as our EchoAppDomainManager. After creating this class, I'll just modify the HostSecurityManager property of the EchoAppDomainManager to point to our new EchoHostSecurityManager, and rerun the hello world application. One small note, there were a few bugs in the beta 1 release of HostSecurityManager. The output that I show here is on a build of Whidbey which has these bugs fixed, so if you run this code on beta 1 you'll see some slightly different output.
As an enhancement to the EchoHostSecurityManager, to demonstrate more clearly what's going on, I've modified it to display the assembly that's being resolved and any evidence provided to it when its asked to resolve policy. This makes the output from the HelloWorld application is much more verbose, as would be expected. I'll split it up into two portions, one for each AppDomain. Control starts in the default domain:
Following along in the output, we see that the first call to the HostSecurityManager is to get the domain policy for the default AppDomain. (Actually the first call is to the Flags property ... every time the CLR is making this call, its checking to see if the current HostSecurityManager wants to participate in the following call or not. Since our HostSecurityManager specifies that it wants to participate in all calls, every call to Flags will be followed by a call to the method that the corresponding flag is for).
Next, is a call to ResolvePolicy, which is attempting to resolve the policy on the HelloWorld.exe file. This all seems ok so far, but the next two calls might seem a little strange given that we've already resolved the policy for HelloWorld.exe. Why are we being asked to provide evidence for HelloWorld and then resolve the policy again? It turns out that the first call to ResolvePolicy is done by the loader, in order to ensure that the assembly meets the minimum requirements to even load. The reason that there's no call to ProvideAssemblyEvidence for this first ResolvePolicy is that there isn't yet an Assembly object to represent HelloWorld.exe, since it hasn't been loaded. Once we've loaded HelloWorld.exe, now we have a chance to provide additional evidence via the next call to ProvideAssemblyEvidence. Then we need to resolve the policy again, since any extra evidence we assign wouldn't be useful unless the policy was re-evaluated.
OK, so that all makes sense, but what's with the next call to ProvideAssemblyEvidence, after HelloWorld tries to create the new domain? From the output, that call is still occurring in the default domain, where we've already resolved the evidence on HelloWorld.exe. This call is because in HelloWorld we call CreateDomain, but don't pass any evidence in. In that case, CreateDomainHelper is going to fall back on a set of default evidence for the entry assembly of the current domain. That means that this call to ProvideAssemblyEvidence is actually a prequel to the upcoming call to ProvideAppDomainEvidence. In fact, you'll see that any evidence you return from this method will be the default set of evidence for the next ProvideAppDomainEvidence call.
In the second AppDomain, the sequence of calls should make sense:
First we set the AppDomain policy, then we ProvideAppDomainEvidence (a call that couldn't be done in the default domain, since a domain did not yet exist to load the HostSecurityManager in). That sets up the security context of the AppDomain, so now we can begin to load assemblies into it. So, the next call to HostSecurityManager is to provide the evidence for HelloWorld.exe, and then resolve the policy on that assembly.
A couple of things to note here. The PermissionRequestEvidence you see bubbling through the calls to ResolvePolicy are actually a nicely wrapped up version of the assembly's declarative security. Each PermissionRequestEvidence object contains a property for Requested, Optional, and Denied permission sets. You can use this information to help you figure out what type of policy to apply.
How would you ever use this feature? As you can imagine there are lots of uses. One scenario I can think of would be for an application that wants to grant FullTrust to every assembly that's part of the application. However, this application might support third party plugins. If these plugins will require FullTrust, then the HostSecurityManager might look like this: