The .NET Runtime ships with a Just-In-Time (JIT or JITter) compiler, which will convert the MSIL code in to the native code (CPU Specific executable code). So whatever code we write will be complied in to MSIL format and the JIT takes over when you run it.
The .NET runtime/Common Language Runtime (CLR) ships three different classes of JITters. The Main JIT compiler converts the MSIL code it to native code with out any optimizations. The JIT compiler takes the MSIL code and optimizes it. So this compiler requires lot of resources like, time to compile, larger memory footprint, etc. The PreJIT is based on the Main JIT and it works like the traditional compilers (compiles MSIL to native code during compilation time rather than runtime). This compiler is usually used at
the time of installation.
No matter whatever language we used to develop the HelloWorld program, it’s a known fact that compiler’s are going to generate a MSIL format, once our code has been converted in to MSIL format, from MSIL format all the code that we write will be converted to native code in the same way whether if it is a VB.NET source or C# source.
Intermediate Language (IL)
To support our discussion let us examine the IL code of HelloWorld program written using VB.NET and C#. To visualize the IL code Microsoft provides a disassembler tool through which you can easily see the IL code
To use the tool, choose command prompt and type ILDASM->ILDASM dialog is shown-
> choose file open dialog and select the assembly
(make sure you set your path variable to point to the place where your ILDASM is available)
Figure showing disassembled HelloWorld program
The above window showing a tree displays the path of the assembly as the root node, manifest information and namespace information as the child node (if you do not specify the namespace for the class then class name will be shown instead of namespace).
Figure showing manifest information of helloworld program
The manifest information shows the dependent assemblies like mscorlib, Microsoft.VisualBasic and their versions and it self describes the HelloWorld assembly. Since we have a simple program, which does not contain any embedded resource, the manifest does not include any information on those.
Figure showing list of information present in the namespace
The above figure shows the list of information present within the namespace. In general the namespace contains the list of classes, structures, delegates, enums etc., In this case it shows the HelloWorld class which in turn contains the methods present in the class. It also shows the following information.
.. .class public auto ansi
The above figure shows that HelloWorld is derived from System.Object, System.Object is the base class in the .NET framework
.. .ctor : void()
The above figure shows the IL code of the constructor of HelloWorld Class, you can see that it in turn calls System.Object::.ctor(), which is the base class’s constructor
.. Main : void()
The above figure shows the IL code of the Main function, which is the entry point for that assembly. It also shows the method “System.Console::WriteLine” is called with the string “HelloWorld “ within the Main function
Compiler Options
If you can recollect the statement we have used to compile the HelloWorld program is vbc HelloWorld.vb for Vb.NET and csc HelloWorld.cs for C# , in this we have used the default settings of the compiler. Let us spend sometime in compiling the same code with some important options of the vbc /csc compiler.
In our program we have referred System.Dll assembly, in real life we would be application-referring lot of assemblies, in those cases the compiler should be intimated about the references. We can achieve this by the option mentioned below
/reference:<file-list> - needs to be used to indicate the list of references used by the application, in short it can also be represented as “/r”. In our case it will be represented like this statement given below
vbc /reference:”System.dll” HelloWorld.vb for Vb.NET
csc /reference:”System.dll” HelloWorld.cs for C#.NET
The compiler by default will produce a HelloWorld.exe, in case you want to create a module or a library, then you have to specify the target in the compiler. It can be done like this
vbc /target:library /reference:”System.dll” HelloWorld.vb => to generate a library
csc /target:library /reference:”System.dll” HelloWorld.cs => to generate a library
Executing the above line of statement in the command prompt will generate a HelloWorld.dll, in the same manner a module can be generated by applying this switch /target:module
In case if we would like to give a different name to the assembly file then the statement given below can be applied
vbc /target:exe /out:”SampleHelloWorld.exe” /reference:”System.dll”
HelloWorld.vb
csc /target:exe /out:”SampleHelloWorld.exe” /reference:”System.dll”
HelloWorld.cs
In the above statement the switch /out:<filename> is used to give a different name to the
output assembly file.
The above compiler statements what we have seen is for simple applications, let us assume we have an application which is a Win32 executable and it has got resources, which could be embedded or linked (More on resource file later). An embedded resource could be an image for the splash screen, in those cases the following compiler options will be used
/target:winexe - used to create a Win32 executable file
/linkresource:<resource file(s)> - used to link a resource file to the assembly
/resource:<resource file(s)> - used to embed a resource file to the assembly
/imports:<import list> - used to include the list of namespaces used by the assembly
For other compiler options refer .Net framework SDK documentation.
.NET Debugging
Debugging is the most important feature of any programming language and Visual Studio .NET IDE provides this feature in an effective manner (but you can still do pretty good job with the .NET SDK alone). Application source code goes through two distinct steps before a user can run it. First, the source code is compiled to Microsoft Intermediate Language (MSIL) code using a .NET compiler. Then, at runtime, the MSIL code is compiled to native code. When we debug a .NET application, this process works in reverse. The debugger first maps the native code to the MSIL code. The MSIL code is then mapped back to the source code using the programmer's database (PDB) file. In order to debug an application, these two mappings must be available to the .NET runtime environment.
To accomplish the mapping between the source code and the MSIL, use the/debug:pdbonly compiler switch to create the PDB file (Note: When building ASP.NET applications, specify the compilation setting debug="true" in the application’s Web.config file). The second mapping between the MSIL code and native code is accomplished by setting the JITTracking attribute in our assembly. By specifying the /debug compiler switch, the PDB file is created and the JITTracking attribute is enabled. When using this compiler switch, a debugger can be attached to an application loaded outside of the debugger.
Once the required mappings exist, there are several means by which to debug our applications. We can use the integrated debugger within Visual Studio .NET, or, if we prefer, we can use DbgClr, a GUI-based debugger. There is also a command line debugger, CorDBG that is included in the .NET Framework SDK.