-15-

 

A GUI Application in  IL

 

So far, we have been writing small programs and rendering explanations for specific concepts. The IL documentation contains a very large program that demonstrates how to write a small application. Their application spawns five files, creates four dll's and has code that is spread over at least 30 pages.

 

We believe in what Newton once said, that he could see very far because he stood on the shoulders of tall people. We would like to emulate this thought. So, let us get down to hard work in our last chapter. We have decided to use the same application to explain the different concepts. We also expect you to read the original program yourselves.

 

We first created a batch file a.bat as shown below. The batch file does everything for us, including running the program. As far as possible, we have maintained the same variable and class names that the original program contained. The only change incorporated is that we have put everything into the following two files:

 

  countdown.il that becomes an executable.

  aaa.il that contains the rest of the code and becomes a dll.

 

a.bat

del *.dll

del *.exe

ilasm aaa.il /dll

ilasm countdown.il

countdown

 

countdown.il

.assembly CountDown {}

.assembly extern System.WinForms {}

.assembly extern System.Drawing {}

.file aaa.dll

.module extern aaa.dll

.class  zzz extends [System.WinForms]System.WinForms.Form

{

.field  class [.module aaa.dll]ctb counterBox

.field  class [.module aaa.dll]ssb button

.method public static  void Main()

{

.entrypoint

newobj  instance void zzz::.ctor()

call void [System.WinForms]System.WinForms.Application::Run(class [System.WinForms]System.WinForms.Form)

ret

}

.method  instance void .ctor()

{

ldarg.0

call instance void [System.WinForms]System.WinForms.Form::.ctor()

ldarg.0

ldarg.0

newobj  instance void [.module aaa.dll]ssb::.ctor(class [System.WinForms]System.WinForms.Form)

stfld class [.module aaa.dll]ssb zzz::button

ldarg.0

dup

newobj  instance void [.module aaa.dll]ctb::.ctor(class zzz)

stfld   class [.module aaa.dll]ctb zzz::counterBox

.locals init (value class [System.Drawing]System.Drawing.Size size)

ldloca  size

initobj value class [System.Drawing]System.Drawing.Size

ldloca  size

ldc.i4  425

ldc.i4  300

call    instance void [System.Drawing]System.Drawing.Size::.ctor(int32, int32)

ldarg.0

ldloc   size

callvirt instance void zzz::set_Size(value class [System.Drawing]System.Drawing.Size)

ldarg.0

call    value class [System.Drawing]System.Drawing.Color [System.Drawing]System.Drawing.Color::get_CadetBlue()

callvirt instance void zzz::set_BackColor(value class [System.Drawing]System.Drawing.Color)

ldarg.0

ldstr   "CountDown"

callvirt instance void zzz::set_Text(class System.String)

ret

}

}

 

aaa.il

.module aaa.dll

.assembly extern mscorlib {}

.assembly extern System.WinForms {}

.assembly extern System.Drawing {}

.file CountDown.exe

.module extern CountDown.exe

.class public ctb extends [System.WinForms]System.WinForms.TextBox

{

.field class [.module CountDown.exe]zzz parent

.field static family int32 counterDefault at Vijay

.method instance void .ctor(class [.module CountDown.exe]zzz parent)

{

ldarg.0

call instance void [System.WinForms]System.WinForms.TextBox::.ctor()

ldarg.0

ldarg  parent

stfld class [.module CountDown.exe]zzz ctb::parent

.locals (value class [System.Drawing]System.Drawing.Point point)

ldloca  point

ldc.i4  75 

ldc.i4  100

call instance void [System.Drawing]System.Drawing.Point::.ctor(int32, int32)

ldarg.0

ldloc   point

call instance void [System.WinForms]System.WinForms.TextBox::set_Location(value class [System.Drawing]System.Drawing.Point)

ldarg parent

call instance class [System.WinForms]System.WinForms.Control$ControlCollection [System.WinForms]System.WinForms.Form::get_Controls()

ldarg.0

callvirt instance void [System.WinForms]System.WinForms.Control$ControlCollection::Add(class [System.WinForms]System.WinForms.Control)

ldarg.0

ldsfld int32 ctb::counterDefault

call class System.String [mscorlib]System.Int32::ToString(int32)

callvirt instance void [System.WinForms]System.WinForms.TextBox::set_Text(class System.String)

ret

}

}

.data Vijay = int32(3)

.class public ssb extends [System.WinForms]System.WinForms.Button

{

.method instance void .ctor(class [System.WinForms]System.WinForms.Form parent)

{

ldarg.0 

call instance void [System.WinForms]System.WinForms.Button::.ctor()

.locals init (value class [System.Drawing]System.Drawing.Size size,value class [System.Drawing]System.Drawing.Point point)

ldloca  point

initobj value class [System.Drawing]System.Drawing.Point

ldloca  point

ldc.i4  100

ldc.i4  200

call instance void [System.Drawing]System.Drawing.Point::.ctor(int32, int32)

ldarg.0

ldloc   point

call    instance void [System.WinForms]System.WinForms.Button::set_Location(value class [System.Drawing]System.Drawing.Point)

ldloca  size

initobj value class [System.Drawing]System.Drawing.Size

ldloca  size

ldc.i4  200

ldc.i4  50

call instance void [System.Drawing]System.Drawing.Size::.ctor(int32, int32)

ldarg.0

ldloc   size

callvirt instance void [System.WinForms]System.WinForms.Button::set_Size(value class [System.Drawing]System.Drawing.Size)

ldarg.0

call    value class [System.Drawing]System.Drawing.Color [System.Drawing]System.Drawing.Color::get_Gold()

callvirt instance void [System.WinForms]System.WinForms.Button::set_BackColor(value class [System.Drawing]System.Drawing.Color)

ldarg.0

call    value class [System.Drawing]System.Drawing.Color [System.Drawing]System.Drawing.Color::get_Navy()

callvirt instance void [System.WinForms]System.WinForms.Button::set_ForeColor(value class [System.Drawing]System.Drawing.Color)

ldarg.0

ldstr   "Arial"

ldc.r4  20

newobj  instance void [System.Drawing]System.Drawing.Font::.ctor(class System.String, float32)

callvirt instance void [System.WinForms]System.WinForms.Button::set_Font(class [System.Drawing]System.Drawing.Font)

ldarg.0

ldstr   "Start"

callvirt instance void [System.WinForms]System.WinForms.Button::set_Text(class System.String)

ldarg   parent

call    instance class [System.WinForms]System.WinForms.Control$ControlCollection [System.WinForms]System.WinForms.Form::get_Controls()

ldarg.0

callvirt instance void [System.WinForms]System.WinForms.Control$ControlCollection::Add(class [System.WinForms]System.WinForms.Control)

ret

}

}

 

We have repeated some of the earlier explanations to refresh your memory. The explanation is also a summary of what we have learnt so far.

 

When we run the above program, a button and a text box are displayed on the screen. The button has a different color and the window has a title.

 

This large program enlightens us on designing a GUI application in IL. To write one ourselves, we start with the main program which is countdown.il.

 

The assembly is named as countdown for want of a better name. The code for the GUI classes resides in a file called System.WinForms.dll and belongs to the namespace System.Winforms. The type is  prefaced with the dll name which is optional in case of mscorlib.dll.

 

As we are referring to code in an external assembly, the extern parameter is given after the directive assembly. This is followed by the name of the assembly that contains the code. The extern directive is supplied for System.Drawing also. No error is generated if an extern directive is mentioned and the code within the file  is not executed

 

Most of our code is contained in classes and resides in a file aaa.il. The assembler converts the il file into a dll hence the file name is  aaa.dll. The two directives used are:

 

  The first specifies the name of the physical file using the file directive.

  The second is the assembly extern directive.

 

We will not repeat the code explanations that have been delved upon earlier.

 

We have created two fields:

 

.field  class [.module aaa.dll]ctb counterBox

.field  class [.module aaa.dll]ssb button

 

The first one is named counterBox and typed ctb. The class ctb exists in aaa.dll file, hence the field is prefaced with the name of the class alongwith the .module directive and the name of the dll. This information is mandatory when referencing a type in an external dll. In the same vein, the field named button that looks like class ssb, resides in file aaa.dll.

 

.class  zzz extends [System.WinForms]System.WinForms.Form

 

The class zzz is derived from the class System.Winforms.Form. To build a GUI applications, in Microsoft parlance, WinForms, the  class has to be derived from Form in namespace System.Winforms.

 

 

The code execution begins in Main as the entrypoint directive is placed in this function.Within this function, a new object zzz is created and placed on the stack. This is to facilitates the next function call  Run that requires a Forms object on the stack.

 

newobj  instance void zzz::.ctor()

call void [System.WinForms]System.WinForms.Application::Run(class [System.WinForms]System.WinForms.Form)

 

The object created by the newobj instruction is all that we need to place an empty window on our screen. newobj calls the constructor of class zzz  i.e. .ctor which populates the window with a variety of buttons, text boxes and widgets.

 

A look at the constructor of class zzz:

 

ldarg.0

call instance void [System.WinForms]System.WinForms.Form::.ctor()

 

At first, the  constructor of the base class Forms is called. Prior to the call, the this pointer is the only value that is placed on the stack as the constructor of the base class Forms, takes no parameters. The this pointer is stored in the invisible first parameter to every non static function and its value can be accessed using the instruction ldarg.0.

 

The next job is creating an object, an instance of class ssb i.e. a button. The instruction newobj comes into focus again and the constructor of the class ssb is called.

 

newobj  instance void [.module aaa.dll]ssb::.ctor(class [System.WinForms]System.WinForms.Form)

 

As explained earler, the this pointer is placed on the stack but it is done twice. The second this pointer is for the parameter to the constructor.  A reference to the newly created object on the stack is stored in the field button, so that it can be used later.

 

stfld class [.module aaa.dll]ssb zzz::button

To store the return value of newobj in a field, the first this pointer is used. This proves that to access a field, we first need a reference to the object that contains the field.

 

The return value of newobj that is placed on the stack can be stored in a local, if desired so. But, to store it in a field, the this pointer must be loaded on the stack  prior to calling newobj. newobj then removes the second this pointer from the stack.

 

In short, newobj requres the this pointer on the stack first and then calls the constructor. The constructor is not called directly but to do so the this pointer reference must be placed on the stack first. With newobj it is impractical to stack position the this pointer as the object has not been created at all.

 

The instruction stfld not only requires the name of the field, but also its data type, since the field can be in another assembly. Incidentally, it takes the same effort to use an entity from another assembly, as it takes to use the same entity located in our file. The only difference is in the use of square brackets [] and in the name of the module or assembly.

 

After the button, the next widget to be created is a text box of type ctb.

newobj  instance void [.module aaa.dll]ctb::.ctor(class zzz)

 

The constructor to this class needs a parameter as in the ssb class. Here, instead of repeating the ldarg.0 instruction twice, the dup instruction is used. This instruction simply duplicates the value present on the stack. ldarg.0 places the this pointer on the stack and dup creates one more copy of the this pointer.

 

You are free in choosing from two ldarg.0 statements or using the dup instruction. We will focus on the role of the constructors a little later after a brief synopsis of the program.

 

stfld   class [.module aaa.dll]ctb zzz::counterBox

 

The reference to the newly created object on the stack, akin to the button is stored in the field counterBox, so that it can be used later.

The directive .locals very often used for creating variables can be placed anywhere in the function, as styles evolve with time, but good programming style demands that we use it at the beginning. A local variable called size of type Size within  the namespaces System.Drawing is created.

 

.locals init (value class [System.Drawing]System.Drawing.Size size)

 

Since it is a value type, we see the modifier value in front of class.

The init keyword initializes the locals to zero. ldloca places the address of size on the stack and then initobj calls the constructor of the value class or structure.

 

ldloca  size

initobj value class [System.Drawing]System.Drawing.Size

 

initobj is optional, but it is a good idea to use it. We cannot use newobj on a value type.

 

Now to initialize this size object to the size of our opening windows:

To accomplish this, on the stack first is  placed the address of the size, followed by x and y co-ordinates of the window.

 

ldloca  size

ldc.i4  425

ldc.i4  300

 

Thereafter, the constructor of the Size class is called which initializes the size object.

 

call    instance void [System.Drawing]System.Drawing.Size::.ctor(int32, int32)

 

The Form class has a function or property called set_Size that initializes the window size on the screen, depending upon the size object passed.

 

In order to change the look and feel of any GUI application, the parameters are first placed on the stack and then the relevant functions are called . From now on, we will not comment upon the unavoidable requirement of the this pointer on the stack.

To change the foreground and background color of the window, a property called get_CadetBlue from the Color Class, a value class, is used.

 

call    value class [System.Drawing]System.Drawing.Color [System.Drawing]System.Drawing.Color::get_CadetBlue()

 

The return value is placed on the stack and can either be an enum or a constant. Remember properties are functions within a class.

 

The virtual function set_BackColor changes the background color.

 

callvirt instance void zzz::set_BackColor(value class [System.Drawing]System.Drawing.Color)

 

We could have gone on endlessly on changing the appearance of our window, however, we will stop with just one more function, to change the title of our windows. To achieve this, the title as a string is loaded on the stack using ldstr and then function set_Text is called to change the title.

 

ldstr   "CountDown"

callvirt instance void zzz::set_Text(class System.String)

 

Now, to understanding the file aaa.il.

 

aaa.il eventually gets converted into a dll, hence the assembly directive is evaded. Instead, a module directive stating the name of the module is inserted.

 

.module aaa.dll

.assembly extern mscorlib {}

.assembly extern System.WinForms {}

.assembly extern System.Drawing {}

.file CountDown.exe

.module extern CountDown.exe

 

This file references some fields stored in the executable file countdown.exe, and thus, we need both the file and assembly extern directive.

 

The first function to be called in the dll is the constructor of class ssb, an instance of the Button class. This is because countdown.il executes newobj on ssb i.e the Button class. The Button class contains all the code needed to represent a Button object on the screen. As usual, the first call is to the constructor of the base class Button.

 

call instance void [System.WinForms]System.WinForms.Button::.ctor()

 

Two locals are created thereafter:

 

  One to store a width and height dimension, called size,

  Another to represent a point on the screen, in x and y coordinates, called point.

 

.locals init (value class [System.Drawing]System.Drawing.Size size,value class [System.Drawing]System.Drawing.Point point)

 

The point member is initialized in the same manner as the size member. point is then loaded on the stack and set_Location from the Button class is called.

 

ldloc   point

call    instance void [System.WinForms]System.WinForms.Button::set_Location(value class [System.Drawing]System.Drawing.Point)

 

Remember, the this pointer in this case is an ssb, a Button reference type.

 

Every Winforms widget has a set_Size function that lets us set the size of the widget. The color can be set as shown before. To create a font object, besides the this pointer, we need only two parameters to the constructor:

 

  The first is the name of the font

  The second is a point size for the font.

 

ldarg.0

ldstr   "Arial"

ldc.r4  20

newobj  instance void [System.Drawing]System.Drawing.Font::.ctor(class System.String, float32)

callvirt instance void [System.WinForms]System.WinForms.Button::set_Font(class [System.Drawing]System.Drawing.Font)

 

This newly created font object is placed on the stack and the function set_Font is called.

 

ldstr   "Start"

callvirt instance void [System.WinForms]System.WinForms.Button::set_Text(class System.String)

 

To place the label "Start" on the button, the string is loaded on the stack using ldstr and thereafter the function set_Text is called. It is as simple as that.

 

The next instruction in the sequence is loading the parameter parent on the stack.

 

ldarg   parent

call    instance class [System.WinForms]System.WinForms.Control$ControlCollection [System.WinForms]System.WinForms.Form::get_Controls()

ldarg.0

callvirt instance void [System.WinForms]System.WinForms.Control$ControlCollection::Add(class [System.WinForms]System.WinForms.Control)

 

If you remember, the constructor was called with two identical parameters on the stack. Thus ldarg.0 and ldarg parent are the same. The function get_Controls places a Control$ControlCollection object on the stack. The this pointer is also loaded and the virtual function Add is called. Add, adds the newly created control to the list of controls that are finally to be displayed by Winforms. The function Add belongs to the class Control$ControlCollection and hence a reference to this class is required. It is easier in a language like C#, that shields us from passing the this pointer.

 

The next widget to be  displayed on the window is the text box.. The text box class is called ctb and derives from TextBox. As usual, the constructor is called. We will repeat this aspect of the code, i.e. calling of the constructor, for the last time. If you do not call the base class constructor, you will be in serious trouble.

 

To set the location, set_Location is used and then the control is added to the list of controls using the Add function. How does Winforms display the number 3 in the text box ?

 

For this purpose, a field called counterDefault  is created which is static and an int32. Tagged alongwith it is modifier at, and then a name Vijay.

 

.field static family int32 counterDefault at Vijay

 

This denotes that the variable counterDefault will receive its initial value from Vijay.

 

There is a directive called .data that creates a word "Vijay" and initialises it to the number 3 using int32.

 

.data Vijay = int32(3)

 

This directive called data places the value 3 in the .data section in the PE Executable file. The PE file is divided into smaller parts called sections. All the code goes into a section called .text and all the data goes into a section called .data. We thus do not have to initialize the variable in a constructor.

 

This number is placed on the stack and a static function To_String is called, that converts this int32 into a string. Then our good old function set_Text displays it in a text box.

 

Lets us now proceed to the next example.

 

 

countdown.il

.assembly CountDown {}

.assembly extern System.WinForms {}

.file aaa.dll

.module extern aaa.dll

.class  zzz extends [System.WinForms]System.WinForms.Form

{

.field  class [.module aaa.dll]ssb button

.method public static  void Main()

{

.entrypoint

newobj  instance void zzz::.ctor()

call void [System.WinForms]System.WinForms.Application::Run(class [System.WinForms]System.WinForms.Form)

ret

}

.method  instance void .ctor()

{

ldarg.0

call instance void [System.WinForms]System.WinForms.Form::.ctor()

ldarg.0

ldarg.0

newobj  instance void [.module aaa.dll]ssb::.ctor(class [System.WinForms]System.WinForms.Form)

stfld class [.module aaa.dll]ssb zzz::button

ret

}

}

 

aaa.il

.module aaa.dll

.assembly extern mscorlib {}

.assembly extern System.WinForms {}

.assembly extern System.Drawing {}

.file CountDown.exe

.module extern CountDown.exe

.class public ssb extends [System.WinForms]System.WinForms.Button

{

.field  class [mscorlib]System.EventHandler onClickEventHandler

.method instance void .ctor(class [System.WinForms]System.WinForms.Form parent)

{

ldarg.0 

call instance void [System.WinForms]System.WinForms.Button::.ctor()

ldarg.0

ldstr   "Start"

callvirt instance void [System.WinForms]System.WinForms.Button::set_Text(class System.String)

ldarg parent

call instance class [System.WinForms]System.WinForms.Control$ControlCollection [System.WinForms]System.WinForms.Form::get_Controls()

ldarg.0

callvirt instance void [System.WinForms]System.WinForms.Control$ControlCollection::Add(class [System.WinForms]System.WinForms.Control)

ldarg.0

dup

dup

ldvirtftn instance void ssb::OnClick(class System.Object, class [mscorlib]System.EventArgs)

newobj  instance void [mscorlib]System.EventHandler::.ctor(class System.Object, int32)

stfld   class [mscorlib]System.EventHandler ssb::onClickEventHandler

ldarg.0

dup

ldfld   class [mscorlib]System.EventHandler ssb::onClickEventHandler

call    instance void [System.WinForms]System.WinForms.Button::add_Click(class [mscorlib]System.EventHandler)

ret

}

.method virtual newslot public instance void OnClick(class System.Object, class [mscorlib]System.EventArgs)

{

ldc.i4.1

call int8 User32::MessageBeep(unsigned int32)

pop

ret

}

}

.class abstract sealed public auto autochar User32 extends [mscorlib]System.Object

{

.method public static pinvokeimpl("user32.dll" cdecl) int8 MessageBeep(unsigned int32) native unmanaged

{

}

}

In this example we simply display a button sans all the bells and whistles. When we click on this button, function OnClick merely gets called. In this function, we simply call another function MessageBeep  that rings a bell or simply produces a beep sound. This function is present in a dll called user32.dll. In the file countdown.il, no new code has been added. Modifications have only been made in the file aaa.il.

 

After the control is registered with WinForms, the this pointer is placed thrice on the stack. Also, the address of the virtual function OnClick is loaded on the stack.

 

 

ldarg.0

dup

dup

ldvirtftn instance void ssb::OnClick(class System.Object, class [mscorlib]System.EventArgs)

 

This function is called each time the button is clicked on. The dup instruction is placed on the stack to account for the parameters to the function OnClick.

 

Simultaneously, a new object is created that is an instance of the class EventHandler.

 

newobj  instance void [mscorlib]System.EventHandler::.ctor(class System.Object, int32)

stfld   class [mscorlib]System.EventHandler ssb::onClickEventHandler

 

This newly created object is stored in the field onClickEventHandler. Then function add_Click from the button class registers this function with Winforms.

 

ldfld   class [mscorlib]System.EventHandler ssb::onClickEventHandler

call    instance void [System.WinForms]System.WinForms.Button::add_Click(class [mscorlib]System.EventHandler)

 

Now, the function OnClick gets called with a click on the button and a static function MessageBeep is executed. The code for this function is not available as the attributes on the function are native. This implies that  the developers  have supplied the code and the function will be executed in the unmanaged state. To call a function from a dll, pinvokeimpl is used, stating the name of the dll and the calling convention.

 

The program countdown.il remains the same for the next program. The modified file aaa.il is given below.

 

aaa.il

.module aaa.dll

.assembly extern mscorlib {}

.assembly extern System.Timers {}

.assembly extern System.WinForms {}

.assembly extern System.Drawing {}

.file CountDown.exe

.module extern CountDown.exe

.class abstract sealed public auto autochar User32 extends [mscorlib]System.Object

{

.method public static pinvokeimpl("user32.dll" cdecl) int8 MessageBeep(unsigned int32) native unmanaged {}

}

.class public ssb extends [System.WinForms]System.WinForms.Button

{

.field  class [mscorlib]System.EventHandler onClickEventHandler

.method instance void .ctor(class [System.WinForms]System.WinForms.Form parent)

{

ldarg.0 

call instance void [System.WinForms]System.WinForms.Button::.ctor()

ldarg.0

ldstr   "Start"

callvirt instance void [System.WinForms]System.WinForms.Button::set_Text(class System.String)

ldarg parent

call instance class [System.WinForms]System.WinForms.Control$ControlCollection [System.WinForms]System.WinForms.Form::get_Controls()

ldarg.0

callvirt instance void [System.WinForms]System.WinForms.Control$ControlCollection::Add(class [System.WinForms]System.WinForms.Control)

ldarg.0

dup

dup

ldvirtftn instance void ssb::OnClick(class System.Object, class [mscorlib]System.EventArgs)

newobj  instance void [mscorlib]System.EventHandler::.ctor(class System.Object, int32)

stfld   class [mscorlib]System.EventHandler ssb::onClickEventHandler

ldarg.0

dup

ldfld   class [mscorlib]System.EventHandler ssb::onClickEventHandler

call    instance void [System.WinForms]System.WinForms.Button::add_Click(class [mscorlib]System.EventHandler)

ret

}

.method virtual newslot public instance void OnClick(class System.Object, class [mscorlib]System.EventArgs)

{

.locals (class [System.Timers]System.Timers.Timer V_0)

newobj     instance void [System.Timers]System.Timers.Timer::.ctor()

stloc.0

ldloc.0

ldnull

ldftn void ssb::OnTimedEvent(class System.Object,class [mscorlib]System.EventArgs)

newobj instance void [mscorlib]System.EventHandler::.ctor(class System.Object,int32)

call instance void [System.Timers]System.Timers.Timer::add_Tick(class [mscorlib]System.EventHandler)

ldloc.0

ldc.r8     300.

call       instance void [System.Timers]System.Timers.Timer::set_Interval(float64)

ldloc.0

ldc.i4.1

callvirt   instance void [System.Timers]System.Timers.Timer::set_Enabled(bool)

ret

}

.method public hidebysig static void  OnTimedEvent(class System.Object source,class [mscorlib]System.EventArgs e) il managed

{

ldc.i4.1

call int8 User32::MessageBeep(unsigned int32)

pop

ret

}

}

 

In this program, when the button is clicked on, for 3 seconds nothing happens. Thereafter, beeps are heard. Then on, after every 3 seconds, a beep sound is heard. This is a program that does nothing for three seconds and then activates some code.

 

These programs are timer-based.

 

 

The program aaa.il has only one change incorporated in it within the OnClick function. A local is declared that looks like class Timer and an object like class Timer is created using newobj.

 

.locals (class [System.Timers]System.Timers.Timer V_0)

newobj     instance void [System.Timers]System.Timers.Timer::.ctor()

 

The value returned is stored in the local V_0. Thereafter, the Timer object is again loaded on the stack, followed by a NULL reference and the address of a static function OnTimedEvent.

 

ldloc.0

ldnull

ldftn void ssb::OnTimedEvent(class System.Object,class [mscorlib]System.EventArgs)

 

This function is repeatedly called after a certain time period has elapsed.

 

Concurrently, an object that looks like an EventHandler is created.

 

newobj instance void [mscorlib]System.EventHandler::.ctor(class System.Object,int32)

 

This constructor, in addition to the this pointer, needs two more parameters, the second being the address of our function.

 

