Examining the Assembly using Reflection in .NET

The CLR code of a project is packaged into an assembly. We can inspect a range of metadata from an assembly that also the CLR uses to load and execute runnable code: classes, methods, interfaces, enumerations etc.

In order to inspect an Assembly in a .NET project we’ll first need to get a reference to the assembly in question. There are various ways to retrieve an assembly, including:

Assembly callingAssembly = Assembly.GetCallingAssembly();
Assembly entryAssembly = Assembly.GetEntryAssembly();
Assembly executingAssembly = Assembly.GetExecutingAssembly();

…where Assembly is located in the System.Reflection namespace.

Read more of this post

Advertisement

Create code at runtime with Reflection in .NET C#: Assembly

We’ve looked at how to inspect assemblies, types and similar elements in other posts on Reflection – see link below. With Reflection you cannot only inspect assemblies but create new ones on the fly.

Why would you create a new .NET assembly programmatically? A typical scenario is when you have an application which allows users to create their plugins that your application can use in some way. Normally programmers can create such plugins and compile them into libraries based on some instructions on your website. However, what to do if you want to allow non-programmers to create their plugins? Or if the rules for creating the plugin are so complex that you don’t even trust programmers with the task? Then you can have a GUI where people can make their choices and you create the plugin for them based on the options they have selected.

The entry point to creating assemblies in code is found in the System.Reflection.Emit namespace. It contains Builder classes to build the elements of an assembly: AssemblyBuilder, EnumBuilder, EventBuilder, MethodBuilder etc. Most builders are quite descriptive of their function where the string before ‘Builder’ shows what can be built with it:

  • AssemblyBuilder
  • ConstructorBuilder
  • EnumBuilder
  • EventBuilder
  • FieldBuilder
  • LocalBuilder: to build local variables for methods and constructors
  • MethodBuilder
  • ModuleBuilder
  • ParameterBuilder: to build method parameters
  • PropertyBuilder
  • TypeBuilder

The first step towards creating an assembly with types and methods is creating an assembly and a module:

string assemblySimpleName = "MyGreatPlugin";
string assemblyFileName = string.Concat(assemblySimpleName, ".dll");
AssemblyName assemblyName = new AssemblyName(assemblySimpleName);			
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);

ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MyPluginModule", assemblyFileName);

We first set up some basic information about the assembly simple name and the library name. We use the simple assembly name to construct an AssemblyName object. This is in turn used to create an AssemblyBuilder with the DefineDynamicAssembly method of the AppDomain class. The AssemblyBuilderAccess enumeration defines what the users can do with the assembly. Possible values:

  • RunAndSave: to save and execute an assembly which is probably what you want in a plugin scenario
  • Run: can be executed but not saved
  • RunAndCollect: can be executed and reclaimed from memory, read more here
  • Save: can be saved but not executed
  • ReflectionOnly: can be loaded into memory to inspect its element with Reflection but cannot be executed

Next we use the assembly builder to get a ModuleBuilder. The parameters into the DefineDynamicModule are the module name and the DLL file name where the module will be stored.

We’ll create a Type in the module in the next part.

View all posts on Reflection here.

Examining a .NET assembly through Reflection

We’ve gone through a couple of points about Reflection in .NET on this blog:

  • Inspecting assemblies
  • Inspecting types
  • Examining the members of a class

To recap we’ll see how to inspect the members of a .NET dll library built into the framework. The .NET 4 or 4.5 libraries are usually stored in the following location:

C:\Windows\Microsoft.NET\Framework64\v4.0.30319

Have a look into that folder and you’ll find the familiar libraries such as mscorlib, System.IO, System.Net etc. The following code will load the System.Net.Http assembly and print some basic information about those members which…

  • …are public
  • …are instance level, i.e. non-static
  • …are declared directly on the specific type

This last point means that we’ll ignore the inherited members of a Type. Without this flag we’d get all inherited members, such as ToString and GetType from Object:

string pathToAssembly = @"C:\Windows\Microsoft.NET\Framework64\v4.0.30319\System.Net.Http.dll";
BindingFlags bindingFlags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance;
Assembly assembly = Assembly.LoadFrom(pathToAssembly);

Console.WriteLine("Assembly full name: {0}", assembly.FullName);
Type[] typesInAssembly = assembly.GetTypes();

foreach (Type type in typesInAssembly)
{
	Console.WriteLine("Type name: {0}", type.Name);
	MemberInfo[] members = type.GetMembers(bindingFlags);
	foreach (MemberInfo mi in members)
	{
		Console.WriteLine("Member type: {0}, member name: {1}.", mi.MemberType, mi.Name);
	}
}

The assembly is quite large but here comes an excerpt:

Examining system.net.http through reflection

View all posts on Reflection here.

Reading assembly attributes at runtime using Reflection in .NET

