.NET Reflection
Abstract
Assemblies are the core units of deployment. At design time, we can examine the set of reference assemblies in CIL code with couple of external tools, Reflector and ildasm, to peek into the underlying metadata, MSIL code, and manifest. Hence, the triggering point behind writing this article is to gain an understanding of how to programmatically obtain the same type of information by using Reflection. Reflection offers a means by which to extend our programs in a fully dynamic way. This capability is useful for writing metadata-driven channels, generating or executing code using program input, and creating applications that make runtime decisions about what code to load and run. In addition to that, this article will also elaborate on the late binding concept, which is much related to reflection. Lastly, we'll discuss briefly the MSIL code and metadata emitting using reflection.
Learn Digital Forensics
What Is Reflection?
Reflection typically is the process of runtime discovery to inspect metadata, CIL code, late binding, and self-generating code. At runtime, using reflection, we can access the same type information as displayed by the ildasm utility at design time. The reflection is analogous to reverse engineering in which we can break an existing *.exe or *.dll assembly to explore defined significant contents information's including methods, fields, events, and properties.
You can dynamically discover the set of interfaces supported by a given type using the System.Reflection and System.Reflection.Emit namespace. The following section high level overview of Reflection APIs.
- InfoAPI: these API allows accessing the CLR type system in a truly dynamic fashion. You can construct objects, invoke methods, and access fields and properties on objects.
-
Types Description Assembly This static class allows you to load, investigate, and manipulate an assembly. AssemblyName It allows exploring abundant details behind an assembly. EventInfo It holds information about given event. PropertyInfo Holds information’s about given property. MethodInfo It contains information’s about given method. - Custom Attributes: they enable dynamic extensibility through metadata.
- Activation: they make it possible to easily construct instance of objects from a type and set of arguments. This is useful in COM interoperability.
- Delegates: they have both static and dynamic support in the runtime.
Role of Reflection
Reflection is typically used to dump out the loaded assemblies and list their references to inspect methods, properties, etc. Reflection is also used in the external disassembling tools such Reflector, Fxcop, and NUnit because .NET tools don't have to parse the source code similar to C++.
Metadata Investigation
This following program depicts the process of reflection by creating a console-based application. This program will display details of the fields, methods, properties, and interfaces for any type within the mscorlib.dll assembly. Before moving forward, it is mandatory to import System.Reflection;
Here, we are defining a number of static methods in the program class to enumerate fields, methods, and interfaces in the given type. The static method takes a single System.Type parameter and returns void.
[cpp]
static void FieldInvestigation(Type t)
{
Console.WriteLine("*********Fields*********");
FieldInfo [] fld= t.GetFields();
foreach(FieldInfo f in fld)
{
Console.WriteLine("-->{0}", f.Name);
}
static void MethodInvestigation(Type t)
{
Console.WriteLine("*********Methods*********");
MethodInfo [] mth = t.GetMethods();
foreach (MethodInfo m in mth)
{
Console.WriteLine("-->{0}", m.Name);
}
}
Once we have done with the static method. It is time to call them in the main() method, which first prompts the user for the fully qualified name of the type. Once we obtain the type in string format, we pass it into the Type.GetType() method and send the extracted Type object into each of the static methods:
[cpp]
static void Main(string[] args)
{
Console.Write("Enter the Name to Explore:");
Type t = Type.GetType(typName);
FieldInvestigation(t);
Console.ReadKey();
}
Finally, run the application and examine the content of the System.Math type when the screen prompts:
Dynamic Assembly Loading
Technically, the act of loading external assemblies on demand is known as dynamic loading. Using the Assembly class, we can dynamically load both private and shared assemblies from local to a remote location as well as explore its properties.
To illustrate dynamic loading, we create a console-based application that loads an external TestLib.dll assembly. During the execution , the application asks the user to specify the dynamic loading assembly name and that reference is passed to the helper method which is responsible for loading the assembly:
[cpp]
using System;
namespace Reflection
{
class Program
{
static void Main(string[] args)
{
Console.Write("Enter External Assembly:");
string typName = Console.ReadLine();
try
{
Assembly asm = Assembly.Load(typName);
DispalyAssembly(asm);
}
catch
{
Console.WriteLine("Can't Load Assembly");
}
Console.ReadKey();
static void DispalyAssembly(Assembly a)
{
Console.WriteLine("*******Contents in Assembly*********");
Console.WriteLine("Information:{0}",a.FullName);
Type[] asm = a.GetTypes();
foreach (Type tp in asm)
{
Console.WriteLine("Type:{0}", tp);
}
}
}
}
After successfully compiling the application, the important point to remember is that you need to copy the dynamically loading TestLib.dll binary into the solution Bin/Debug folder to run this program properly. Here, the output is:
If you wish to make this program more flexible, you can update your code to load the external assembly using the LoadFrom() method. In such a situation, you don't need to place the external assembly in the Bin/Debug folder.
[cpp]
static void Main(string[] args)
{
try
{
Assembly asm = Assembly.LoadFrom(@"E:TestLib.dll");
DispalyAssembly(asm);
}
catch
{
Console.WriteLine("Can't Load Assembly");
}
[/cpp]
We can also implement reflection on shared assemblies. We have to pass sets of information that identify an assembly such as the assembly name, version, and publicKeyToken as follows:
[cpp]
class Program
{
static void Main(string[] args)
{
try
{
string info = @"System.Windows.Forms," + "Version=4.0.0.0," +
"PublicKeyToken=B77A5C561934E089," +
@"Culture=""";
Assembly asm = Assembly.Load(info);
DispalyAssembly(asm);
}
catch
{
Console.WriteLine("Can't Load Assembly");
}
Console.ReadKey();
static void DispalyAssembly(Assembly a)
{
Console.WriteLine("Name:{0}", a.GetName().Name);
Console.WriteLine("Version:{0}", a.GetName().Version);
Console.WriteLine("Culture:{0}", a.GetName().CultureInfo.DisplayName);
Console.WriteLine("Loaded from GAC?:{0}", a.GlobalAssemblyCache);
}
}
The output of that program would:
Late Binding
The .NET framework can create an instance of a type of assembly using early binding and late binding. In early binding, we typically set the external assembly reference in the project and allocate the type using new keyword. Early binding allows us to determine errors at compile time rather than at runtime.
In late binding, you can create an instance of a given type and invoke its methods at runtime without having knowledge of compile time. There is no provision to set an external assembly reference in this construct.
We can create a late binding instance of the external assembly using the CreateInstance() method of the System.Activator static class. Here, we are dynamically instantiating the utility class of the TestLib.dll; the code is refreshingly simple:
[cpp]
using System;
namespace Reflection
{
class Program
{
static void Main(string[] args)
{
try
{
Type t = Type.GetType("TestLib.utility,TestLib");
object obj = Activator.CreateInstance(t);
MethodInfo mth = t.GetMethod("Test");
mth.Invoke(obj, null);
Console.WriteLine("Method Invoked");
}
catch
{
Console.WriteLine("Can't Create Assembly Instance");
}
Console.ReadKey();
}
}
}
Here you don't need to put the external assembly binary copy in the solution Bin/Debug folder as was done earlier. Once the code is complete and you compile this application, you obtain this output:
Emitting code
Normally the programmer defines an assembly, modules, sets of types, and methods when generating code dynamically but, using LCG (lightweight code generation), we can skip such cumbersome steps. The System.Reflection.Emit namespace classes can be utilized to emit CIL code to generate code explicitly.
The compiler often emits code and metadata in case of source code optimization, source code generation, and in a situation where you want to develop your own compiler. The most important point to remember about code emitting is that such an implementation requires a thorough understanding of CIL code.
The objective of this sample program is to generate a dynamic assembly that keeps a single module, a class type, and discloses a single method, and then save this assembly to disk as an executable file.
[cpp]
using System;
using System.Reflection;
using System.Reflection.Emit;
namespace ReflectionEmit
{
class Program
{
static void Main(string[] args)
{
// Create an assembly.
AssemblyName am = new AssemblyName();
am.Name = "test";
AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly(am, AssemblyBuilderAccess.Save);
// Define a public class 'AjaySample'.
TypeBuilder tb = mb.DefineType("AjaySample", TypeAttributes.Public);
//Create a public method
MethodBuilder metb = tb.DefineMethod("HelloWorld", MethodAttributes.Public |
MethodAttributes.Static, null, null);
ILGenerator ilg = metb.GetILGenerator();
ilg.EmitWriteLine("Hi! Welcome to this World");
ilg.Emit(OpCodes.Ret);
tb.CreateType();
Console.ReadLine();
}
}
}
After successful compilation of this sample, a test.exe file is generated in the Bin/Debug folder of the solution directory. Here, notice that we have emitted three sample instructions ildstr, call and ret in the method. You can examine the CIL code by opening the test.exe file via ildasm.exe utility as follows:
Summary
Learn Digital Forensics
Refection is a very fascinating feature of the .NET framework programming. The reflection services typically revolve around the System.Type and System.Reflection namespace. You've also come across the other interesting concept, late binding, which is the process of creating a type and invoking its members without prior knowledge of the specific names of those attributes and members. Hence, this mechanism is typically used to load a .net assembly into memory programmatically. Finally, this code teaches the MSIL code emitting using reflection.