call instance void [System.Timers]System.Timers.Timer::add_Tick(class [mscorlib]System.EventHandler)

ldloc.0

 

The add_Tick function then incorporates these changes and stores the handle in the local variable.

 

So, the timeout period i.e. the time duration after which the function is to be called, is placed on the stack. This number is a float, and hence, 8 bytes are allocated on the stack for it. The set_Interval function sets the timeout period and the set_Enabled function sets the timer on, which runs periodically. 

 

ldc.r8     300.

call       instance void [System.Timers]System.Timers.Timer::set_Interval(float64)

 

Add the following lines of code to the end of the function OnClick, just before the ret instruction.

 

ldloc.0

ldc.i4.0

callvirt instance void [System.Timers]System.Timers.Timer::set_AutoReset(bool)

 

This code calls the function set_AutoReset with the number 1, so that, the timeout function gets called over and over again.

 

countdown.il

.assembly CountDown {}

.assembly extern System.WinForms {}

.assembly extern System.Drawing {}

.file aaa.dll

.module extern aaa.dll

.class  zzz extends [System.WinForms]System.WinForms.Form

{

.field  class [.module aaa.dll]ctb counterBox

.method public static  void Main()

{

.entrypoint

newobj  instance void zzz::.ctor()

call void [System.WinForms]System.WinForms.Application::Run(class [System.WinForms]System.WinForms.Form)

ret

}

.method  instance void .ctor()

{

ldarg.0

call instance void [System.WinForms]System.WinForms.Form::.ctor()

ldarg.0

dup

newobj  instance void [.module aaa.dll]ctb::.ctor(class zzz)

stfld   class [.module aaa.dll]ctb zzz::counterBox

ldarg.0

ldarg.0

ldfld class [.module aaa.dll]ctb zzz::counterBox

newobj  instance void [.module aaa.dll]ssb::.ctor(class [System.WinForms]System.WinForms.Form, class [.module aaa.dll]ctb)

pop

ret

}

}

 

aaa.il

.module aaa.dll

.assembly extern System.Timers {}

.assembly extern mscorlib {}

.assembly extern System.WinForms {}

.assembly extern System.Drawing {}

.file CountDown.exe

.module extern CountDown.exe

.class public ctb extends [System.WinForms]System.WinForms.TextBox

{

.field int32 count

.method instance void .ctor(class [.module CountDown.exe]zzz parent)

{

ldarg.0

call instance void [System.WinForms]System.WinForms.TextBox::.ctor()

ldarg.0

ldc.i4 3

stfld int32 ctb::count

.locals (value class [System.Drawing]System.Drawing.Point point)

ldloca  point

ldc.i4  75 

ldc.i4  100

call instance void [System.Drawing]System.Drawing.Point::.ctor(int32, int32)

ldarg.0

ldloc   point

call instance void [System.WinForms]System.WinForms.TextBox::set_Location(value class [System.Drawing]System.Drawing.Point)

ldarg.0

ldarg.0

ldfld int32 ctb::count

call class System.String [mscorlib]System.Int32::ToString(int32)

callvirt instance void [System.WinForms]System.WinForms.TextBox::set_Text(class System.String)

ldarg parent

call instance class [System.WinForms]System.WinForms.Control$ControlCollection [System.WinForms]System.WinForms.Form::get_Controls()

ldarg.0

callvirt instance void [System.WinForms]System.WinForms.Control$ControlCollection::Add(class [System.WinForms]System.WinForms.Control)

ret

}

.method virtual newslot instance void SetCount(int32 count1)

{

ldarg.0

ldarg   count1

call class System.String [mscorlib]System.Int32::ToString(int32)

callvirt instance void [System.WinForms]System.WinForms.TextBox::set_Text(class System.String)

ret

}

.method virtual newslot instance int32 GetCount()

{

ldarg.0

callvirt instance class System.String ctb::get_Text()

callvirt instance int32 [mscorlib]System.String::ToInt32()

ret

}

}

.class public ssb extends [System.WinForms]System.WinForms.Button

{

.field  class [mscorlib]System.EventHandler onClickEventHandler

.field class ctb par1

.method instance void .ctor(class [System.WinForms]System.WinForms.Form parent,class ctb aa)

{

ldarg.0 

call instance void [System.WinForms]System.WinForms.Button::.ctor()

ldarg.0

ldarg.2

stfld class ctb ssb::par1

ldarg.0

ldstr   "Start"

callvirt instance void [System.WinForms]System.WinForms.Button::set_Text(class System.String)

ldarg parent

call instance class [System.WinForms]System.WinForms.Control$ControlCollection [System.WinForms]System.WinForms.Form::get_Controls()

ldarg.0

callvirt instance void [System.WinForms]System.WinForms.Control$ControlCollection::Add(class [System.WinForms]System.WinForms.Control)

ldarg.0

dup

dup

ldvirtftn instance void ssb::OnClick(class System.Object, class [mscorlib]System.EventArgs)

newobj  instance void [mscorlib]System.EventHandler::.ctor(class System.Object, int32)

stfld   class [mscorlib]System.EventHandler ssb::onClickEventHandler

ldarg.0

dup

ldfld   class [mscorlib]System.EventHandler ssb::onClickEventHandler

call    instance void [System.WinForms]System.WinForms.Button::add_Click(class [mscorlib]System.EventHandler)

ret

}

.method virtual newslot public instance void OnClick(class System.Object, class [mscorlib]System.EventArgs)

{

.locals ( int32 i)

ldarg.0

ldfld class ctb ssb::par1

callvirt instance int32 ctb::GetCount()

ldc.i4.1

sub

stloc.0

ldc.i4.0

ldloc.0

bgt a1

ldarg.0

ldfld class ctb ssb::par1

ldloc.0

callvirt instance void ctb::SetCount(int32)

a1:

ret

}

}

 

The above program simply displays the number 3 in the edit box. With every click on the button, the number decreases by 1. When the value becomes 0, the execution of the program stops.

 

Let us now understand as to what goes behind writing such a program.

 

 

In countdown.il, a field counterBox is created that stores the reference of the text box. Since the reference value is saved in a field, any method can access the text field if it possesses this value. Consider that any call to a function in the text box class ctb, or access to the value stored in the text box, needs the reference of the text box on the stack.

 

At first, a text box object is created and the value is stored  in counterBox.  Then this  reference to the text box is passed on to the constructor of the button class, as the second parameter. The button can now call any methods from the text box class by simply placing this reference on the stack. The button constructor stores the address of this text box in a field for later use. A point to note here is that all the contents of a method die at the end of the method whereas, fields are perpetual.

 

The constructor of the text box is well explained before. The first change incorporated is in the constructor of the class button, i.e. ssb. Here, the this pointer is placed on the stack, and then, using ldarg.2 the reference of the button is also placed on the stack. Thereafter, the reference of the text box is stored in the field par1.

 

ldarg.0

ldarg.2

stfld class ctb ssb::par1

 

The rest of the code ensures that the function OnClick gets called each time the button is clicked.

 

In function OnClick, a local int32 is created to store the current value of the text box. After that, the this pointer is placed on the stack, and the value of the field par1 is retrieved. Using the reference to the text box on the stack, function GetCount from class ctb is called.

.locals ( int32 i)

ldarg.0

ldfld class ctb ssb::par1

callvirt instance int32 ctb::GetCount()

 

This function places its this pointer and par1 on the stack, and then calls the virtual function get_Text from the textbox class.

 

.method virtual newslot instance int32 GetCount()

{

ldarg.0

callvirt instance class System.String ctb::get_Text()

callvirt instance int32 [mscorlib]System.String::ToInt32()

ret

}

 

This function places a string, representing the text within the text box, on the stack. This string is converted into a number by calling the function ToInt32, which resides in the String class. GetCount returns this number on the stack as the value of the text box.

 

After placing the number 1 on the stack, sub is called.

 

ldc.i4.1

sub

stloc.0

 

This instruction now subtracts 1 from the value present earlier on the stack. The number happens to be the value stored in the text box. This new value is stored in the local i to make our programming easier.

 

 

Since IL has no equivalent of the if statement, the bgt instruction is used to compare two values. 0 is placed on the stack, followed by the value of the variable i.

 

ldc.i4.0

ldloc.0

bgt a1

 

If the value of i is zero, the bgt instruction jumps to label a1, which is at the end of the function. If i has a value of 2, then no jump takes place as the second value happens to be larger than the first.

 

The this pointer or reference of the text box is again pushed on the stack, followed by the new value of i and then, the function SetCount is called. This function simply changes the value displayed in the text box.

 

ldarg.0

ldfld class ctb ssb::par1

ldloc.0

callvirt instance void ctb::SetCount(int32)

 

This function loads the parameter passed, i.e. count1, on the stack, and uses ToString from the int32 class to convert it into a string. The string is placed on the stack. Finally set_Text is called to change the value displayed. This function is the reverse of the function GetCount.

 

.method virtual newslot instance void SetCount(int32 count1)

{

ldarg.0

ldarg   count1

call class System.String [mscorlib]System.Int32::ToString(int32)

callvirt instance void [System.WinForms]System.WinForms.TextBox::set_Text(class System.String)

ret

}

 

The last part of the code only gets called if the value of the local i is positive.

There is no change in program countdown.il  and aaa.il file resembles as shown below.

 

aaa.il

.module aaa.dll

.assembly extern System.Timers {}

.assembly extern mscorlib {}

.assembly extern System.WinForms {}

.assembly extern System.Drawing {}

.file CountDown.exe

.module extern CountDown.exe

.class public ctb extends [System.WinForms]System.WinForms.TextBox

{

.field int32 count

.method instance void .ctor(class [.module CountDown.exe]zzz parent)

{

ldarg.0

call instance void [System.WinForms]System.WinForms.TextBox::.ctor()

ldarg.0

ldc.i4 3

stfld int32 ctb::count

.locals (value class [System.Drawing]System.Drawing.Point point)

ldloca  point

ldc.i4  75 

ldc.i4  100

call instance void [System.Drawing]System.Drawing.Point::.ctor(int32, int32)

ldarg.0

ldloc   point

call instance void [System.WinForms]System.WinForms.TextBox::set_Location(value class [System.Drawing]System.Drawing.Point)

ldarg.0

ldarg.0

ldfld int32 ctb::count

call class System.String [mscorlib]System.Int32::ToString(int32)

callvirt instance void [System.WinForms]System.WinForms.TextBox::set_Text(class System.String)

ldarg parent

call instance class [System.WinForms]System.WinForms.Control$ControlCollection [System.WinForms]System.WinForms.Form::get_Controls()

ldarg.0

callvirt instance void [System.WinForms]System.WinForms.Control$ControlCollection::Add(class [System.WinForms]System.WinForms.Control)

ret

}

.method virtual newslot instance void SetCount(int32 count1)

{

ldarg.0

ldarg   count1

call class System.String [mscorlib]System.Int32::ToString(int32)

callvirt instance void [System.WinForms]System.WinForms.TextBox::set_Text(class System.String)

ret

}

.method virtual newslot instance int32 GetCount()

{

ldarg.0

callvirt instance class System.String ctb::get_Text()

callvirt instance int32 [mscorlib]System.String::ToInt32()

ret

}

}

.class public ssb extends [System.WinForms]System.WinForms.Button

{

.field  class [mscorlib]System.EventHandler onClickEventHandler

.field class ctb par1

.field class [System.Timers]System.Timers.Timer timer

.method instance void .ctor(class [System.WinForms]System.WinForms.Form parent,class ctb aa)

{

ldarg.0 

call instance void [System.WinForms]System.WinForms.Button::.ctor()

ldarg.0

ldarg.2

stfld class ctb ssb::par1

ldarg.0

ldstr   "Start"

callvirt instance void [System.WinForms]System.WinForms.Button::set_Text(class System.String)

ldarg parent

call instance class [System.WinForms]System.WinForms.Control$ControlCollection [System.WinForms]System.WinForms.Form::get_Controls()

ldarg.0

callvirt instance void [System.WinForms]System.WinForms.Control$ControlCollection::Add(class [System.WinForms]System.WinForms.Control)

ldarg.0

dup

dup

ldvirtftn instance void ssb::OnClick(class System.Object, class [mscorlib]System.EventArgs)

newobj  instance void [mscorlib]System.EventHandler::.ctor(class System.Object, int32)

stfld   class [mscorlib]System.EventHandler ssb::onClickEventHandler

ldarg.0

dup

ldfld   class [mscorlib]System.EventHandler ssb::onClickEventHandler

call    instance void [System.WinForms]System.WinForms.Button::add_Click(class [mscorlib]System.EventHandler)

ret

}

.method virtual newslot public instance void OnClick(class System.Object, class [mscorlib]System.EventArgs)

{

.locals (class [System.Timers]System.Timers.Timer V_0)

newobj     instance void [System.Timers]System.Timers.Timer::.ctor()

stloc.0

ldloc.0

ldarg.0

stfld class [System.Timers]System.Timers.Timer ssb::timer

ldloc.0

ldarg.1

ldftn instance void ssb::OnTimedEvent(class System.Object,class [mscorlib]System.EventArgs)

newobj     instance void [mscorlib]System.EventHandler::.ctor(class System.Object,int32)

call       instance void [System.Timers]System.Timers.Timer::add_Tick(class [mscorlib]System.EventHandler)

ldloc.1

ldc.r8     500.

call       instance void [System.Timers]System.Timers.Timer::set_Interval(float64)

ldloc.1

ldc.i4.1

callvirt   instance void [System.Timers]System.Timers.Timer::set_Enabled(bool)

ret

}

.method public instance void OnTimedEvent(class System.Object source,class [mscorlib]System.EventArgs e) il managed

{

.locals ( int32 i)

ldarg.0

ldfld class ctb ssb::par1

callvirt instance int32 ctb::GetCount()

ldc.i4.1

sub

stloc.0

ldc.i4.0

ldloc.0

bgt a1

ldarg.0

ldfld class ctb ssb::par1

ldloc.0

callvirt instance void ctb::SetCount(int32)

br a2

a1:

ldarg.0

ldfld class [System.Timers]System.Timers.Timer ssb::timer

call    instance void [System.Timers]System.Timers.Timer::Stop()

a2:

ldc.i4.1

call int8 User32::MessageBeep(unsigned int32)

pop

ret

}

}

.class abstract sealed public auto autochar User32 extends [mscorlib]System.Object

{

.method public static pinvokeimpl("user32.dll" cdecl) int8 MessageBeep(unsigned int32) native unmanaged {}

}

 

In this program, the numbers change automatically with every click on the button. The program stops when the value becomes 0. The beep sound also stops. The class ctb and the constructor of class ssb remains the same. It calls the function OnClick at the press of a mouse.

 

The function OnClick does things differently. Using stfld, the timer object is first stored in a field called timer.

 

stfld class [System.Timers]System.Timers.Timer ssb::timer

ldloc.0

 

This is because, a function from the timer class is to be called. The same value is saved in a local V_0. This is a poor programming style, but nobody's looking. The timer object calls the function OnTimedEvent periodically.

 

Earlier the function was static but now it is an instance function and it is given the this pointer instead of a NULL. The code for the timer tick in the earlier program has been assigned to the button click. The only change is that the text box value on attaining ZERO will stop the timer. This routine is employed with the function Stop from the timer class. It is given the timer reference on the stack

 

ldfld class [System.Timers]System.Timers.Timer ssb::timer

call    instance void [System.Timers]System.Timers.Timer::Stop()

 

 

Let us put together all that we have learnt so far and write the largest program in our book. This program should be followed up by reading the same program in the IL documentation. It is relatively larger and spread over more files. Let us start from the very beginning.

 

countdown.il

.assembly CountDown {}

.assembly extern System.WinForms {}

.assembly extern System.Drawing {}

.file aaa.dll

.module extern aaa.dll

.class  zzz extends [System.WinForms]System.WinForms.Form

{

.field  class [.module aaa.dll]ctb counterBox

.field  class [.module aaa.dll]ssb button

.field  class [.module aaa.dll]Counter counter

.method public static  void Main()

{

.entrypoint

newobj  instance void zzz::.ctor()

call void [System.WinForms]System.WinForms.Application::Run(class [System.WinForms]System.WinForms.Form)

ret

}

.method  instance void .ctor()

{

.locals  (class [.module aaa.dll]Count count)

ldarg.0

call instance void [System.WinForms]System.WinForms.Form::.ctor()

ldarg.0

ldarg.0

newobj  instance void [.module aaa.dll]ssb::.ctor(class [System.WinForms]System.WinForms.Form)

stfld class [.module aaa.dll]ssb zzz::button

ldarg.0

dup

newobj  instance void [.module aaa.dll]ctb::.ctor(class zzz)

stfld   class [.module aaa.dll]ctb zzz::counterBox

ldarg.0

ldfld class [.module aaa.dll]ctb zzz::counterBox

newobj instance void [.module aaa.dll]Count::.ctor(class [.module aaa.dll]ICountDisplay)

stloc  count

ldarg.0

dup

ldfld  class [.module aaa.dll]ssb zzz::button

ldloc  count

newobj  instance void [.module aaa.dll]BeepingCounter::.ctor(class [.module aaa.dll]IStartStopEventSource, class [.module aaa.dll]Count)

stfld  class [.module aaa.dll]Counter zzz::counter

ldarg.0

ldfld class [.module aaa.dll]ssb zzz::button

ldarg.0

ldfld class [.module aaa.dll]Counter zzz::counter

call instance void [.module aaa.dll]ssb::AddToTimeUp(class [.module aaa.dll]Counter)

ret

}

}

 