A lot of metadata of an assembly is stored by way of attributes in the AssemblyInfo.cs file. E.g. if you create a simple Console application then this file will be readily available in the Properties folder. Assembly-related attributes are denoted by an “assembly:” prefix and can carry a lot of customisable information. Examples:

[assembly: AssemblyTitle("ReflectionCodeBits")]
[assembly: AssemblyDescription("This is a container for Reflection related code examples")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Great Company Ltd.")]
[assembly: AssemblyProduct("ReflectionCodeBits")]
[assembly: AssemblyCopyright("Copyright ©  2014")]
[assembly: AssemblyTrademark("GC")]
[assembly: AssemblyCulture("sv-SE")]
[assembly: ComVisible(false)]
[assembly: Guid("8376337d-c211-4507-bc0d-bcd39bc9fb4f")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

Most of these are self-explanatory but others deserve more attention:

  • AssemblyConfiguration: to specify which configuration is used for the assembly. You can specify this value like “DEBUG”, “RELEASE” or some custom configuration name, like “ALPHA”
  • AssemblyCulture: normally only used for satellite assemblies, otherwise an empty string denoting neutral culture – in fact you specify an assembly culture like I have done above you’ll get a compile error saying that executables cannot be satellite assemblies; culture should always be empty.

You can read the full documentation about assembly attributes here.

In order to extract the assembly attributes you’ll first need to get a reference to that assembly. You can then list all attributes of the assembly as follows:

Assembly executingAssembly = Assembly.GetExecutingAssembly();
IEnumerable<CustomAttributeData> assemblyAttributes = executingAssembly.CustomAttributes;
foreach (CustomAttributeData assemblyAttribute in assemblyAttributes)
{
	Type attributeType = assemblyAttribute.AttributeType;
	Console.WriteLine("Attribute type: {0}", attributeType);
	IList<CustomAttributeTypedArgument> arguments = assemblyAttribute.ConstructorArguments;
	Console.WriteLine("Attribute arguments: ");
	foreach (CustomAttributeTypedArgument arg in arguments)
	{
		Console.WriteLine(arg.Value);
	}
}

In my case I got the following output:

Assembly attributes output

You can also extract a specific attribute type as follows:

AssemblyDescriptionAttribute assemblyDescriptionAttribute =     executingAssembly.GetCustomAttribute<AssemblyDescriptionAttribute>();
string assemblyDescription = assemblyDescriptionAttribute.Description;

…which returns “This is a container for Reflection related code examples” as expected.

View all posts on Reflection here.

Examining the Assembly using Reflection in .NET

The CLR code of a project is packaged into an assembly. We can inspect a range of metadata from an assembly that also the CLR uses to load and execute runnable code: classes, methods, interfaces, enumerations etc.

In order to inspect an Assembly in a .NET project we’ll first need to get a reference to the assembly in question. There are various ways to retrieve an assembly, including:

Assembly callingAssembly = Assembly.GetCallingAssembly();
Assembly entryAssembly = Assembly.GetEntryAssembly();
Assembly executingAssembly = Assembly.GetExecutingAssembly();

…where Assembly is located in the System.Reflection namespace.

The static methods above represent the following:

  • GetCallingAssembly: to get the assembly one level up the call stack, i.e. which contains the method the current executing code
  • GetEntryAssembly: to get the assembly which contains the entry point to the application, e.g. the Main method in a Console app
  • GetExecutingAssembly: to get the assembly of the currently running code

There’s also a way to load a specific assembly using the GetAssembly(Type type) method. E.g. if you have a Customer class then you can load the assembly that contains the Customer class as follows:

Assembly specificAssembly = Assembly.GetAssembly(typeof(Customer));

Once you have the assembly you can read various metadata from it, examples:

Console.WriteLine("Full name: {0}", callingAssembly.FullName);
Console.WriteLine("Location: {0}", callingAssembly.Location);
Console.WriteLine("Loaded for reflection only? {0}", callingAssembly.ReflectionOnly);
Console.WriteLine("Loaded from GAC? {0}", callingAssembly.GlobalAssemblyCache);
Console.WriteLine(".NET Version: {0}", callingAssembly.ImageRuntimeVersion);

…which in my case outputs the following:

Basic assembly information

You may wonder what “loaded for reflection” means. You can get hold of an assembly in order to inspect its metadata without the possibility to execute any action on it. This is how we can load the assembly containing the “string” class in .NET:

Assembly reflectionOnlyAssembly = Assembly.ReflectionOnlyLoad("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");

You can execute an assembly by creating an instance of it using the CreateInstance method. However, if the assembly was only loaded for reflection then CreateInstance will throw an exception. If you know that you only want to read some metadata from an assembly but not execute it then you can use the ReflectionOnlyLoad method to save time loading it fully into the AppDomain.

View all posts on Reflection here.

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.

%d bloggers like this: