IL perversions: throwing and catching strings
Inspired by Mohamed Mahmoud’s blog posting How to: Create Interfaces with Static Methods via IL? I wrote another sick example on IL (Intermediate Language) to show you how different is the world behind compilers. For tonight I have enough of exceptions, I want to throw some strings! Let’s do it!
Sample solution available! I made sample solution for this blog post available at GitHub repository gpeipman/ThrowStrings. GitHub page has also links to all necessary tools and support materials that help you to make solution work on your machine.
Throwing exception in IL
As a first thing take a look at the following code written in IL. In short this code defines class with two methods. Run() is entry point and it is run automatically when compiled assembly is executed from command line. ThrowSomething() is method that throws exception. This exception is caught and program terminates without any errors. I borrowed this code from Vijay Mukhi’s IL book, Chapter 10 “Exception Handling”.
.assembly StringMess {}
.class private auto ansi Program extends [mscorlib]System.Object
{
.method public hidebysig static void Run() il managed
{
.entrypoint
.locals (class [mscorlib]System.Exception V_0)
.try
{
call void Program::ThrowSomething()
ldstr "Bye"
call void [mscorlib]System.Console::WriteLine
(class System.String)
leave.s IL_001e
}
catch [mscorlib]System.Exception
{
stloc.0
ldstr "Exception was thrown!"
call void [mscorlib]System.Console::WriteLine
(class System.String)
leave.s IL_001e
}
IL_001e: ldstr "Finish"
call void [mscorlib]System.Console::WriteLine
(class System.String)
ret
}
.method public hidebysig static void ThrowSomething()
il managed
{
newobj instance void [mscorlib]System.Exception::.ctor()
throw
}
}
Everybody who is familiar with C# should be able to understand this code at least in some parts. Now let’s see what is the output of this method. As image on right shows it is nothing special. We threw exception, it was handled and program terminated normally. Just like we expected.
Let’s throw some strings now
Now let’s modify this code so it should do something we expect to end up with error. I add one more catch clause. This additional catch catches String. To test it I will do exactly what this catch expects – I will throw out the String. Here is the code.
.assembly StringMess {}
.class private auto ansi Program extends [mscorlib]System.Object
{
.method public hidebysig static void Run() il managed
{
.entrypoint
.locals (class [mscorlib]System.Exception V_0)
.try
{
call void Program::ThrowSomething()
ldstr "Bye"
call void [mscorlib]System.Console::WriteLine
(class System.String)
leave.s IL_001e
}
catch [mscorlib]System.Exception
{
stloc.0
ldstr "Exception was thrown!"
call void [mscorlib]System.Console::WriteLine
(class System.String)
leave.s IL_001e
}
//
// CATCH STRING
//
catch System.String
{
stloc.0
ldstr "String was thrown"
call void [mscorlib]System.Console::WriteLine
(class System.String)
leave.s IL_001e
}
IL_001e: ldstr "Finish"
call void [mscorlib]System.Console::WriteLine
(class System.String)
ret
}
.method public hidebysig static void ThrowSomething()
il managed
{
//newobj instance void [mscorlib]System.Exception::.ctor()
//
// THROW STRING
//
ldsfld string [mscorlib]System.String::Empty
throw
}
}
We can expect that this code doesn’t compile, but it does. As a next thing we may expect that running this code some runtime error occurs, but no, there is no runtime error. We can expect that exception handling lands in catch for Exception… we can expect many things but the result is here.
I bet at least half of readers expected something else but yes, we just threw string and we also caught it. But why C# and VB.NET give us errors when we try to compile code like this? Well, it is a compiler level limitation. It is not related to CLR as we just saw.
Playing with Visual Studio
Let’s play now with Visual Studio. I created console application, compiled previous example as DLL and referenced it from my console application. I want to know what happens when string is thrown and caught. Does it affect compiler somehow?
using System;
namespace ConsoleExamples
{
class Program
{
static void Main(string[] args)
{
StringMess.Program.Run();
Console.ReadLine();
Console.ReadLine();
}
}
}
Well, it does not. Let’s try to call ThrowSomething() method now. Maybe something interesting happens.
using System;
namespace ConsoleExamples
{
class Program
{
static void Main(string[] args)
{
StringMess.Program.ThrowSomething();
Console.ReadLine();
}
}
}
Okay, now something happens. Our application is not able to handle the situation and it throws RuntimeWrappedException.
RuntimeWrappedException is located under System.Runtime.CompilerServices namespace and as documentation sais it is here to maintain compatibility between languages. The common language runtime (CLR) wraps objects that do not derive from Exception in a RuntimeWrappedException object. This is why we got exception and not completely crashed application.
TIP! If you want to find out more about IL there is very good book I can suggest: Expert .NET 2.0 IL Assembler by Serge Lidin. I have read this book and also the idea of this posting is taken from this book. If you are interested in my other postings about IL and compiling results then please take a look at IL category.
Okay, it’s couple of minutes over midnight here and it is time to throw some real strings, I guess. Good night!