CLR internals, Mono.Cecil

Mono.Cecil: Hello, World

In previous post, I explained what is Cecil, now lets write some code.

This is a minimal program that show how to crate an assembly, add one type, and one method that print “Hello World”.

Before you start, you need to get the latest Mono.Cecil from nuget, create a new console application and add the necessary using statements.

static void Main(string[] args)
{
// Crate a new assenbly
var assembly = AssemblyDefinition.CreateAssembly(
new AssemblyNameDefinition("HelloWorld", new Version()),
"HelloWorld",
ModuleKind.Console);

// This is the main module you need to work with
var module = assembly.MainModule;

// Create a new type called "Program" and add it to main module
var programType = new TypeDefinition(
"HelloWorld",
"Program",
TypeAttributes.Class | TypeAttributes.Public,
module.Import(typeof(object)));

module.Types.Add(programType);

// create a new method called 'Main' method and add it to 'Program' type
var mainMethod = new MethodDefinition(
"Main",
MethodAttributes.Public | MethodAttributes.Static,
module.Import(typeof(void)));

programType.Methods.Add(mainMethod);

// Get ILProcessor for the method body
var ilProcessor = mainMethod.Body.GetILProcessor();

// Load the string "Hello World" to stack
ilProcessor.Emit(OpCodes.Ldstr, "Hello World");

// Call Console.WrtieLine(string)
var writline = module.Import(
typeof(Console).GetMethod("WriteLine", new[] { typeof(string) }));
ilProcessor.Emit(OpCodes.Call, writline);

// Call Console.ReadKey()
var readKey = module.Import(typeof(Console).GetMethod("Read"));
ilProcessor.Emit(OpCodes.Call, readKey);

// You must pop out the return value before you leave the method
ilProcessor.Emit(OpCodes.Pop);

// Return
ilProcessor.Emit(OpCodes.Ret);

// Because this is an executable assembly, you must define an entry point
assembly.EntryPoint = mainMethod;

// Save the assembly to disk
assembly.Write(@"c:\temp\HelloWorld.exe");
}

This code, generate a class equivalent to this class:

namespace HelloWorld
{
public class Program
{
public static void Main()
{
Console.WriteLine("Hello World");
Console.Read();
}
}
}

If you run the output exe, you will see in the console Hello World and the program will wait to input.

I think the comments on the code explain everything, well almost…

What is Import method?

Import is the Cecil way to get a reference (type, method etc.). As you know, void and object are not define in out assembly, so we need to import them as a type reference from mscorlib.dll. Import accepting parameter from Cecil types or .Net types. The opposite is Resolve, which is return a definition. We will see more on this in next posts.