Click here to Skip to main content
15,900,461 members
Articles / Operating Systems / Windows
Article

Another use of SEH

Rate me:
Please Sign up or sign in to vote.
3.43/5 (11 votes)
17 Jun 20044 min read 102.3K   24   29
How to use SEH to supply a parameter to a function.

Introduction

Suppose you have written a function with a specific set of parameters. However, this function is designed in such a way that sometimes it needs more parameters. What does that mean? Wait, I'll explain.

If this function is called by your own functions, which in turn are also called by your functions and so on, then you can simply supply all possible parameters to all functions from which our "problematic" function can ever be called. Thus our function will always receive all the parameters it may ever need. But the disadvantage of this solution is that you have to supply all possible parameters to every function in the chain. This takes more stack, and makes the program totally unreadable. And also, you have to modify dozens of your code to add one single parameter to a single function, because you'll have to add it to all your functions.

There can also be another situation, where your function is called from within another function, which is not yours. In fact - your function is a callback function with a predefined set of parameters, which can't be changed. Well, usually in such situations, smart design always allows you to specify at least one parameter to your callback function (a context), but, alas, not all designs are smart :). Also, sometimes you'd like to have more parameters anyway.

So, what can be done in such situations?

  1. Use of global variables. Before your function could be called, you set some values to global variables, which are accessible from anywhere. This is the most straight and stupid way. Use of global variables should be avoided as much as possible. What if you call this function in more than one situation? And what if you call this function from within another pending call to the same function? And what will you do in the multi-tasking environment? Well, all that can be solved, but in very ugly ways.
  2. We can improve a bit the previous solution by using TLS. We won't have to bother about multi-tasking synchronization. However, it still has almost the same disadvantages. In addition, you must initialize the TLS (allocate a TLS index), hence your code needs a global initialization/uninitialization.
  3. And, finally, there is a way to supply a parameter to a function without the use of global variables. It uses SEH (Windows structured exception handling). Take a look at the following sample:
const DWORD EXCEPTION_FETCHPARAM_B = 0x800A0001L;

void Func_Nested(int a)
{
    // do some job. At some situation we realize
    // that we need another parameter.
    // Let's fetch it.
    int b;

    ULONG_PTR nExceptionParam = (ULONG_PTR) &b;
    RaiseException(EXCEPTION_FETCHPARAM_B, 0, 1, &nExceptionParam);

    // By now 'b' is filled. Continue our work as usual.
}

void Func_Intermediate(int a)
{
    // do some job. At some point call nested function
    Func_Nested(a);
}

void Func_Top()
{
    // at this point we have all possible parameters.

    EXCEPTION_POINTERS* pException;
    __try
    {
        Func_Intermediate(12);
    }
    __except
        (
          pException = GetExceptionInformation(),
          EXCEPTION_FETCHPARAM_B == pException->ExceptionRecord->ExceptionCode ?
           (
             ASSERT(1 == pException->ExceptionRecord->NumberParameters),
             ASSERT(pException->ExceptionRecord->ExceptionInformation[0]),
             *((int*) pException->ExceptionRecord->ExceptionInformation[0]) = 14,
             EXCEPTION_CONTINUE_EXECUTION
           ) :
          EXCEPTION_CONTINUE_SEARCH
        )
    {
        // This point must not be reached,
        // since we never return EXCEPTION_EXECUTE_HANDLER
    }
}

In this sample, we start from Func_Top, where we have all possible parameters, from there we call Func_Intermediate, and this function eventually calls Func_Nested. At some point, it realizes that it needs one more parameter. It raises a SEH exception with one parameter, which is a pointer to a placeholder for a missing parameter. Next, the OS looks for a suitable handler for this exception. Our handler detects this exception by querying its code, writes the missing parameter, and then reports to the OS that the exception is dismissed by returning EXCEPTION_CONTINUE_EXECUTION code.

Next, this sample can be extended to an arbitrary number of 'missing' parameters. Take a look:

DWORD ParseException(EXCEPTION_POINTERS* pException)
{
    switch (pException->ExceptionRecord->ExceptionCode)
    {
    case EXCEPTION_FETCHPARAM_B:
        ASSERT(1 == pException->ExceptionRecord->NumberParameters);
        ASSERT(pException->ExceptionRecord->ExceptionInformation[0]);
        *((int*) pException->ExceptionRecord->ExceptionInformation[0]) = 14;
        return EXCEPTION_CONTINUE_EXECUTION;

    case EXCEPTION_FETCHPARAM_C:
        ASSERT(1 == pException->ExceptionRecord->NumberParameters);
        ASSERT(pException->ExceptionRecord->ExceptionInformation[0]);
        *((int*) pException->ExceptionRecord->ExceptionInformation[0]) = 16;
        return EXCEPTION_CONTINUE_EXECUTION;

        // and so on. Fill the needed parameter by querying exception code.
    }
    return EXCEPTION_CONTINUE_SEARCH;
}

void Func_Top()
{
    // at this point we have all possible parameters.
    __try
    {
        Func_Intermediate(12);
    }
    __except(ParseException(GetExceptionInformation()))
    {
        // This point must not be reached,
        // since we never return EXCEPTION_EXECUTE_HANDLER
    }
}

Performance

It is believed that exception-handling mechanism is very slow and heavy. This is not accurate. It becomes really heavy only if the exception has been issued (handler returns EXCEPTION_EXECUTE_HANDLER). In such a case takes place so-called unwind operation, which is a rapid return from all nested scopes, during which also guarded sections are executed (__finally blocks). In our case, the exception is not issued. We just use SEH mechanism to communicate between some nested point and the __except block. Such a trick can be compared to a function call by its complexity.

BTW: When you call RaiseException function and your program runs under a debugger - it may write in the console something like "First-chance exception blablabla...". If you do this too frequently - the debugger can consume too much CPU for those outputs, but that doesn't mean that this method is heavy.

Where to use

This method is very powerful. It allows you to fetch parameters in almost any place in your application from up to WinMain function.

General rule: you must be absolutely sure that the call to your function will take place in the same thread you put the __try - __except block, otherwise the exception will lead to a crash.

Another precaution: some people like to dismiss all exceptions in their code, to make buggy programs look more stable. So, if one of your nested functions uses such a dirty trick - this method won't be too useful. But I personally believe that is not a correct concept.

There seems to be a bit weird thing when raising such an exception from within a window procedure, where the handler resides out of the GetMessage -> DispatchMessage loop, or the DoModal functions. It seems to work ok, but the User32 issues a page error exception (which it dismisses too).

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior)
Israel Israel
My name is Vladislav Gelfer, I was born in Kiev (former Soviet Union), since 1993 I live in Israel.
In programming I'm interested mostly in low-level, OOP design, DSP and multimedia.
Besides of the programming I like physics, math, digital photography.

Comments and Discussions

 
Generaloverhead Pin
Anton Bassov10-Jul-05 14:16
Anton Bassov10-Jul-05 14:16 
Generalhm Pin
diannik6-Jul-04 3:37
diannik6-Jul-04 3:37 
GeneralSEH and C++ exceptions Pin
Lukas Rypacek23-Jun-04 5:51
Lukas Rypacek23-Jun-04 5:51 
GeneralRe: SEH and C++ exceptions Pin
valdok23-Jun-04 9:58
valdok23-Jun-04 9:58 
GeneralRe: SEH and C++ exceptions Pin
Lukas Rypacek23-Jun-04 11:03
Lukas Rypacek23-Jun-04 11:03 
GeneralRe: SEH and C++ exceptions Pin
valdok23-Jun-04 12:17
valdok23-Jun-04 12:17 
GeneralRe: SEH and C++ exceptions Pin
Lukas Rypacek23-Jun-04 13:07
Lukas Rypacek23-Jun-04 13:07 
GeneralThis is REVOLUTIONARY thinking!! Pin
WREY20-Jun-04 8:27
WREY20-Jun-04 8:27 
GeneralRe: This is REVOLUTIONARY thinking!! Pin
Tim Smith20-Jun-04 14:36
Tim Smith20-Jun-04 14:36 
GeneralRe: This is REVOLUTIONARY thinking!! Pin
WREY20-Jun-04 21:20
WREY20-Jun-04 21:20 
The author of this article certainly doesn't get a "free pass" from you (not that I believe he was looking for any, or even needs one), and is not something over which I believe he'll lose any sleep.

The same line of reasoning you use to state your case, could also be used to negate it. And while I would not mix 'apples' with 'oranges' (to use the proverbial phrase) in bunching genuine revolutionary ideas with crackpot ones, distinction needs to be made to understand when dealing with one from the other.

I use history, and reference history in making my point, because it is the greatest teacher mankind has, but like most everything else, it is also subject to distortion and misinterpretation (sometimes even up to today) by those with self-serving agendas. It is mostly to that end, that the author of the book you quoted, sought to expose (differentiate) sound scientific pursuits from misguided ones. So while science itself provides certain quantitative means by which unsound scientific claims can be exposed before being labelled disingenuous, let's not forget that some scienctific ideas (themselves) when first introduced, were classified as groundless (and I don't have to go very far to remember the reception Quantum Mechanics received when it first surfaced).

Without the rigors of scrutiny, the use of logic that "Great ideas are often vilified. This is an idea being vilified. Therefore this must be a great idea," may indeed have purpose and cause for use if the idea has NOT been proven that it lacks soundness ("staying power"), workability (does it do what it claims to do), and benefit. Devoid of these checks, to quote it in the face of someone's work is reminiscent of the Dark Age and the time of The Inquisition, popularly used by skeptics, diehard disbelivers, and those in fear of upsetting the status quo because of self-serving agendas (Galileo comes to mind as one who was at the receiving end from people like those).

It is pretty much a foregone conclusion that revolutionary thinkers don't get the recognition of their outstanding work until they've been either verbially scourged, ostracized, or dead (sometimes all three).

Hmmm | :|

William

Fortes in fide et opere!
GeneralRe: This is REVOLUTIONARY thinking!! Pin
Tim Smith21-Jun-04 4:21
Tim Smith21-Jun-04 4:21 
GeneralRe: This is REVOLUTIONARY thinking!! Pin
WREY21-Jun-04 6:35
WREY21-Jun-04 6:35 
GeneralRe: This is REVOLUTIONARY thinking!! Pin
valdok21-Jun-04 2:55
valdok21-Jun-04 2:55 
GeneralAnswers Pin
valdok19-Jun-04 7:13
valdok19-Jun-04 7:13 
GeneralRe: Answers Pin
valdok19-Jun-04 7:21
valdok19-Jun-04 7:21 
GeneralRe: Answers Pin
Johan Rosengren19-Jun-04 8:06
Johan Rosengren19-Jun-04 8:06 
GeneralYou have got to be kidding Pin
Tim Smith20-Jun-04 4:57
Tim Smith20-Jun-04 4:57 
GeneralRe: You have got to be kidding Pin
valdok20-Jun-04 5:56
valdok20-Jun-04 5:56 
GeneralRe: You have got to be kidding Pin
Tim Smith20-Jun-04 15:22
Tim Smith20-Jun-04 15:22 
GeneralSome questions. Pin
WREY19-Jun-04 6:30
WREY19-Jun-04 6:30 
GeneralRe: Some questions. Pin
Jörgen Sigvardsson19-Jun-04 6:37
Jörgen Sigvardsson19-Jun-04 6:37 
GeneralRe: Some questions. Pin
WREY19-Jun-04 6:56
WREY19-Jun-04 6:56 
GeneralGawd Pin
Johan Rosengren19-Jun-04 5:17
Johan Rosengren19-Jun-04 5:17 
GeneralRe: Gawd Pin
Jörgen Sigvardsson19-Jun-04 6:30
Jörgen Sigvardsson19-Jun-04 6:30 
GeneralRe: Gawd Pin
WREY19-Jun-04 6:34
WREY19-Jun-04 6:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.