ENTERPRISE

Programming .NET Components : Remoting - Application Domains (part 2) - The AppDomain Class, The Host App Domain

8/8/2012 6:16:35 PM

4. The AppDomain Class

.NET represents app domains with the AppDomain class, which provides numerous methods for loading assemblies, creating objects from those assemblies, and configuring app domain security. You can get hold of an object representing the app domain within which your component code is currently running by accessing the static property CurrentDomain of the AppDomain class:

    AppDomain currentAppDomain;
    currentAppDomain = AppDomain.CurrentDomain;

Alternatively, you can call the GetDomain( ) static method of the Thread class, which also returns an AppDomain object representing the current domain:

    AppDomain currentAppDomain;
    currentAppDomain = Thread.GetDomain( );

Every app domain has a readable name. Use the FriendlyName read-only property of the AppDomain class to obtain the name of the app domain:

    AppDomain currentAppDomain;
    currentAppDomain = AppDomain.CurrentDomain;

    Trace.WriteLine(currentAppDomain.FriendlyName);

4.1. The default app domain

Every unmanaged process hosting .NET components is created by launching a .NET EXE assembly, such as a console application, a Windows Forms application, or a Windows Service application. Each such EXE has a Main( ) method, which is the entry point to the new app domain in the process. When the EXE is launched, .NET creates a new app domain called the default app domain. The name of the default app domain is that of the hosting EXE assembly (such as MyApp.exe). The default app domain cannot be unloaded, and it remains running throughout the life of the hosting process. For diagnostic purposes, you can verify whether your code executes in the default app domain using the IsDefaultAppDomain( ) method of the AppDomain class:

    AppDomain currentAppDomain;
    currentAppDomain = AppDomain.CurrentDomain;
    Debug.Assert(currentAppDomain.IsDefaultAppDomain( ));

When debugging inside Visual Studio 2005, the EXE assembly is actually loaded in the VSHost process. Consequently, in a debug session of the MyApp.exe assembly, the default app domain's name will be MyApp.vshost.exe.


4.2. Creating objects in app domains

The AppDomain class offers a few permutations of a CreateInstance( ) method that allows you to explicitly create a new instance of any type in the app domain. For example, one of the versions of CreateInstance( ) is called CreateInstanceAndUnwrap( ), defined as:

    public object CreateInstanceAndUnwrap(string assemblyName, string typeName);


					  

CreateInstanceAndUnwrap( ) accepts an assembly filename and a fully qualified type name. CreateInstanceAndUnwrap( ) then creates an instance of the type and returns an object representing it. Example 1 demonstrates CreateInstanceAndUnwrap( ).

When you specify an assembly name to any of the methods of AppDomain, the calling assembly must reference the assembly being specified.


Example 1. Explicitly creating an object in the current app domain
//In the MyClassLibrary.dll assembly:
namespace MyNamespace
{
   public class MyClass
   {
      public void TraceAppDomain( )
      {
         AppDomain currentAppDomain;
         currentAppDomain = AppDomain.CurrentDomain;
  
         Console.WriteLine(currentAppDomain.FriendlyName);
      }
   }
}
 
//In the MyApp.exe assembly:
using MyNamespace;
  
public class MyClient
{
   static void Main( )
   {
      AppDomain currentAppDomain;
      currentAppDomain = AppDomain.CurrentDomain;
      Console.WriteLine(currentAppDomain.FriendlyName);
  
      MyClass obj;
      obj = (MyClass)currentAppDomain.CreateInstanceAndUnwrap("MyClassLibrary",
                                                            "MyNamespace.MyClass");
      obj.TraceAppDomain( );
   }
}
//Output:
MyApp.exe //Traces MyApp.vshost.exe when running in the debugger


					  

In this example, a class called MyClass is defined in the MyNamespace namespace in the MyClassLibrary.dll class library assembly. MyClass provides the TraceAppDomain( ) method, which traces the name of its current app domain to the Console window. The client is in a separate EXE assembly called MyApp.exe. The client obtains its current AppDomain object and traces its name. When running outside the debugger, the trace yields MyApp.exe—the name of the default app domain. Next, instead of creating an instance of MyClass directly using new, the client calls CreateInstanceAndUnwrap( ), providing the assembly name and the fully qualified type name. When the client calls the TraceAppDomain( ) method on the new MyClass object, the object traces MyApp.exe to the Console window because it shares the app domain of the client (the default app domain, in this example).

The client can use CreateInstanceAndUnwrap( ) to create types defined in its own assembly, by providing its own assembly name. Note that CreateInstanceAndUnwrap( ) uses the default constructor of the object. To provide construction parameters you need to use another version of CreateInstanceAndUnwrap( ), which accepts an array of construction parameters:

    public object CreateInstanceAndUnwrap(string assemblyName, string typeName,        
                                                       object[] activationAttributes);


					  

The client can also specify explicitly how to bind to the server assembly, using yet another overloaded version of CreateInstanceAndUnwrap( ).

Interacting with app domains is usually required by framework vendors who want to explicitly create new app domains and load assemblies and types into them. I find that during conventional development I need to interact with app domains only to configure security policies or for advanced security purposes. 


4.3. Creating a new app domain

You typically create new app domains for the same reasons you create processes in traditional Windows development: to provide fault isolation and security isolation. The AppDomain class provides the static CreateDomain( ) method, which allows you to create new app domains:

    public static AppDomain CreateDomain(string friendlyName);

CreateDomain( ) creates a new app domain in the same process and returns an AppDomain object representing the new app domain. The new app domain must be given a new name when you call CreateDomain( ).

The AppDomain type is derived from MarshalByRefObject. Deriving from MarshalByRefObject allows .NET to pass a reference to the AppDomain object outside its app domain boundaries. When you create a new app domain using the CreateDomain( ) method, .NET creates a new app domain, retrieves a reference to the AppDomain object, and marshals it back to your current domain.


Example 2 demonstrates how to create a new app domain and then instantiate a new object in the new app domain.

Example 2. Creating a new app domain and a new object in it
//In the MyClassLibrary.dll assembly:
  
namespace MyNamespace
{
   public class MyClass : MarshalByRefObject
   {
      public void TraceAppDomain( )
      {
         AppDomain currentAppDomain;
         currentAppDomain = AppDomain.CurrentDomain;
  
         Console.WriteLine(currentAppDomain.FriendlyName);
      }
   }
}
 
//In the MyApp.exe assembly:
using MyNamespace;
  
public class MyClient
{
   static void Main( )
   {
      AppDomain currentAppDomain;
      currentAppDomain = AppDomain.CurrentDomain;
      Console.WriteLine(currentAppDomain.FriendlyName);
  
      AppDomain newAppDomain;
      newAppDomain = AppDomain.CreateDomain("My new AppDomain");
      MyClass obj;
      obj = (MyClass)newAppDomain.CreateInstanceAndUnwrap("MyClassLibrary",
                                                            "MyNamespace.MyClass");
      obj.TraceAppDomain( );
   }
}
//Output:
MyApp.exe //Or MyApp.vshost.exe when running in the debugger
My new AppDomain
						

					  

Example 2 is similar to Example 1, with a few notable exceptions. The class MyClass is derived from MarshalByRefObject, so you can access it across app domains. The client traces its own app domain name (MyApp.exe) and then creates a new app domain using the CreateDomain( ) static method. As in Example 1, the client creates a new object of type MyClass in the new app domain and asks it to trace its app domain. When the program is executed, the name My new AppDomain is displayed in the console, confirming that the object is in the newly created app domain.

4.4. Unwrapping remote objects

You're probably wondering why the word Unwrap is appended to the CreateInstanceAndUnwrap( ) method. As already mentioned, accessing a remote object is done through a proxy. For optimization purposes, .NET separates the act of creating an object from the act of setting up a proxy on the client side. This allows you to create a remote object and set up the proxy later. The AppDomain class provides a set of CreateInstance( ) methods that create a new object but return a handle to the remote object in the form of ObjectHandle:

    public virtual ObjectHandle CreateInstance(string assemblyName, string typeName);


					  

ObjectHandle is defined in the System.Runtime.Remoting namespace. It implements the IObjectHandle interface:

    public interface IObjectHandle
    {
       object Unwrap( );
    }
    public class ObjectHandle : MarshalByRefObject,IObjectHandle
    {
        public ObjectHandle(object obj);
        public virtual object Unwrap( );
    }

The Unwrap( ) method sets up the proxy on the client side. You can actually unwrap a handle multiple times, either by the same client or by different clients. Using the same object definitions as Example 2, here is how to unwrap an object handle:

    AppDomain newAppDomain;
    IObjectHandle  handle;
    MyClass obj;
      
    newAppDomain = AppDomain.CreateDomain("My new AppDomain");
    handle = newAppDomain.CreateInstance("MyClassLibrary","MyNamespace.MyClass");
       
    //Only now a proxy is set up:
    obj = (MyClass)handle.Unwrap( );
    obj.TraceAppDomain( );


					  

Typically, there is no need to manually unwrap object handles. .NET provides the option for the advanced case in which you want to pass the handle between clients in different app domains, instead of the object itself. As a result, the client can defer loading the assembly containing the object metadata (a required step when setting up a proxy) until the client actually needs to use the object.

5. The Host App Domain

.NET calls the app domain that contains the server object the host app domain. The host app domain can be in the same process as the client app domain, in a different process on the same machine, or on a different machine altogether. To qualify as a host, the app domain must register itself as such with .NET, letting .NET know which objects the host is willing to accept remote calls on, and in what manner. Because .NET is aware of the host only after registration, the host must be running before remote calls are issued.

COM differed from .NET in that if calls were made to a hosting process that was not running, COM would launch it and let it host objects. New COM activation requests would then be redirected to the already running process. COM could do that because the Registry held the registration information regarding which process to launch and because once the process was running, it registered the objects it was hosting programmatically. .NET doesn't use the Registry (hence the limitation).


Both the client and the host app domains require access to the server assembly. The client app domain needs the server metadata to compile against, and at runtime .NET requires the server assembly on the client side so it can reflect its metadata and build a proxy. The IL code in the assembly isn't required at compile time or at runtime on the client's side. The host app domain requires the server assembly at runtime to create the hosted components and for call-marshaling purposes. If the host is doing programmatic registration , that host must have access to the server assembly at compile time as well.

Unless you explicitly create a new app domain in your own process (as in Example 2), the host app domain will be in a different process, in the form of an EXE assembly. You could put the server code in the same EXE as the host and have the client application add a reference to the host directly. However, that would make it more complicated for the client to use the same remote type when other hosts are hosting that type. The common solution to this problem is to separate the host, the server, and the client to different assemblies: the host resides in an EXE assembly, the server in a class library assembly, and the client application in a different class library or EXE assembly. That way, the client can use the metadata in the server class library at compile time and redirect the remote calls to any host that loads the same server class library. At runtime, the client loads the class library only to use its metadata; again, it has no need for the code in it. The host assembly uses the server assembly's metadata at compile time if it uses programmatic object registration; at runtime, the host uses both the metadata and the code in the server assembly to host the class library objects. Figure 4 depicts what the client and the host require of the server class library assembly at compile time and at runtime.

Figure 4. Client and host requirements for the server class library assembly

Making a class library DLL available to remote clients by hosting it in an EXE host is analogous to providing a surrogate process to a COM in-process DLL. In fact, the canonical COM surrogate process is called dllhost.exe.

Other  
  •  BizTalk 2006 : Managing Exceptions in Orchestrations (part 4) - Processing and Retrieving Messages and Exceptions from the Fault Message
  •  BizTalk 2006 : Managing Exceptions in Orchestrations (part 3) - Running the EAIProcess
  •  BizTalk 2006 : Managing Exceptions in Orchestrations (part 2) - Failed Orchestration Routing API for BizTalk 2006
  •  BizTalk 2006 : Managing Exceptions in Orchestrations (part 1) - The Exception Management Challenge
  •  D-Link DHP-1565 Wireless N Powerline Router
  •  Application Patterns and Tips : Localize a Windows Forms Application, Localize an ASP.NET Application
  •  Application Patterns and Tips : Use Model-View-ViewModel in WPF
  •  Liquid-Metal
  •  Vigor 2850n
  •  Visual Studio 2010 : Introducing the Visual Studio Extensibility - Extending the Code Editor
  •  Visual Studio 2010 : Managing Extensions with the Extension Manager, Managing Add-Ins with the Add-In Manager
  •  Intel Xeon Phi: Coprocessor speeding at 1 teraflops in a PCIe Card
  •  Visual Studio Team System 2008 : Working with Test Results (part 2) - Build report and test result
  •  Visual Studio Team System 2008 : Working with Test Results (part 1) - Test as part of Team Foundation Server build
  •  Finance - Apple Versus Google
  •  Oracle Coherence 3.5 : Testing and debugging Coherence applications
  •  Oracle Coherence 3.5 : Accessing the data grid (part 6) - Using the Coherence API - Implementing CoherenceTarget, Testing the Cache loader
  •  Oracle Coherence 3.5 : Accessing the data grid (part 5) - Using the Coherence API - Loader design, Implementing CsvSource
  •  Oracle Coherence 3.5 : Accessing the data grid (part 4) - Using the Coherence API - The basics: NamedCache and CacheFactory
  •  Oracle Coherence 3.5 : Accessing the data grid (part 3) - Configuring Coherence
  •  
    Top 10
    Intel SSD 335 Series - Better Under The Hood
    Upgrade Your Mice & Keyboards – May 2013
    Printer Upkeep: Inkjet Printer Maintenance Tips
    Printers: Inkjet vs. Laser, And More
    WD Black 4TB - 4TB Storage Goes Mainstream
    Smart Phones: Bigger, Faster, And Better Than Ever
    Choice Exotica Well Tempered Versalex Turntable (Part 2)
    Choice Exotica Well Tempered Versalex Turntable (Part 1)
    Pre/ Power Amplifier - Nagra Jazz/ MSA (Part 2)
    Pre/ Power Amplifier - Nagra Jazz/ MSA (Part 1)
    Most View
    Hashing Algorithms: Extending the .NET Framework (part 1)
    Motorola Razr
    The Second BlackBerry Developers Conference Asia (Part 2)
    IIS 7.0 : Performance and Tuning - Network
    .NET Compact Framework : Font Selection
    Programming the Mobile Web : Mobile Rich Internet Applications (part 1) - JavaScript UI Libraries
    Windows 8 All-In-One PCs On Test (Part 1) - Dell XPS One 27, Samsung Series 7 All-in-One PC
    Learn How To … Boost Your Protection Against PC Viruses
    Visual Studio Team System 2008 : TFS reports for testing - Bugs
    Oracle Coherence 3.5 : Installing Coherence, Starting up the Coherence cluster
    Dell S2740L - A Beautifully Crafted 27-inch IPS Monitor
    Photoshop School: Use Cloning To Clean Up Coastal Scenes
    Processor Group Test (Part 6) - Intel Core i7-3930K
    Programming with DirectX : Game Math - Bounding Geometry (part 2) - Bounding Spheres & Bounding Hierarchies
    Windows 7 : Mapping Your Networking Infrastructure (part 1) - Using the Network and Sharing Center
    Microsoft Content Management Server : Building SharePoint Web Parts - The SharePoint MCMS Navigation Control, Creating the Web Part Project
    Cooler Master Hyper T4 - A Step Up In Budget Cooling
    SQL Server 2008 : Index design and maintenance - Managing statistics
    Acer Aspire 5560G
    Visual Studio 2010 : Managing Extensions with the Extension Manager, Managing Add-Ins with the Add-In Manager