⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 seh.gml

📁 开放源码的编译器open watcom 1.6.0版的源代码
💻 GML
📖 第 1 页 / 共 3 页
字号:
.chap *refid=seh Structured Exception Handling
.*
.np
.ix 'structured exception handling'
Microsoft-style Structured Exception Handling (SEH) is supported by
the &cmpcname compiler only.
MS SEH is supported under the Win32, Win32s and OS/2 platforms.
You should not confuse SEH with C++ exception handling.
The &cmppname compiler supports the standard C++ syntax for exception
handling.
.np
The following sections introduce some of the aspects of SEH.
For a good description of SEH, please refer to
.book Advanced Windows NT
by Jeffrey Richter (Microsoft Press, 1994).
You may also wish to read the article "Clearer, More Comprehensive
Error Processing with Win32 Structured Exception Handling" by Kevin
Goodman in the January, 1994 issue of Microsoft Systems Journal.
.*
.section Termination Handlers
.*
.np
.ix '_try'
.ix '_finally'
We begin our look at SEH with a simple model.
In this model, there are two blocks of code &mdash. the "guarded" block
and the "termination" block.
The termination code is guaranteed to be executed regardless of how
the "guarded" block of code is exited (including execution of any
"return" statement).
.millust begin
_try {
    /* guarded code */
    .
    .
    .
}
_finally {
    /* termination handler */
    .
    .
    .
}
.millust end
.np
The
.kw _finally
block of code is guaranteed to be executed no matter how the guarded
block is exited (
.kw break,
.kw continue,
.kw return,
.kw goto,
or
.kw longjmp()
.ct ).
Exceptions to this are calls to
.kw abort(),
.kw exit()
or
.kw _exit()
which terminate the execution of the process.
.np
There can be no intervening code between
.us try
and
.us finally
blocks.
.np
The following is a contrived example of the use of
.kw  _try
and
.kw _finally.
.exam begin
#include <stdio.h>
#include <stdlib.h>
#include <excpt.h>

int docopy( char *in, char *out )
{
  FILE        *in_file = NULL;
  FILE        *out_file = NULL;
  char        buffer[256];
.exam break
  _try {
    in_file = fopen( in, "r" );
    if( in_file == NULL ) return( EXIT_FAILURE );
    out_file = fopen( out, "w" );
    if( out_file == NULL ) return( EXIT_FAILURE );

    while( fgets((char *)buffer, 255, in_file) != NULL ) {
      fputs( (char *)buffer, out_file );
    }
  }
  _finally {
    if( in_file != NULL ) {
      printf( "Closing input file\n" );
      fclose( in_file );
    }
    if( out_file != NULL ) {
      printf( "Closing output file\n" );
      fclose( out_file );
    }
    printf( "End of processing\n" );
  }
  return( EXIT_SUCCESS );
}
.exam break
void main( int argc, char **argv )
{
  if( argc < 3 ) {
    printf( "Usage: mv [in_filename] [out_filename]\n" );
    exit( EXIT_FAILURE );
  }
  exit( docopy( argv[1], argv[2] ) );
}
.exam end
.np
The
.us try
block ignores the messy details of what to do when either one of the
input or output files cannot be opened.
It simply tests whether a file can be opened and quits if it cannot.
The
.us finally
block ensures that the files are closed if they were opened,
releasing the resources associated with open files.
This simple example could have been written in C without the use of
SEH.
.np
There are two ways to enter the
.us finally
block.
One way is to exit the
.us try
block using a statement like
.kw return.
The other way is to fall through the end of the
.us try
block and into the
.us finally
block (the normal execution flow for this program).
Any code following the
.us finally
block is only executed in the second case.
You can think of the
.us finally
block as a special function that is invoked whenever an exit (other
than falling out the bottom) is attempted from a corresponding
.us try
block.
.np
More formally stated, a local unwind occurs when the system executes
the contents of a
.us finally
block because of the premature exit of code in a
.us try
block.
.remark
Kevin Goodman describes "unwinds" in his article.
"There are two types of unwinds: global and local. A global unwind
occurs when there are nested functions and an exception takes place. A
local unwind takes place when there are multiple handlers within one
function. Unwinding means that the stack is going to be clean by the
time your handler's code gets executed."
.eremark
.np
The
.us try/finally
structure is a rejection mechanism which is useful when a set of
statements is to be conditionally chosen for execution, but not all
of the conditions required to make the selection are available
beforehand.
It is an extension to the C language.
You start out with the assumption that a certain task can be
accomplished.
You then introduce statements into the code that test your hypothesis.
The
.us try
block consists of the code that you assume, under normal conditions,
will succeed.
Statements like
.us if ... return
can be used as tests.
Execution begins with the statements in the
.us try
block.
If a condition is detected which indicates that the assumption
of a normal state of affairs is wrong, a
.kw return
statement may be executed to cause control to be passed to the
statements in the
.us finally
block.
If the
.us try
block completes execution without executing a
.kw return
statement (i.e., all statements are executed up to the final brace),
then control is passed to the first statement following the
.us try
block (i.e., the first statement in the
.us finally
block).
.np
In the following example, two sets of codes and letters are read in
and some simple sequence checking is performed.
If a sequence error is detected, an error message is printed and
processing terminates; otherwise the numbers are processed and another
pair of numbers is read.
.exam begin
#include <stdio.h>
#include <stdlib.h>
#include <excpt.h>

