Understanding Process, Application Domain And Assemblies

Process

 
A process is an operating system concept and it is the smallest unit of isolation provided by Windows OS. Each application or piece of code runs in Windows runs under the boundary of a process. Application isolation makes sure the failure of one process does not affect the functioning of another process. It is also necessary to ensure that code running in one application cannot adversely affect other, applications.
 
When you run an application, Windows creates a process for the application with a specific process id and other attributes. Each process is allocated with necessary memory and a set of resources.
 
Every Windows process contains at least one thread which takes care of the application execution. Processes can have many threads and they speed up execution and give more responsiveness but a process that contains a single primary thread of execution is considered to be more thread-safe.
 
In Figure 1, you can see how the processes running in the machine are listed. Each process has name, id, description, etc. and each process can be uniquely identified using process ID.
 
System.Diagnostics provides a number of classes to deal with processes including Process class.
 
.Net
 
Figure 1
 
Let us try an application using C# to demonstrate working with the process. The code in Listing 1 explains two things,
 
How to start a process, how to kill/terminate a process.
 
  1. using System;  
  2. using System.Diagnostics;  
  3.   
  4. namespace SampleProcess  
  5. {  
  6.     public class SomeProcess  
  7.     {  
  8.         public static void Main(string[] args)  
  9.         {  
  10.             Process myProcess = new Process();  
  11.   
  12.             try  
  13.             {  
  14.                 // myProcess.StartInfo.UseShellExecute = false;  
  15.                 // You can start any process, HelloWorld is a do-nothing example.  
  16.                 myProcess.StartInfo.FileName = @"C:\Users\[UserID]\source\repos\BMIRuleEngine\BMIRuleEngine.exe";  
  17.                 //  myProcess.StartInfo.CreateNoWindow = true;  
  18.                 myProcess.Start();  
  19.                 // The process you are starting should terminate itself.  
  20.                 // If you set CreateNoWindow = true, the application will run without a window and  
  21.                 // it must terminate itself or you can do it programmatically from this application using the Kill method.  
  22.   
  23.                 // Do your other tasks here  
  24.   
  25.                 // Let us kill the process which we created because my application doesn’t terminate by itself.  
  26.                 Process[] processes = Process.GetProcessesByName("BMIRuleEngine");  
  27.                 foreach(Process process in processes)  
  28.                 {  
  29.                     process.Kill();  
  30.                 }  
  31.             }  
  32.             catch (Exception ex)  
  33.             {  
  34.                 Console.WriteLine(ex.Message);  
  35.             }  
  36.         }  
  37.     }  
  38. }  
Listing 1
  • StartInfo - gets or sets the properties to pass to the Process.Start() of the Process.
  • StartInfo.CreateNoWindow - help you specify whether you need to run the application with a window or without a window.
  • StartInfo.UseShellExecute - help you specify whether to use the operating system shell to start the process or not. If you set UseShellExecute=True, the application will use the operating system shell to start processes. With OS shell, you can start any document (which is any registered file type associated with an executable that has a default open action) and perform operations on the file, such as printing, by using the Process object. If you set UseShellExecute to false, you can start only executables by using the Process object.
  • Arguments – help you specify arguments to be passed to the application.
There are many other useful properties available for the Process class, please check it out.
 
Application Domain
 
Application Domain or AppDomain is one of the most powerful features of the .NET framework. AppDomain can be considered as a light-weight process. An application domain is a .NET concept whereas the process is an operating system concept. Both have many characteristics in common. A process can contain multiple app domains and each one provides a unit of isolation within that process. Most importantly, only those applications are written in .net, or in other words, only managed applications will have application domain.
 
Application domain provides isolation between code running in different app domains. App Domain is a logical container for code and data just like process and has separate memory space and access to resources. App domain also serves as a boundary like a process that does avoid any accidental or illegal attempts to access the data of an object in one running application from another.
 
System.AppDomain class provides you ways to deal with the application domain. It provides methods to create a new application domain, unload domain from memory, etc.
 
Why we need App Domain while we have Process
 
This is the first question that came to my mind while I first heard about application domains.
 
A Process is much heavier and expensive to create and maintain. So, the server would have a tough time managing the processes. So, when Microsoft developed the .Net framework, they introduced the concept of Application Domain and it is an integral part of the .net framework.
 
While AppDomains still offer many of the features that a Windows process offers, they are more efficient than a process by enabling multiple assemblies to be run in separate application domains without the overhead of launching separate processes. An App Domain is relatively cheaper when compared to the process to create, and has relatively less overhead to maintain.
 
Think about a server that hosts hundreds of applications. Before the app domain was introduced, each running application used to create one process. AppDomain is of great advantage for ISP (Internet Service Provider) who hosts hundreds of applications. Because each application can be contained in an application domain and a process can contain many such AppDomains, thus providing a lot of cost-saving.
 
In short, any process running a managed application will have at least one app domain in it. Since AppDomain is a .NET concept, any process running unmanaged code will not have any application domain.
 
Figure 2 will help you understand the concept better.
 
.NET
 
Figure 2
 
Process A runs managed code with one application domain while Process B runs managed code has three application domains. Note that Process C which runs unmanaged code has no application domain.
 
Code and data are safely isolated using the boundary provided by the AppDomain. If two AppDomains want to communicate with each other pass objects, .NET techniques such as Remoting or Web Services should be used.
 
Application Domain Example
 
Create a Console Application
 
I am using Visual Studio 2017 for this demo.
 
File -> New -> Project
 
From the left pane, Visual C# -> Windows classic desktop
 
On the right pane, choose Console App.
 
Alternatively, you can type the console app in the search box which is located at the top right corner of the window, and choose the type of solution you want to create.
 
There will be a Console App (.NET Framework) and Console App (.NET Core).
 
If you are going to use the application only in windows, choose .NET framework one.
 
.NET Core project will help you create a command-line application that can work on .NET Core on Windows, Linux, and macOS.
 
For our purpose, the .NET framework is enough.
 
There is a Visual C# version and a Visual Basic version for the above two types of application, choose the right one based on which language you are going to use.
 
I am going to use C# one instead of the VB one.
 
Name the project as AppDomainSample and click OK.
 
.NET
 
Figure 3
 
Add the below code shown in Listing 2 to the Program class and build the application.
 
I will show you how to create an application domain.
  1. using System;  
  2.   
  3. namespace AppDomainSample  
  4. {  
  5.     class Program  
  6.     {  
  7.         static void Main(string[] args)  
  8.         {  
  9.             // Get the current application domain  
  10.             AppDomain appDomain1 = AppDomain.CurrentDomain;  
  11.             Console.WriteLine("App Domain Name : '{0}'", AppDomain.CurrentDomain.FriendlyName);  
  12.             Console.WriteLine("ID of the Domain : '{0}'", AppDomain.CurrentDomain.Id);  
  13.             Console.ReadKey();  
  14.         }  
  15.     }  
  16. }   
Listing 2
 
You can see in Figure 4; an application domain is created by default and code is running under that app domain.
 
.NET
 
Figure 4
 
To create a new app domain, just add the following line of code.
  1. AppDomain newAppDomain = AppDomain.CreateDomain("NewAppDomain");  
It will create an application domain with the name NewAppDomain.
 

Assemblies

 
As per Microsoft, “An assembly is a collection of types and resources that forms a logical unit of functionality. All types in the .NET Framework must exist in assemblies; the common language runtime does not support types outside of assemblies. An assembly is a unit at which security permissions are requested and granted.”.
 
In more simple terms, when you create a console application, or a windows forms application or a class library, or other types of applications in .NET, you are creating an assembly. Assembly can be a .exe or .dll. A console application creates and .exe while a class library creates a .dll.
 
When you add a reference to another project or a .dll in your application, you are loading an assembly into your project. It is a static way of assembly binding since you know which assembly to be loaded before compilation. So, it is easy for you to create objects of classes in those assemblies and to use their methods.
 
There are situations when you will have to dynamically load an assembly during runt time. System.Reflection.Assembly class provides different ways to load Assemblies – Assembly.Load(), Assembly.LoadFrom(), Assembly.LoadFile().
 
Use these methods to load an assembly, into the current application domain.
 
Assembly Loading Example
 
I am going to create another console application.
 
This time I am leaving the default name as it is.
 
This will create a new Console Application with the name ConsoleApp1.
 
.NET
 
Figure 5
 
Just added a line of code in the Main method and added GetAssemblyName() as a public method.
  1. using System;  
  2.   
  3. namespace ConsoleApp1  
  4. {  
  5.     public class Program  
  6.     {  
  7.         static void Main(string[] args)  
  8.         {  
  9.             Console.WriteLine("Assembly Loading Demo.");  
  10.         }  
  11.   
  12.         public string GetAssemblyName()  
  13.         {  
  14.             Console.WriteLine("This is ConsoleApp1!");  
  15.             Console.WriteLine("App Domain Name : '{0}'", AppDomain.CurrentDomain.FriendlyName);  
  16.             Console.WriteLine("ID of the Domain : '{0}'", AppDomain.CurrentDomain.Id);  
  17.             return AppDomain.CurrentDomain.FriendlyName;  
  18.         }  
  19.     }  
  20. }   
Listing 3
 
Creating an object of a class/type in the dynamically loaded assembly and Dynamically invoking a method with Reflection
 
Now go back to the AppDomainSample project and add the below code to the Main method.
  1. // Use the path to load the assembly into the current application domain.  
  2. Assembly a = Assembly.LoadFrom(@"C:\Users\u23027\source\repos\ConsoleApp1\ConsoleApp1\bin\Debug\ConsoleApp1.exe");  
  3. // Get the Program class of the ConsoleApp1 to use here.  
  4. Type programClass = a.GetType("ConsoleApp1.Program");  
  5. // Get the method GetAssemblyName method to make a call.  
  6. MethodInfo getAssemblyName = programClass.GetMethod("GetAssemblyName");  
  7. // Create an instance o.  
  8. object programObject = Activator.CreateInstance(programClass);  
  9. // Execute the GetAssemblyName method.  
  10. getAssemblyName.Invoke(programObject, null);  
  11. Console.ReadKey();  
Listing 4
 
You need to add a reference to the System.Reflection namespace.
 
The assembly ConsoleApp1.exe will be loaded to the same application domain as the AppDomainSample is running.
 
You can verify this by looking at the output of the program as shown in Figure 6.
 
The assembly is loaded onto the current AppDomain even when you statically load assemblies, I mean by adding a reference to your project before compilation.
 
.NET
 
Figure 6
 
Dynamically invoking a static method with Reflection
 
For invoking a static method, you only need to do a few changes to the code for invoking an instance method.
 
You don’t need to create an instance of the class.
 
In this example, you can comment on the line:
  1. object programObject = Activator.CreateInstance(programClass);  
While invoking the method, you don’t need to pass the object as a parameter, so pass it as null.
 
So, the code will change like:
  1. getAssemblyName.Invoke(nullnull); 
Memory and Performance
 
As we have seen the assemblies are loaded onto the current application domain. This may create memory issues when there are large assemblies to load or the number of assemblies increases.
 
So, it is better to unload the unwanted assemblies once it is executed.
 
You can use the Unload method of the AppDomain class to unload the contents (all assemblies) of an application domain.
  1. AppDomain.Unload(appDomainObject);  
But, once an assembly is loaded an application domain, you cannot unload the assembly alone from the AppDomain.
 
To overcome this, we can create a new Application Domain and load the new assemblies there. Then, after the new assemblies are executed, you can safely unload the new app domain.
 
This approach has two benefits,
  1. All the unwanted assemblies are removed from the memory.
  2. Main application thread and related references are maintained in the memory.
Load an assembly into the application domain and execute it using AppDomain class
 
Create a new application domain.
  1. AppDomain newAppDomain = AppDomain.CreateDomain("NewAppDomain");  
NewAppDomain is the name of the AppDomain.
  1. newAppDomain.ExecuteAssembly(@"C:\Users\u23027\source\repos\ConsoleApp1\ConsoleApp1\bin\Debug\ConsoleApp1.exe");  
The above line of code will load the assembly ConsoleApp1 into the NewAppDomain application domain and will start executing it.
 
After you have done executing the new assembly, you can unload it using:
  1. AppDomain.Unload(newAppDomain);  
To test if ConsoleApp1 is loaded into the new application domain, go to the ConsoleApp1 project and add the following two lines to the Main method.
  1. Console.WriteLine("App Domain Name : '{0}'", AppDomain.CurrentDomain.FriendlyName);  
  2. Console.WriteLine("ID of the Domain : '{0}'", AppDomain.CurrentDomain.Id);  
Now build the ConsoleApp1 project and build and run the AppDomainSample project.
 
.NET
 
Figure 7
 
You can see that the ConsoleApp1 project is loaded into the NewAppDomain application domain we created.
 
Source code is attached to this article.
 
You can try other methods available with Assembly, AppDomain, and Process class.
 
References
  • https://msdn.microsoft.com/en-us/library/system.diagnostics.process(v=vs.110).aspx
  • https://docs.microsoft.com/en-us/dotnet/framework/app-domains/application-domains
  • https://docs.microsoft.com/en-us/dotnet/framework/deployment/best-practices-for-assembly-loading
  • https://msdn.microsoft.com/en-us/library/43wc4hhs(v=vs.100).aspx
  • https://docs.microsoft.com/en-us/dotnet/framework/app-domains/how-to-load-assemblies-into-an-application-domain
  • https://www.c-sharpcorner.com/UploadFile/84c85b/clr-internals-process-and-application-domain/
  • https://www.dotnetspider.com/forum/ViewForum.aspx?ForumId=139934
  • https://www.codeproject.com/Articles/597398/Loading-Assemblies-using-Assemb