Label Form of SEH Clause Declaration
The most generic form of IL assembly language (ILAsm) notation of an EH clause is as follows:
.try <label> to <label> <EH_type_specific> handler <label> to <label>
where <EH_type_specific> ::=
catch <class_ref> filter <label> finally fault
Take a look at this example:
BeginTry: leave KeepGoing BeginHandler: leave KeepGoing KeepGoing: ret .try BeginTry to BeginHandler catch [mscorlib]System.Exception handler BeginHandler to KeepGoing
Figure 11-1 Engagement of the finally exception handler.
In the final lines of the example, the code .try <label> to <label> defines the guarded block, and handler <label> to <label> defines the handler block. In both cases, the second <label> is exclusive, pointing at the first instruction after the respective block. ILAsm imposes a limitation on the positioning of the EH clause declaration directives: all labels used in the directives must have already been defined. Thus, the best place for EH clause declarations in the label form is at the end of the method scope.
In the case just presented, the handler block immediately follows the guarded block, but we could put the handler block anywhere within the method, provided it does not overlap with the guarded block or other handlers:
br AfterHandler // Can't enter the handler block on our own BeginHandler: leave KeepGoing AfterHandler: BeginTry: leave KeepGoing KeepGoing: ret .try BeginTry to KeepGoing catch [mscorlib]System.Exception handler BeginHandler to AfterHandler
A single guarded block can have several handlers:
br AfterHandler2 // Can't enter the handler block(s) on our own BeginHandler1: leave KeepGoing AfterHandler1: BeginHandler2: leave KeepGoing AfterHandler2: BeginTry: leave KeepGoing KeepGoing: ret .try BeginTry to KeepGoing catch [mscorlib]System.StackOverflowException handler BeginHandler1 to AfterHandler1 .try BeginTry to KeepGoing catch [mscorlib]System.Exception handler BeginHandler2 to AfterHandler2
In the case of multiple handlers—catch or filter, but not finally or fault—the guarded block declaration need not be repeated:
.try BeginTry to KeepGoing catch [mscorlib]System.StackOverflowException handler BeginHandler1 to AfterHandler1 catch [mscorlib]System.Exception handler BeginHandler2 to AfterHandler2
The lexical order of handlers belonging to the same guarded block is the order in which the ILAsm compiler emits the EH clauses, and hence is the same order in which the execution engine of the runtime processes these clauses. We must be careful about ordering the handlers. For instance, if we swap the handlers in the preceding example, the handler for [mscorlib]System.Exception will always work and the handler for [mscorlib]System.StackOverflowException will never work. This is because all exceptions are derived, eventually, from [mscorlib]System.Exception, and hence all exceptions are caught by the first handler, leaving the other handlers unemployed.
The finally and fault handlers cannot peacefully coexist with other handlers, so if a guarded block has a finally or fault handler, it cannot have anything else. To combine a finally or fault handler with other handlers, we need to nest the guarded and handler blocks within other guarded blocks, as shown in Figure 11-1, so that each finally or fault handler has its own personal guarded block.