aaa.il

.module aaa.dll

.assembly extern System.Timers {}

.assembly extern mscorlib {}

.assembly extern System.WinForms {}

.assembly extern System.Drawing {}

.file CountDown.exe

.module extern CountDown.exe

.class public ctb extends [System.WinForms]System.WinForms.TextBox implements ICountDisplay

{

.field class [.module CountDown.exe]zzz parent

.method instance void .ctor(class [.module CountDown.exe]zzz parent)

{

ldarg.0

call instance void [System.WinForms]System.WinForms.TextBox::.ctor()

ldarg.0

ldarg  parent

stfld class [.module CountDown.exe]zzz ctb::parent

.locals (value class [System.Drawing]System.Drawing.Point point)

ldloca  point

ldc.i4  75 

ldc.i4  100

call instance void [System.Drawing]System.Drawing.Point::.ctor(int32, int32)

ldarg.0

ldloc   point

call instance void [System.WinForms]System.WinForms.TextBox::set_Location(value class [System.Drawing]System.Drawing.Point)

ldarg parent

call instance class [System.WinForms]System.WinForms.Control$ControlCollection [System.WinForms]System.WinForms.Form::get_Controls()

ldarg.0

callvirt instance void [System.WinForms]System.WinForms.Control$ControlCollection::Add(class [System.WinForms]System.WinForms.Control)

ret

}

.method virtual newslot instance void SetCount(int32 count)

{

ldarg.0

ldarg   count

call class System.String [mscorlib]System.Int32::ToString(int32)

callvirt instance void [System.WinForms]System.WinForms.TextBox::set_Text(class System.String)

ret

}

.method virtual newslot instance int32 GetCount()

{

ldarg.0

callvirt instance class System.String ctb::get_Text()

callvirt instance int32 [mscorlib]System.String::ToInt32()

ret

}

}

.data COUNTER_DEFAULT = int32(3)

.class interface abstract public auto autochar ICountDisplay

{

.method virtual abstract public hidebysig instance void SetCount(int32 count) il managed {}

.method virtual abstract public hidebysig instance int32 GetCount() il managed {}

}

.class interface abstract auto autochar public IStartStopEventSource

{

.method virtual abstract public hidebysig instance void add_StartStopEvent(class StartStopEventHandler) il managed {}

}

.class public Count extends [mscorlib]System.Object

{

.field int32 count

.field static family int32 counterDefault at COUNTER_DEFAULT

.field  class ICountDisplay display

.method public instance void .ctor(class ICountDisplay display)

{

ldarg.0

call instance void [mscorlib]System.Object::.ctor()

ldarg.0

ldarg   display

stfld   class ICountDisplay Count::display

ldarg.0

ldsfld  int32 Count::counterDefault

callvirt instance void Count::set_Count(int32)

ret

}

.property int32 Count()

{

.backing int32 count

.get instance int32 get_Count()

.set instance void set_Count(int32)

.other instance void refresh_Count()

}

.method virtual newslot instance int32 get_Count()

{

ldarg.0

ldfld   int32 Count::count

ret

}

.method virtual newslot instance void set_Count(int32 newCount) synchronized

{

ldarg.0

ldarg   newCount

stfld   int32 Count::count

ldarg.0

ldfld   class ICountDisplay Count::display

ldarg   newCount

callvirt instance void ICountDisplay::SetCount(int32)

ret

}

.method virtual newslot instance void refresh_Count() synchronized

{

ldarg.0

dup

ldfld   class ICountDisplay Count::display

callvirt instance int32 ICountDisplay::GetCount()

stfld   int32 Count::count

ret

}

}

.class public Counter extends [mscorlib]System.Object

{

.field class [System.Timers]System.Timers.Timer timer

.field  class [mscorlib]System.EventHandler timerEventHandler

.field  class Count count

.field  class IStartStopEventSource startStopEventSource

.field  class StartStopEventHandler startStopEventHandler

.field  class TimeUpEventHandler timeUpEventHandler

.method instance void .ctor(class IStartStopEventSource startStopEventSource, class Count count)

 {

ldarg.0

call instance void [mscorlib]System.Object::.ctor()

ldarg.0

ldarg   startStopEventSource

stfld   class IStartStopEventSource Counter::startStopEventSource

ldarg.0

ldarg   count

stfld   class Count Counter::count

ldarg.0

callvirt instance void Counter::SetupTimer()

ldarg.0

callvirt instance void Counter::SetupStartStopEvent()

ret

}

.method virtual newslot instance void SetupTimer()

{

ldarg.0

ldc.r8  1000

newobj  instance void [System.Timers]System.Timers.Timer::.ctor(float64)

stfld   class [System.Timers]System.Timers.Timer Counter::timer

ldarg.0

ldfld   class [System.Timers]System.Timers.Timer Counter::timer

ldc.i4.1

call    instance void [System.Timers]System.Timers.Timer::set_AutoReset(bool)

ldarg.0

dup

dup

ldvirtftn instance void Counter::OnTick(class System.Object, class [mscorlib]System.EventArgs)

newobj  instance void [mscorlib]System.EventHandler::.ctor(class System.Object, int32)

stfld   class [mscorlib]System.EventHandler Counter::timerEventHandler

ldarg.0

ldfld   class [System.Timers]System.Timers.Timer Counter::timer

ldarg.0

ldfld   class [mscorlib]System.EventHandler Counter::timerEventHandler

call    instance void [System.Timers]System.Timers.Timer::add_Tick(class [mscorlib]System.EventHandler)

ret

}

.method virtual newslot instance instance void SetupStartStopEvent()

{

ldarg.0

dup

ldftn   instance void Counter::OnStartStop(int32)

newobj  instance void StartStopEventHandler::.ctor(class System.Object, int32)

stfld   class StartStopEventHandler Counter::startStopEventHandler

ldarg.0

ldfld   class IStartStopEventSource Counter::startStopEventSource

ldarg.0

ldfld   class StartStopEventHandler Counter::startStopEventHandler

callvirt instance void IStartStopEventSource::add_StartStopEvent(class StartStopEventHandler)

ret

}

.method instance void OnStartStop(int32 action)

{

ldarg   action

brtrue  start

ldarg.0

call    instance void Counter::Stop()

br      done

start:

ldarg.0

call    instance void Counter::Start()

done:

ret

}

.method private hidebysig instance void Start() il managed {

ldarg.0

ldfld class Count Counter::count

callvirt instance void Count::refresh_Count()

ldarg.0

ldfld class Count Counter::count

callvirt instance int32 Count::get_Count()

ldc.i4.0

ble do_not_start

ldarg.0

ldfld class [System.Timers]System.Timers.Timer Counter::timer

call instance void [System.Timers]System.Timers.Timer::Start()

br done

do_not_start:

ldarg.0

callvirt instance void Counter::fire_TimeUpEvent()

done:

ret

}

.method private hidebysig instance void Stop()

{

ldarg.0

ldfld   class [System.Timers]System.Timers.Timer Counter::timer

call    instance void [System.Timers]System.Timers.Timer::Stop()

ret

}

.method virtual newslot family hidebysig instance void OnTick(class System.Object, class [mscorlib]System.EventArgs) il managed {

ldarg.0

ldfld   class Count Counter::count

dup

callvirt instance int32 Count::get_Count()

ldc.i4.1

sub

callvirt instance void Count::set_Count(int32)

ldarg.0

ldfld   class Count Counter::count

callvirt instance int32 Count::get_Count()

ldc.i4.0

ble time_up

br done

time_up:

ldarg.0

call    instance void Counter::Stop()

ldarg.0

callvirt instance void Counter::fire_TimeUpEvent()

done:

ret

}

.event TimeUpEventHandler TimeUpEvent

{

.addon instance void add_TimeUp(class TimeUpEventHandler 'handler')

.removeon instance void remove_TimeUp(class TimeUpEventHandler 'handler')

.fire instance void fire_TimeUpEvent()

}

.method virtual newslot instance void add_TimeUp(class TimeUpEventHandler 'handler') il managed {

ldarg.0

dup

ldfld   class TimeUpEventHandler Counter::timeUpEventHandler

ldarg   'handler'

call    class[mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate)

castclass TimeUpEventHandler

stfld   class TimeUpEventHandler Counter::timeUpEventHandler

ret

}

.method virtual newslot instance void remove_TimeUp(class TimeUpEventHandler 'handler') il managed {ret}

.method virtual newslot instance void fire_TimeUpEvent()

{

ldarg.0

ldfld   class TimeUpEventHandler Counter::timeUpEventHandler

callvirt instance void TimeUpEventHandler::Invoke()

ret

}

}

.class public BeepingCounter extends Counter

{

.method instance void .ctor(class IStartStopEventSource startStopEventSource, class Count count) il managed {

ldarg.0

ldarg   startStopEventSource

ldarg   count

call    instance void Counter::.ctor(class IStartStopEventSource, class Count)

ret

}

.method virtual instance void OnTick(class System.Object object, class [mscorlib]System.EventArgs eventArgs)

{

ldarg.0

ldarg   object

ldarg   eventArgs

call instance void Counter::OnTick(class System.Object, class [mscorlib]System.EventArgs)

ldarg.0

dup

ldfld   class Count Counter::count

callvirt instance int32 Count::get_Count()

ldc.i4.0

ble  final_beep

ldc.i4.0

br beep_it

final_beep:

ldc.i4.1

beep_it:

callvirt instance void BeepingCounter::Beep(bool)

ret

}

.method virtual newslot instance void Beep(bool finalBeep)

{

ldarg   finalBeep

brtrue  'final'

ldc.i4.0

br      continue

'final':

ldc.i4  48

continue:

call    int8 User32::MessageBeep(unsigned int32)

pop

ret

}

}

.class abstract sealed public auto autochar User32 extends [mscorlib]System.Object {

.method public static pinvokeimpl("user32.dll" cdecl) int8 MessageBeep(unsigned int32) native unmanaged {}

}

.class private sealed auto autochar StartStopEventHandler extends [mscorlib]System.MulticastDelegate {

.method public specialname rtspecialname hidebysig instance void .ctor(class System.Object object, int32 'method') runtime managed {}

.method virtual newslot public hidebysig instance void Invoke(int32 action) runtime managed {}

.method virtual newslot public hidebysig instance class ['mscorlib']System.IAsyncResult BeginInvoke(int32 action,class ['mscorlib']System.AsyncCallback callback, class System.Object object) runtime managed {}

.method virtual newslot  public hidebysig instance void EndInvoke(class ['mscorlib']System.IAsyncResult result) runtime managed {}

}

.class private sealed auto autochar TimeUpEventHandler extends [mscorlib]System.MulticastDelegate {

.method public specialname rtspecialname hidebysig instance void .ctor(class System.Object object, int32 'method') runtime managed {}

.method virtual newslot public hidebysig instance void Invoke() runtime managed {}

.method virtual newslot public newslot hidebysig instance class ['mscorlib']System.IAsyncResult BeginInvoke(class ['mscorlib']System.AsyncCallback callback, class System.Object object) runtime managed {}

.method virtual newslot public hidebysig instance void EndInvoke(class ['mscorlib']System.IAsyncResult result) runtime managed {}

}

.class public ssb extends [System.WinForms]System.WinForms.Button implements IStartStopEventSource

{

.field  class [mscorlib]System.EventHandler onClickEventHandler

.field  class TimeUpEventHandler timeUpEventHandler

.field  class StartStopEventHandler startStopEventHandler

.field  bool state 

.method instance void .ctor(class [System.WinForms]System.WinForms.Form parent)

{

ldarg.0 

call instance void [System.WinForms]System.WinForms.Button::.ctor()

ldarg.0

ldc.i4.0

stfld   bool ssb::state

ldarg.0

ldstr   "Start"

callvirt instance void [System.WinForms]System.WinForms.Button::set_Text(class System.String)

ldarg parent

call instance class [System.WinForms]System.WinForms.Control$ControlCollection [System.WinForms]System.WinForms.Form::get_Controls()

ldarg.0

callvirt instance void [System.WinForms]System.WinForms.Control$ControlCollection::Add(class [System.WinForms]System.WinForms.Control)

ldarg.0

dup

dup

ldvirtftn instance void ssb::OnClick(class System.Object, class [mscorlib]System.EventArgs)

newobj  instance void [mscorlib]System.EventHandler::.ctor(class System.Object, int32)

stfld   class [mscorlib]System.EventHandler ssb::onClickEventHandler

ldarg.0

dup

ldfld   class [mscorlib]System.EventHandler ssb::onClickEventHandler

call    instance void [System.WinForms]System.WinForms.Button::add_Click(class [mscorlib]System.EventHandler)

ret

}

.method virtual newslot instance void SetState(int32 newState)

{

ldarg.0

ldarg   newState

stfld   bool ssb::state

ldarg   newState

ldc.i4.0

beq stop_state

ldarg.0

ldstr   "Stop"

callvirt instance void ssb::set_Text(class System.String)

br done

stop_state:

ldarg.0

ldstr   "Start"

callvirt instance void ssb::set_Text(class System.String)

done:

ret

}

.method virtual newslot public instance void OnClick(class System.Object, class [mscorlib]System.EventArgs)

{

ldarg.0

callvirt instance void ssb::fire_StartStopEvent()

ret

}

.method public instance void AddToTimeUp(class Counter counter)

{

ldarg.0

dup

ldftn   instance void ssb::OnTimeUp()

newobj  instance void TimeUpEventHandler::.ctor(class System.Object, int32)

stfld   class TimeUpEventHandler ssb::timeUpEventHandler

ldarg   counter

ldarg.0

ldfld   class TimeUpEventHandler ssb::timeUpEventHandler

call    instance void Counter::add_TimeUp(class TimeUpEventHandler)

ret

}

.method virtual newslot instance void OnTimeUp()

{

ldarg.0

ldc.i4.0

callvirt instance void ssb::SetState(int32)

ret

}

.event StartStopEventHandler StartStopEvent

{

.addon instance void add_StartStopEvent(class StartStopEventHandler 'handler')

.fire instance void fire_StartStopEvent()

}

.method virtual newslot instance void add_StartStopEvent(class StartStopEventHandler 'handler')

{

ldarg.0

dup

ldfld   class StartStopEventHandler ssb::startStopEventHandler

ldarg   'handler'

call    class[mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate)

castclass StartStopEventHandler

stfld   class StartStopEventHandler ssb::startStopEventHandler

ret

}

.method virtual newslot instance void fire_StartStopEvent()

{

ldarg.0

ldfld   bool ssb::state

brtrue  stop_it

ldarg.0

ldc.i4.1        // start counter

callvirt instance void ssb::SetState(int32)

br      continue

stop_it:

ldarg.0

ldc.i4.0        // stop counter

callvirt instance void ssb::SetState(int32)

continue:

ldarg.0

ldfld   class StartStopEventHandler ssb::startStopEventHandler

ldarg.0

ldfld   bool ssb::state

callvirt instance void StartStopEventHandler::Invoke(int32)

ret

}

}

 

Let us first start with the file countdown.il. We will proceed extremely cautiously, in a step by step manner, so that you can understand how to write a large IL program. Fields will be explained only just prior to using them.

 

We start with the following directives viz assembly, assembly extern, file and module.

In aaa.dll, class zzz extends from the class Form as it is a Winforms application. The entrypoint method is Main. An object that looks like zzz is created using newobj and it is then placed on the stack. Using the function Run, that accepts a parameter that looks like Form, a window is displayed. This function keeps executing until the user closes the window.

 

But before this, the constructor of class zzz puts in a lot of hard work.

 

The constructor of class zzz first calls the constructor of class Form. It is necessary to call the base class constructor here as there may be some initial routine to be executed. Why take a chance?

 

While calling the constructor of the base class or super class, the this pointer is placed on the stack, unlike the instruction newobj.

 

Now to place a button on the screen:

 

 A new object that looks like the button class ssb is created. Even though the constructor of this class needs only one parameter, the this pointer is placed twice. This is so because the reference of the ssb object is to be stored in a field button using the stfld instruction.

 

A quick look at the code of the constructor of class ssb present in aaa.dll at runtime and file aaa.il.

 

The class ssb implements the interface IStartStopEventSource. It is also derived from the Button class. This interface has one function called add_StartStopEvent. The question that comes to mind is: Why should we have an interface at all? The only advantage of having it is that, an ssb object can now be referred to as an ssb object, or a Button class or an interface. Any object that looks like IStartStopEventSource will now be  supplied with an ssb object. This too can be overridden. Since ssb is derived from Button, it contains all the functionality of a button object.

 

There is no bool data type in IL. This data type is converted to a variable that will store either a 0 or 1. Thus, the field state indicates whether the timer is on or off. As its value is set to zero, the timer is currently off.  The label of the button is set to Start. Then using the Add function, the button is registered with Winforms, to be displayed later.

 

With every click on the button, the function OnClick from the ssb class is to be called. The function add_Click is used to accomplish this task.

 

The next call made is to the constructor of the ctb class and this value is stored in the field counterBox within the class zzz. The class ctb represents a text box in the program. It is derived from the class TextBox and it implements the interface ICountDisplay. This interface has two members, SetCount and GetCount, whose job is to deal with the number displayed in the text box.

 

We seem to be digressing from the topic. Back again, the function set_Location from the TextBox class is used  to position the text box on the screen and eventually the text box too is registered  with Winforms.

 

Next, an object, an instance of the class Count is created by passing the constructor a textbox reference, disguised as ICountDisplay. This object is stored in the local count. The class Count is a stand-alone class, since it is derived only from Object. Every class may not explicitly derive from Object

 

A field display, that looks like interface ICountDisplay is created and the textbox reference is saved in it. Thus, the class Count uses this field, display, to get and set the text box at will, since it now holds a reference to it.

 

We have also created a field called counterDefault, that is passed a value directly from the data section of the PE file. The .data directive uses the same words after the at, i.e. COUNTER_DEFAULT, which we have initialized to the value 3. We could have also used the instruction ldc to initialize this variable. We put its current value 3 on the stack and call the function set_Count from the class Count itself. The number 3 is passed as a parameter newCount, to this function.

 

There is an int32 type field called count in the class Count. At first the parameter newCount is saved in this field and then the reference to the text box is stored in the field display. Eventually, the function SetCount from class ctb is called; to be precise it is ICountDisplay. This displays a number 3 in the textbox. The field count stores the current displayed value.

 

Thus, function set_Count does little work. It internally  calls SetCount from class ctb to do the real work. The function SetCount first loads the parameter 3, passed to it, on the stack. It then converts it into a string using the static function ToString from the int class, which places the string on the stack. This string on the stack is used up by the set_Text function from the TextBox class, to actually display the string in the text box.

 

While we are at it, let us also understand the corresponding get functions. The function get_Count from the Count class simply returns the value of the field count. GetCount from class ctb, first uses function get_Text to place the string stored in the text box on the stack. Then, the static function ToInt32 from the String class is called to convert it into a number. In either case, the value is left on the stack. The last function in class Count is refresh_Count. This function first places the field display on the stack, and directly calls the virtual function GetCount from the class ctb. The return value is stored in the field count.

 

The directive property is for illustrative purposes only. It is used by tools to document the property. To refresh your memory, in IL, a property is simply a function, but in other languages like C#, properties have much more significance and make programming simpler. The get and set directives have already been explained earlier.

 

The backing directive denotes a field that will store the value of the property. In this case, we use count. The other directive is for functions that are part of a property, but do not cleanly fit in a get set world, like refresh_Count. As mentioned earlier, the directive property is optional.

 

 

An object that looks like BeepingCounter is the next in sequence to be created. Two parameters are given to the constructor, one a button and the other a variable that represents a count. The return value is held in a Counter field called counter. Note the type is not a BeepingCounter.

 

The class BeepingCounter is derived from Counter, which in turn is derived from Object. In the constructor of BeepingCounter, both the entities: one is the button or IStartStopEventSource interface and the other being count is loaded on the stack. The constructor in the base class Counter is the subsequent routine to be executed.

 

In the constructor:

 

A field called startStopEventSource stores the button reference and count stores the count reference. The function SetupTimer from the Counter class is called that creates a timer object. The constructor of this object accepts the timer click as a float parameter. This timer object is stored in a field called timer.

 

The property set_AutoReset to set to true to ensure that a function called OnTick is called when the timer is enabled. This is done by the function add_Tick of the timer class. All this code has been explained in detail in the earlier programs.

 

The last function that this constructor calls is SetupStartStopEvent, from the Counter class. In this function, at first the address of a function called OnStartStop is placed on the stack. A new object is created that is an instance of a class StartStopEventHandler.  This class is derived from MulticastDelegate and is passed the address of a function that is to be executed whenever the function Invoke is called.

 

This class is nothing but a delegate and contains no code at all, since all the code of the functions is to be supplied by the runtime. Thus, invoke will call the function OnStartStop. The field startStopEventHandler now contains the reference of this delegate. Two more parameters are placed on the stack, a button reference to call a function add_StartStopEvent from class ssb or the interface it extends and the next is a a parameter that the event handler just created above. 

 

The function add_StartStopEvent registers the earlier function with the runtime. If you recollect, in the delegate chapter we had explained the importance of the function Combine. A reserved word, when it is to be used as a parameter has to be placed in  single inverted quotes, as in the case of handler. It is casted to the correct class and stored in a field startStopEventHandler within ssb.

 

To avoid any more confusion, remember that a delegate simply calls a function indirectly. Thus, using this reference, the function OnStartStop can be called.

 

The last act of the zzz constructor is to place the button reference on the stack and call function AddToTimeUp with a counter reference as a parameter. In this function, the address of a function OnTimeUp is placed on the stack. Then an object is created which is an instance of TimeUpEventHandler. This is a class derived from MulticastDelegate, thus incorporating two delegates.

 

The function OnTimeUp is to be called when Invoke is executed. This delegate is stored in field TimeUpEventHandler in the ssb class. Finally add_TimeUp function is called with a counter as a parameter. This function completes the delegate handling by calling the Combine function and storing the reference in field TimeUpEventHandler. Two functions have been registered so far. Every delegate that is added, can also be removed. The function remove_TimeUp removes the delegate.

 

Like a property, an event also has a directive that has effect on compilers and tools only An event called TimeUpEvent that is an instance of class TimeUpEventHandler is available which has the usual .addon and .removeon directives and also a fire directive. The latter one supplies information as to  which function would be called by invoke. This is used for documentation purposes only. All the code that gets called, sets up the actual framework. The action starts only when the button is clicked on. On doing so, the function OnClick from class ssb gets called.

 

 

In function OnClick, another function fire_StartStopEvent is called from the same class. In this function, a check on the value of the field called state is maded. If you flip back a few pages, it was given an initial value of zero or false. As its value is false, the brtrue is not executed and a function called Set_State is called from class ssb with the value 1 as a parameter.

 

In the function, the parameter received becomes the new value of field state, whose value now changes from 0 to 1. This value and zero are placed on the stack and the instruction beq is executed.  A jump is made to the label if the two parameters are equal or, in other words, the value of state is zero. Being unequal for the moment, the string Stop is placed on the stack and set_Text is called to change the label to Stop. The course proceeds to the label done.

 

A round about turn to the function fire_StartStopEvent and back to the label continue. The delegate startStopEventHandler and the state of the button i.e. enabled or 1, is placed on the stack and the Invoke function is called. This calls the function OnStartStop in the class Counter.

 

This function checks the value of the parameter passed to it. Since the value of field state is 1 on the stack, the instruction brtrue jumps to the label start: and calls the function Start. In other circumstances, the function Stop is called. Thus, OnStartStop is called through Invoke and it will in turn either call the functions Start or Stop, depending upon the parameter value passed, either a 1 or 0, respectively.

 

In the function Start, refresh_Count refreshes the counter giving it a new value. If the value is less than zero, the program is to be terminated, so, the label do_not_start: is executed where the function fire_TimeUpEvent is called that ceases everything.

 

Presently, the timer is fired, so that the function OnTick is called every 1000 milliseconds. To achieve this, the function Start is called from the timer class.

 

The function OnTick is called from the class BeepingCounter and not from the class Counter. Next, the function OnTick is called from the class Counter. In function OnTick, which is called very second, the new value of the counter is displayed. This is achieved using the get_Count function. The value is decremented by 1 and set_Count function restores this new value back to the variable.

 

A check is performed on the value of count using ble. When zero, the timer is stopped by calling the function Stop from the timer class.

 

The class BeepingCounter.

 

The function Beep is called where the parameter is given to change the occurrence of beep sound. Obviously it should beep only once and terminate when the value of count is 0.

 

In the function Beep, a value of 0 or 48 is placed , depending upon whether the parameter is 0 or 1. Then, the actual MessageBeep function from class User32 is called for. This class is an abstract class and has a static function MessageBeep that is of type pinvokeimpl. This specifies that the code of this function is in user32.dll. Finally, the value returned by get_Count evaluates to zero. This will result in a call to Stop of the timer, so that the function OnTick is not called thereafter. The OnTimeUp function calls function SetState with a value of 0. This triggers off the shutdown procedure as explained above.

 

This is the most exhaustive explanation of any program we have given so far.