Ildasm: Viewing CIL

Ildasm is the Common Intermediate Language (CIL) disassembler ”it parses any .NET assembly and shows a graphical or textual representation of the CIL, name -spaces, types, and interfaces within the assembly. If you want to know what's really going on inside your code, Ildasm is the tool that will tell you. As well as analyzing your own assemblies, you can point Ildasm at assemblies belonging to other developers or even at the .NET Framework assemblies themselves .

Ildasm Within Visual Studio

The easiest way to check CIL on a regular basis is to create a new item on Visual Studio's Tools menu. You can do this by going to Tools External Tools and completing the External Tools dialog window with the following entries:

  • Title: CIL (or whatever title you want)

  • Command: C:\Program Files\Microsoft .Net\FrameworkSDK\bin\ ildasm.exe

  • Arguments: $(TargetPath) /adv /source

  • Initial directory: $(TargetDir)

This creates the new item on the Tools menu. Selecting this new item invokes Ildasm in graphical mode on the current assembly, allowing you to dig down to each method and examine the CIL. Notice the two switches that I have chosen to specify. The /adv switch adds some useful extra options to the Ildasm View menu (such as access to statistics), and the /source argument causes source code to be embedded into the CIL display as comments.

Ildasm from the Command Line

Invoking Ildasm from the command line is just as easy. The following command line tells Ildasm to create a disk file named containing the CIL extracted from the assembly MyAssembly.exe. Once again, note the use of the /source argument to embed source code as comments within the CIL listing.

 Ildasm MyAssembly.exe /source / 

Investigating Ildasm Code

To give you a good idea of Ildasm 's capabilities, I'm going to investigate an example that compares two different VB .NET error-handling techniques. When you wrote VB.Classic code, you were required to use the On Error Goto form of error handling. VB .NET still allows you to keep this legacy method of error handling, but it offers a more modern alternative in the form of Try Catch Finally . Now VB developers have to make decisions about which form of error handling is best, and even whether it is advisable to mix the two modes within an application.

Error handling is a large subject that will be investigated in depth as part of Chapter 13. Here I want to investigate a small subset of the problem. Should you replace the On Error Resume Next method of suppressing unwanted errors with Try Catch Finally ? Listing 5-1 shows the simple program that I'm going to use for experimentation. This program sets up a database connection string and then performs two database connection tests. The first test uses the On Error Resume Next form of error handling to suppress any connection error, clean up the connection, and then return true or false depending on whether the connection was made successfully. The second test is almost identical, but it instead uses the Try Catch Finally form of error handling.

Listing 5-1. Error Handling Comparison
start example
 Option Strict On Imports System.Data.SqlClient Module ErrorTest    Sub  Main  ()      Dim strConnection As String  'Set up database connection  strConnection = "Initial Catalog = Northwind;"      strConnection += "Data Source = CHEETAH;"      strConnection += "Integrated Security = SSPI"  'Try old and new error-handling functions  MethodOld(strConnection)      MethodNew(strConnection)    End Sub    Function  MethodOld  (ByVal ConnectString As String) As Boolean      Dim objSqlConnect As SqlConnection  'Test database connection with old error handling  On Error Resume Next      objSqlConnect = New      SqlConnection(ConnectString)      MethodOld = CBool(Err.Number = 0)      objSqlConnect.Close()      objSqlConnect.Dispose()    End Function    Function  MethodNew  (ByVal ConnectString As String) As Boolean      Dim objSqlConnect As SqlConnection  'Test database connection with new error handling  Try         objSqlConnect = New SqlConnection(ConnectString)         objSqlConnect.Close()         objSqlConnect.Dispose()         Return True      Catch         Return False      Finally      End Try    End Function  End Module 
end example

If you compile this program as a debug build, remembering to replace CHEETAH with your own data source, and then disassemble the resulting executable using Ildasm , it is possible to compare the CIL generated for each of these two methods and make some sort of comparison of their relative efficiency.

Examining the CIL

Now you can examine the CIL generated for each of the two test methods. As before, the /source flag means that the VB .NET source code is added as comments to the CIL code. Listing 5-2 shows the CIL generated for the legacy method of error handling, and Listing 5-3 shows its more modern counterpart . The VB .NET source lines are highlighted in bold.

Listing 5-2. CIL Produced by On Error Resume Next
start example
 .method public static bool MethodOld(string ConnectString) cil managed {      // Code size   174 (0xae)      .maxstack 2      .locals init ([0] bool MethodOld,               [1] class [System.Data]System.Data.SqlClient.SqlConnection objSqlConnect,               [2] int32 _Vb_t_CurrentStatement,               [3] class [mscorlib]System.Exception _Vb_t_Exception,               [4] int32 _Vb_t_Resume,               [5] int32 _Vb_t_OnError)  //000017: //000018:      Function MethodOld(ByVal ConnectString As String) As Boolean  IL_0000:     nop  //000019:          Dim objSqlConnect As SqlConnection  //000020:  //000021:           'Test database connection //000022:           On Error Resume Next  IL_0001:       call           void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices. ProjectData::ClearProjectError()        IL_0006:     ldc.i4.1        IL_0007:     stloc.s _Vb_t_OnError  //000023:     objSqlConnect = New SqlConnection(ConnectString)  IL_0009:      ldc.i4.1        IL_000a:      stloc.2        IL_000b:      ldarg.0        IL_000c:      newobj     instance void [System.Data]System.Data.SqlClient.SqlConnection::.ctor(string)        IL_0011:     stloc.1  //000024: MethodOld = CBool(Err.Number = 0)  IL_0012:     ldc.i4.2        IL_0013:     stloc.2        IL_0014:     call     class [Microsoft.VisualBasic]Microsoft.VisualBasic.ErrObject [Microsoft.VisualBasic]Microsoft.VisualBasic.Information::Err()        IL_0019:     callvirt     instance int32 [Microsoft.VisualBasic]Microsoft.VisualBasic.ErrObject::get_ Number()        IL_001e:     ldc.i4.0        IL_001f:     ceq        IL_0021:      stloc.0  //000025:     objSqlConnect.Close()  IL_0022:     ldc.i4.3        IL_0023:     stloc.2        IL_0024:     ldloc.1        IL_0025:     callvirt     instance void [System.Data]System.Data.SqlClient.SqlConnection::Close()        IL_002a:      nop  //000026: objSqlConnect.Dispose()  IL_002b:     ldc.i4.4        IL_002c:     stloc.2        IL_002d:     ldloc.1        IL_002e:     callvirt     instance void [System]System.ComponentModel.Component::Dispose()        IL_0033:     nop        IL_0034:     leave.s           IL_00a3        IL_0036:      ldloc.s      _Vb_t_Resume        IL_0038:     ldc.i4.1        IL_0039:     add        IL_003a:     ldc.i4.0        IL_003b:     stloc.s          _Vb_t_Resume        IL_003d:     switch          (        IL_0001,                                       IL_0009,                                       IL_0012,                                       IL_0022,                                       IL_002b,                                       IL_0034)        IL_005a:     leave.s          IL_00a1        IL_005c:     isinst           [mscorlib]System.Exception        IL_0061:     brtrue.s         IL_0065        IL_0063:      br.s            IL_0070        IL_0065:     ldloc.s          _Vb_t_OnError        IL_0067:     brfalse.s        IL_0070        IL_0069:     ldloc.s          _Vb_t_Resume        IL_006b:     brtrue.s         IL_0070        IL_006d:     ldc.i4.1        IL_006e:     br.s IL_0073        IL_0070:     ldc.i4.0        IL_0071:     br.s             IL_0073        IL_0073:     endfilter        IL_0075:     castclass     [mscorlib]System.Exception        IL_007a:     dup        IL_007b:     call          void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.ProjectData:: SetProjectError(class [mscorlib]System.Exception)        IL_0080:     stloc.3        IL_0081:     ldloc.s          _Vb_t_Resume        IL_0083:     brfalse.s        IL_0087        IL_0085:     leave.s          IL_00a1        IL_0087:     ldloc.2        IL_0088:     stloc.s          _Vb_t_Resume        IL_008a:     ldloc.s          _Vb_t_OnError        IL_008c:     switch           (                                        IL_009b,                                        IL_009d)        IL_0099:     leave.s           IL_009f        IL_009b:     leave.s           IL_009f        IL_009d:     leave.s           IL_0036        IL_009f:     rethrow        IL_00a1:     ldloc.3        .try IL_0001 to IL_005c filter IL_005c handler IL_0075 to IL_00a1        IL_00a2:     throw  //000027: //000028:      End Function  IL_00a3:     ldloc.0        IL_00a4:     ldloc.s           _Vb_t_Resume        IL_00a6:     brfalse.s         IL_00ad        IL_00a8:     call void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.ProjectData:: ClearProjectError()        IL_00ad:     ret      } // end of method ErrorTest::MethodOld 
end example
Listing 5-3. CIL Produced by Try Catch Finally
start example
 .method public static bool MethodNew(string ConnectString) cil managed        {          // Code size 56 (0x38)          .maxstack 1          .locals init ([0] bool MethodNew,                    [1] class [System.Data]System.Data.SqlClient.SqlConnection objSqlConnect)  //000029: //000030:      Function MethodNew(ByVal ConnectString As String) As Boolean  IL_0000:      nop  //000031: Dim objSqlConnect As SqlConnection  //000032:  //000033:      'Test database connection //000034:      Try  IL_0001:     nop  //000035:           objSqlConnect = New SqlConnection(ConnectString)  .try        {         .try         {          IL_0002:     ldarg.0          IL_0003:     newobj          instance void [System.Data]System.Data.SqlClient.SqlConnection::.ctor(string)          IL_0008:      stloc.1  //000036:           objSqlConnect.Close()  IL_0009:     ldloc.1          IL_000a:     callvirt          instance void [System.Data]System.Data.SqlClient.SqlConnection::Close()          IL_000f:     nop  //000037:           objSqlConnect.Dispose()  IL_0010:     ldloc.1          IL_0011:     callvirt          instance void [System]System.ComponentModel.Component::Dispose()          IL_0016:     nop  //000038: Return True  IL_0017:     ldc.i4.1          IL_0018:     stloc.0          IL_0019:     leave.           IL_0036          IL_001b:     leave.s           IL_0035  //000039:          Catch  }      // end.try        catch [mscorlib]System.Exception        {          IL_001d:     call           void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices. ProjectData::SetProjectError(class [mscorlib]System.Exception)          IL_0022:     nop  //000040:          Return False  IL_0023:     ldc.i4.0          IL_0024:     stloc.0          IL_0025:     cal           void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices. ProjectData::ClearProjectError()          IL_002a:     leave.s          IL_0036          IL_002c:     call          void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices. ProjectData::ClearProjectError()          IL_0031:     leave.s          IL_0035  //000041:          Finally  } // end handler       } // end.try       finally       {          IL_0033:     nop          IL_0034:     endfinally  //000042:          End Try  } // end handler          IL_0035:     nop  //000043: //000044:          End Function  IL_0036:      ldloc.0          IL_0037:      ret}        // end of method ErrorTest::MethodNew 
end example

The most obvious result is that the older form of error handling produces 125 lines of CIL, including 9 comment lines containing VB .NET source. The newer form of error handling generates 76 lines of CIL, including 14 comment lines containing VB .NET source. Although it's true that the more compact (by 40%) CIL may not necessarily mean better or faster code, a deeper investigation of the longer CIL routine shows some rather peculiar gyrations. There are some clear signs that, at least in this particular instance, the more modern form of error handling is better.

As you can see from the preceding experiment, Ildasm is very useful for deeper investigations. When you want to be sure of what the code is really doing under the hood, seeing the CIL can be very instructive. If you want to delve further into CIL itself and how it works, see the "CIL Instruction Set Specification" document, which you can find in the Partition III CIL.doc file in the FrameworkSDK\Tool Developers Guide\Docs folder.

Comprehensive VB .NET Debugging
Comprehensive VB .NET Debugging
ISBN: 1590590503
EAN: 2147483647
Year: 2003
Pages: 160
Authors: Mark Pearce © 2008-2017.
If you may any questions please contact us: