![]()
-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