All Your Base Are Belong To Us

Mostly .NET internals and other kinds of gory details

Converting Win32 and C++ Exceptions to Managed Exceptions

In a layered application consisting of managed upper layers and unmanaged lower implementation layers, you might frequently encounter the need to call unmanaged code, handle any exceptional conditions and reflect them to the upper layers as managed exceptions.

This can be done manually, but fortunately as of Windows XP there is an approach that does not require wrapping every unmanaged call in an exception handler.  This approach is vectored exception handling.

A vectored exception handler is stackless – it can be attached to an application and get a chance to process any exception before stack-based exception handlers process it.  If you want to convert Win32 or C++ exceptions to managed exceptions, a vectored exception handler can do it for you.  (For more background on Win32 SEH and vectored exception handling, I strongly recommend Matt Pietrek’s articles: ‘97 MSJ on SEH and ‘01 MSDN on vectored exception handlers.)

A vectored exception handler, like any other exception handler, takes an EXCEPTION_POINTERS structure which contains an EXCEPTION_RECORD.  Inside the record is the Win32 exception code and additional exception information.  For example, C++ exceptions have the magic 0xE06D7363 Win32 exception code, and the exception object is passed by pointer in the second element of the EXCEPTION_RECORD.ExceptionInformation array.

Finally, we will have to ensure that our vectored exception handler does not process CLR exceptions – or we wind up in an infinite loop.  This is easily done because all CLR exceptions have the same Win32 exception code – 0xE0434F4D.

Some kind of policy must be in place with regard to what exceptions are handled and how Win32 exceptions are converted to managed exceptions.  As part of our policy, we should probably decide on a base class for all C++ exceptions propagated from our unmanaged code (e.g., std::exception).

An exception converter that would do the job for now can be implemented like this:

ref class ExceptionConverter {

public:

    static Exception^ GetException(

        unsigned int ExceptionCode,

        void* pExceptionObject) {

 

        //More complicated logic can be added here

 

        if (pExceptionObject == NULL)

            return gcnew Win32Exception(ExceptionCode);

 

        std::exception& rException =

            *(std::exception*)pExceptionObject;

        return gcnew ApplicationException(

            gcnew String(rException.what()));

    }

};

With this policy in place, we can register the following exception handler to do the conversion:

#define CLR_EXCEPTION_CODE 0xE0434F4D

#define CPP_EXCEPTION_CODE 0xE06D7363

 

LONG ConvertToManagedExceptionIfNeeded(

        PEXCEPTION_POINTERS ExceptionInfo) {

 

    void* pExceptionObject = NULL;

 

    PEXCEPTION_RECORD ExceptionRecord =

        ExceptionInfo->ExceptionRecord;

    switch (ExceptionRecord->ExceptionCode){

        case CLR_EXCEPTION_CODE:

            //Don't touch CLR exceptions.

            return EXCEPTION_EXECUTE_HANDLER;

        case CPP_EXCEPTION_CODE:

            pExceptionObject = (void*)

                ExceptionRecord->ExceptionInformation[1];

        default:

            break;

    }

 

    Exception^ e = ExceptionConverter::GetException(

        ExceptionRecord->ExceptionCode, pExceptionObject);

    throw e;

}

 

#pragma unmanaged

 

LONG CALLBACK ConverterVectoredExceptionHandler(

    PEXCEPTION_POINTERS ExceptionInfo){

 

    return ConvertToManagedExceptionIfNeeded(ExceptionInfo);

}

Note that it’s comprised of an unmanaged handler (which we need to register using AddVectoredExceptionHandler) and a managed method that does the actual conversion.

With this in place, we can use the following test method to see our converter in place for handling Win32 exceptions (such as an access violation) and C++ exceptions (std::exception in this case):

#define INSTALL_HANDLER_FIRST 1

#define INSTALL_HANDLER_LAST 0

 

int main()

{

    AddVectoredExceptionHandler(

        INSTALL_HANDLER_FIRST,

        ConverterVectoredExceptionHandler);

 

    try{

        //Trigger an AV:

        AccessViolation();

 

        //Raise a Win32 exception:

        //RaiseException(15, 0, 0, NULL);

    }

    catch (System::Exception^ e){

        Console::WriteLine(

            "Got managed exception: {0}",

            e->Message);

    }

    catch (...){

        Console::WriteLine("unexpected exception");

    }

 

    try{

        //Throw a C++ exception:

        throw std::exception("This is a C++ exception.");

    }

    catch (System::Exception^ e){

        Console::WriteLine(

            "Got managed exception: {0}",

            e->Message);

    }

    catch (...){

        Console::WriteLine("unexpected exception");

    }

 

    return 0;

}

There is one thing to look out for.  If the unmanaged C++ code is rich with exceptions being thrown and handled, then this handler is inappropriate – it will catch all such exceptions and immediately convert them to managed ones.  Therefore, we need some way to differentiate exceptions that need to be converted and propagated from exceptions that are handled entirely in the unmanaged lower layers.

This can be done with a custom exception base class, some sort of flag, or even (hack!) by inspecting EXCEPTION_RECORD.ExceptionAddress to see where the exception occurred in code.

Thanks to the reader Brian Romanko for facilitating the discussion which inspired this post.

Comments

Visual Studio Hacks said:

My latest in a series of the weekly, or more often, summary of interesting links I come across related to Visual Studio. The Web Developer Tools Team announced the release of the Dynamic Data Wizard Preview 0806 for VS 2008 SP1 . US ISV Developer Evangelism

# August 21, 2008 10:32 AM

All Your Base Are Belong To Us said:

Shortly after publishing my last post on converting native (C++ or Win32) exceptions to managed exceptions

# August 24, 2008 11:28 AM

Recent Faves Tagged With "exceptions" : MyNetFaves said:

Pingback from  Recent Faves Tagged With "exceptions" : MyNetFaves

# April 23, 2009 11:35 AM
Leave a Comment

(required) 

(required) 

(optional)

(required) 


Enter the numbers above: