4

How can I get information about an unknown exception, like in the code below?

void foo()
{
  throw 'A';
}

...
try
{
  foo();
}
catch (const std::exception& exc)
{
  std::cout << exc.what();
}
catch (long e)
{
  std::cout << e;
}
catch (...)
{
  std::cout << "???\n";  // code ends up here
}
...

The code ends up at this line, which is expected:

std::cout << "???\n";

As far as I know, there is not much I can do to retrieve whatever exception information in the catch(...) block.

But, is there maybe some Microsoft-specific C++ extensions that allows to do so?

Environment:

  • Windows 10
  • Windows Desktop application using MFC
10
  • 2
    A bad way of getting some information would be to remove the catch(...) clause, the program will crash and stderr will show something like terminate called after throwing and instance of 'blah' but yeah, relying of a crash to get some information is not what I would call a solution... Commented Jun 24 at 14:19
  • 5
    Related (in case it helps): stackoverflow.com/questions/4885334/… Commented Jun 24 at 14:21
  • 2
    Also debugging can help (so you can at least see the callstack), what you should also do is ensure that you don't handle structured exceptions (memory access violations etc.. they should lead to hard crashes). Another option is to not catch with (...) at all and force a crashdump and analyze that. Commented Jun 24 at 14:37
  • Another side note, I see now why you would want to use catch(...) letting exceptions "escape" from functions called by MFC (windows message loop) is also a sure way to crash. Commented Jun 24 at 14:39
  • 1
    stackoverflow.com/questions/39113168/… Commented Jun 25 at 10:34

2 Answers 2

2
#include <ehdata.h>

EXTERN_C 
_CRTIMP
PSTR 
__CRTDECL 
__unDNameEx(
            _Out_ PSTR buffer, 
            _In_ PCSTR mangled, 
            _In_ DWORD cb,
            _In_ void* (__cdecl* memget)(DWORD),
            _In_ void (__cdecl* memfree)(void*),
            _In_ PCSTR (__cdecl* GetParameter)(long i),
            _In_ DWORD flags
            );

static PCSTR __cdecl GetParameter(long /*i*/)
{
    return "";
}

static void* __cdecl fAlloc(ULONG cb)
{
    return LocalAlloc(LMEM_FIXED, (ULONG)cb);
}

static void __cdecl fFree(void* pv)
{
    LocalFree(pv);
}

void PrintName(PCSTR Name)
{
    if ('.' == *Name++)
    {
        PSTR buf = 0, name = 0;

        int len = 0;

        while (0 < (len = _snprintf(name, len, "?_@@YAX%s@Z", Name)))
        {
            if (name)
            {
                len += 0x20;
                len <<= 1;
                if (buf == __unDNameEx(buf = (PSTR)alloca(len), name, len, fAlloc, fFree, GetParameter, UNDNAME_DEFAULT))
                {
                    if (buf = strchr(buf, '('))
                    {
                        buf++;
                        name = buf + strlen(buf)-1;
                        if (')' == *name)
                        {
                            *name = 0;
                            DbgPrint("\tcatch (%hs ) { /***/ }\n", buf);

                            return ;
                        }

                    }
                }
                break;
            }

            name = (PSTR)alloca(++len);
        }
    }

    DbgPrint("\tcatch (%hs ) { ... }\n", Name - 1);
}

EXTERN_C
_CRTIMP
void 
__CRTDECL
__DestructExceptionObject(PEXCEPTION_RECORD ExceptionRecord, BOOLEAN bDestruct);

NTSTATUS seh(PEXCEPTION_RECORD ExceptionRecord)
{
    DbgPrint("%x [%x] at %p\n", ExceptionRecord->ExceptionCode, 
        ExceptionRecord->ExceptionFlags, ExceptionRecord->ExceptionAddress);

    if (ULONG NumberParameters = ExceptionRecord->NumberParameters)
    {
        DbgPrint("%u params: [ ", NumberParameters);
        PULONG_PTR ExceptionInformation = ExceptionRecord->ExceptionInformation;
        do 
        {
            DbgPrint("%p, ", *ExceptionInformation++);
        } while (--NumberParameters);
        DbgPrint("]\n");
    }

    switch (ExceptionRecord->ExceptionCode)
    {
    case EH_EXCEPTION_NUMBER:
        if (!(EXCEPTION_SOFTWARE_ORIGINATE & ExceptionRecord->ExceptionFlags))
        {
            __debugbreak();
        }

        if (EH_EXCEPTION_PARAMETERS <= ExceptionRecord->NumberParameters)
        {
            void **pExceptionObject = reinterpret_cast<void **>(ExceptionRecord->ExceptionInformation[1]);
            ThrowInfo *pTI = reinterpret_cast<ThrowInfo *>(ExceptionRecord->ExceptionInformation[2]);
            ULONG_PTR ImageBase = ExceptionRecord->ExceptionInformation[3];

            DbgPrint("c++[%x]: pExceptionObject=%p\n", ExceptionRecord->ExceptionInformation[0], *pExceptionObject);

            switch (ExceptionRecord->ExceptionInformation[0])
            {
            case EH_MAGIC_NUMBER1:
            case EH_MAGIC_NUMBER2:
            case EH_MAGIC_NUMBER3:
                CatchableTypeArray* p = THROW_CTARRAY_IB(*pTI,ImageBase);
                if (ULONG nCatchableTypes = p->nCatchableTypes)
                {
                    const int* arrayOfCatchableTypes = p->arrayOfCatchableTypes;
                    do 
                    {
                        CatchableType* pt = (CatchableType*)RtlOffsetToPointer(ImageBase, *arrayOfCatchableTypes++);
                        TypeDescriptor* pType = (TypeDescriptor*)RtlOffsetToPointer(ImageBase, pt->pType);

                        PrintName(pType->name);

                    } while (--nCatchableTypes);
                }
                break;
            }

            //  exported by ucrtbase but included to public ucrt.lib
            // __DestructExceptionObject(ExceptionRecord, TRUE);// optional call

            if (PMFN pmfnUnwind = pTI->pmfnUnwind)
            {
                reinterpret_cast<void (__cdecl * )(void*)>(RtlOffsetToPointer(ImageBase, pmfnUnwind))(*pExceptionObject);
            }
        }
        return EXCEPTION_EXECUTE_HANDLER;
    }

    return EXCEPTION_CONTINUE_SEARCH;
}
void Handler_CPP()
{
    try
    {
        foo();
    }
    catch (const std::exception& exc)
    {
        std::cout << exc.what();
    }
    catch (long e)
    {
        std::cout << e;
    }
}

void Handler_C()
{
    __try {
        Handler_CPP();
    } __except(seh(GetExceptionInformation()->ExceptionRecord)){

        __nop();
    }
}

(this code is designed for x64, where _EH_RELATIVE_TYPEINFO is true, however possible here use macros from ehdata.h, like THROW_CTARRAY_IB

Sign up to request clarification or add additional context in comments.

5 Comments

I need to install the WDK first, I'll check this later.
no, really wdk not need. i use some api from here, but it not mandatory. DbgPrint ? but you can replace it to any another. RtlOffsetToPointer - this is macro only - you can define it itself or use equ. by fact here no any dependecies from WDK, i use some, only because for me this more easy
__unDNameEx from msvcrt (i use special lib for it but possible simply use getprocaddress) etc
Almost could compile it, there is one symbol _ThrowInfo missing.
in my code no _ThrowInfo symbol. i use only ThrowInfo it defined in ehdata_forceinclude.h as typedef for _s_ThrowInfo . you need look on your files, macros, etc. this already not my code, but vs standard header files. probably BUILDING_C1XX_FORCEINCLUDE defined in your code and not defined in my
-2

Note that MFC has its own way to handle exceptions :

https://learn.microsoft.com/en-us/cpp/mfc/reference/exception-processing?view=msvc-170#try

May be you just experienced a conflict bewteen standard library and MFC.

Exception are tricky in Win32 so you will probably have to make some try before solving the problem.

25 Comments

MFC uses entirely regular C++ exception handling.
MS dont seem to be aware of your "fact" : "This article explains the exception-handling mechanisms available in MFC. Two mechanisms are available:" Later in article and keeping in mind that Win32 use SEH which ar NOT standard C++ exceptions : " Note To enable C++ exception handling in your code, select Enable C++ Exceptions on the Code Generation page in the C/C++ folder of the project's Property Pages dialog box, or use the /EHsc compiler option." If one mix macro and/or dont use /EHsc he may experiment some issue. As I did some years ago... RTFM...
Exceptions: Changes to Exception Macros in Version 3.0: "In MFC version 3.0 and later, the exception-handling macros have been changed to use C++ exceptions." MFC 3.0 was released in September 1994. The /EH compiler switch controls the exception handling model used by the C++ compiler. It is unrelated to any given library.
Exactly As I said earlier I experimented the very situation in wich 1998 old code was to be update to VS 2017 and where TRY/CATCH were incompatible with C++ try/catch. And once again read what is written : Win32 exceptions are tricky. MFC is a wrapper around Win32. So any difficulties in MCF can turn to actually to a Win32 issue. Really dont see your point there...
"1998 old code" - That's "Visual C++5.0" (or later) then, which translates to MFC 4.21 (or later). Which is well beyond the pre-3.0 MFC days. Whatever problem you ran into, it wasn't caused by a quirk in the implementation of a version of the library you aren't using. (I can't believe I had to write this.) "[I] dont see your point" - If you are confused about the down-votes: "At a point in time, before time was time, MFC was weird" isn't a useful answer to the question: "How do I get circumstantial information out of standard C++ exceptions?"
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.