void main( int argc, char **argv )
{
  read_file( fopen( argv[1], "r" ) );
}
.exam break
void read_file( FILE *input )
{
  int         line = 0;
  char        buffer[256];
  char        icode;
  char        x, y;

  if( input == NULL ) {
    printf( "Unable to open file\n" );
    return;
  }
.exam break
  _try {
    for(;;) {
      line++;
      if( fgets( buffer, 255, input ) == NULL ) break;
      icode = buffer[0];
      if( icode != '1' ) return;
      x = buffer[1];
      line++;
      if( fgets( buffer, 255, input ) == NULL ) return;
      icode = buffer[0];
      if( icode != '2' ) return;
      y = buffer[1];
      process( x, y );
    }
    printf( "Processing complete\n" );
    fclose( input );
    input = NULL;
  }
.exam break
  _finally {
    if( input != NULL ) {
      printf( "Invalid sequence: line = %d\n", line );
      fclose( input );
    }
  }
}
.exam break
void process( char x, char y )
{
    printf( "processing pair %c,%c\n", x, y );
}
.exam end
.np
The above example attempts to read a code and letter.
If an end of file occurs then the loop is terminated by the
.kw break
statement.
.np
If the code is not 1 then we did not get what we expected and an
error condition has arisen.
Control is passed to the first statement in the
.us finally
block by the
.kw return
statement.
An error message is printed and the open file is closed.
.np
If the code is 1 then a second code and number are read.
If an end of file occurs then we are missing a complete set of data
and an error condition has arisen.
Control is passed to the first statement in the
.us finally
block by the
.kw return
statement.
An error message is printed and the open file is closed.
.np
Similarly if the expected code is not 2 an error condition has arisen.
The same error handling procedure occurs.
.np
If the second code is 2, the values of variables
.id x
and
.id y
are processed (printed).
The
.kw for
loop is repeated again.
.np
The above example illustrates the point that all the information
required to test an assumption (that the file contains valid pairs of
data) is not available from the start.
We write our code with the assumption that the data values are correct
(our hypothesis) and then test the assumption at various points in the
algorithm.
If any of the tests fail, we reject the hypothesis.
.np
Consider the following example.
What values are printed by the program?
.exam begin
#include <stdio.h>
#include <stdlib.h>
#include <excpt.h>

void main( int argc, char **argv )
{
  int ctr = 0;
.exam break
  while( ctr < 10 ) {
    printf( "%d\n", ctr );
    _try {
      if( ctr == 2 ) continue;
      if( ctr == 3 ) break;
    }
    _finally {
      ctr++;
    }
.exam break
    ctr++;
  }
  printf( "%d\n", ctr );
}
.exam end
.np
At the top of the loop,
the value of
.id ctr
is 0.
The next time we reach the top of the loop,
the value of
.id ctr
is 2 (having been incremented twice, once by the
.us finally
block and once at the bottom of the loop).
When
.id ctr
has the value 2, the
.kw continue
statement will cause the
.us finally
block to be executed (resulting in
.id ctr
being incremented to 3),
after which execution continues at the top of the
.kw while
loop.
When
.id ctr
has the value 3, the
.kw break
statement will cause the
.us finally
block to be executed (resulting in
.id ctr
being incremented to 4),
after which execution continues after the
.kw while
loop.
Thus the output is:
.millust begin
0
2
3
4
.millust end
.np
The point of this exercise was that after the
.us finally
block is executed, the normal flow of execution is resumed
at the
.kw break,
.kw continue,
.kw return,
etc. statement and the normal behaviour for that statement occurs.
It is as if the compiler had inserted a function call just before
the statement that exits the
.us try
block.
.millust begin

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -