¿Por qué el apilamiento no funciona correctamente cuando se usa SetUnhandledExceptionFilter?

Estoy usando el siguiente código para recorrer la pila en una excepción (Nota: debe ejecutarlo en versión para recibir correctamente la salida deseada del seguimiento de la pila a la consola, no en modo de depuración o de lo contrario solo mostrará una ventana emergente):

#include "stdafx.h"
#include <process.h>
#include <iostream>
#include <Windows.h>
#include "dbghelp.h"

using namespace std;

#define TRACE_MAX_FUNCTION_NAME_LENGTH 1024
#define TRACE_LOG_ERRORS FALSE
#define TRACE_DUMP_NAME L"Exception.dmp"

void function2()
{
    int a = 0;
    int b = 0;
    throw new exception;
}

void function1()
{
    int a = 0;
    function2();
}

void function0()
{
    function1();
}

static void threadFunction(void *param)
{
    function0();
}

LONG WINAPI UnhandledExceptionFilter(PEXCEPTION_POINTERS exception)
{
    CONTEXT context = *(exception->ContextRecord);
    HANDLE thread = GetCurrentThread();
    HANDLE process = GetCurrentProcess();
    STACKFRAME64 frame;
    memset(&frame, 0, sizeof(STACKFRAME64));
    DWORD image;
#ifdef _M_IX86
    image = IMAGE_FILE_MACHINE_I386;
    frame.AddrPC.Offset = context.Eip;
    frame.AddrPC.Mode = AddrModeFlat;
    frame.AddrFrame.Offset = context.Ebp;
    frame.AddrFrame.Mode = AddrModeFlat;
    frame.AddrStack.Offset = context.Esp;
    frame.AddrStack.Mode = AddrModeFlat;
#elif _M_X64
    image = IMAGE_FILE_MACHINE_AMD64;
    frame.AddrPC.Offset = context.Rip;
    frame.AddrPC.Mode = AddrModeFlat;
    frame.AddrFrame.Offset = context.Rbp;
    frame.AddrFrame.Mode = AddrModeFlat;
    frame.AddrStack.Offset = context.Rsp;
    frame.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64
    image = IMAGE_FILE_MACHINE_IA64;
    frame.AddrPC.Offset = context.StIIP;
    frame.AddrPC.Mode = AddrModeFlat;
    frame.AddrFrame.Offset = context.IntSp;
    frame.AddrFrame.Mode = AddrModeFlat;
    frame.AddrBStore.Offset = context.RsBSP;
    frame.AddrBStore.Mode = AddrModeFlat;
    frame.AddrStack.Offset = context.IntSp;
    frame.AddrStack.Mode = AddrModeFlat;
#else
#error "This platform is not supported."
#endif
    SYMBOL_INFO *symbol = (SYMBOL_INFO *)malloc(sizeof(SYMBOL_INFO)+(TRACE_MAX_FUNCTION_NAME_LENGTH - 1) * sizeof(TCHAR));
    symbol->MaxNameLen = TRACE_MAX_FUNCTION_NAME_LENGTH;
    symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
    IMAGEHLP_LINE64 *line = (IMAGEHLP_LINE64 *)malloc(sizeof(IMAGEHLP_LINE64));
    line->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
    DWORD displacement;
    SymInitialize(process, NULL, TRUE);
    while (StackWalk(image, process, thread, &frame, &context, NULL, NULL, NULL, NULL))
    {
        if (SymFromAddr(process, frame.AddrPC.Offset, NULL, symbol))
        {
            if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &displacement, line))
            {
                printf("\tat %s in %s: line: %lu: address: 0x%0X\n", symbol->Name, line->FileName, line->LineNumber, symbol->Address);
            }
            else if (TRACE_LOG_ERRORS)
            {
                printf("Error from SymGetLineFromAddr64: %lu.\n", GetLastError());
            }
        }
        else if (TRACE_LOG_ERRORS)
        {
            printf("Error from SymFromAddr: %lu.\n", GetLastError());
        }
    }
    DWORD error = GetLastError();
    if (error && TRACE_LOG_ERRORS)
    {
        printf("Error from StackWalk64: %lu.\n", error);
    }
    HANDLE dumpFile = CreateFile(TRACE_DUMP_NAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    MINIDUMP_EXCEPTION_INFORMATION exceptionInformation;
    exceptionInformation.ThreadId = GetCurrentThreadId();
    exceptionInformation.ExceptionPointers = exception;
    exceptionInformation.ClientPointers = FALSE;
    if (MiniDumpWriteDump(process, GetProcessId(process), dumpFile, MiniDumpNormal, exception ? &exceptionInformation : NULL, NULL, NULL))
    {
        printf("Wrote a dump.");
    }
    return EXCEPTION_CONTINUE_SEARCH;
}

int _tmain(int argc, _TCHAR* argv[])
{
    SetUnhandledExceptionFilter(UnhandledExceptionFilter);
    _beginthread(threadFunction, 0, NULL);
    printf("Press any key to exit.\n");
    cin.get();
    return 0;
}

Salida:

Press any key to exit.
        at threadFunction in c:\users\<youruseraccount>\documents\visual studio 2013\project
s\stacktracing\stacktracing\stacktracing.cpp: line: 135: address: 0x498B12D0
Wrote a dump.

El problema es que, la traza anterior contiene sololine: 135, que corresponde a la llamada a la llamada afunction0(); enthreadFunction. Sin embargo, me gustaría que incluyera, como parte del seguimiento de la pila,line: 29, donde hace unthrow new exception;. ¿Por qué no incluye esto como parte del seguimiento de la pila? ¿Cómo puedo hacer que también incluya esta parte del seguimiento de la pila?La única forma en que he podido lograr esta funcionalidad hasta ahora es usar__try y__except(FatalExceptionFilter(GetExceptionCode(), GetExceptionInformation())) bloques alrededor de la llamada afunction0(); y pasar el excepto a unFatalExceptionFilter, pero esto no es bueno porque tiene sus propias advertencias, ya que tendría que usarse en todas partes, y quiero una solución de nivel superior. Quiero atrapar todas las excepciones de nivel superior, y quiero saber exactamente dónde fueron lanzadas.

PD Este código se ejecuta en una máquina con Windows 8.1 de 64 bits. Es una aplicación de consola MSVC ++ compilada bajo una versión de lanzamiento / plataforma x64.

Actualizar: He intentado lo siguiente usando el método _set_se_translator y las sugerencias de Petr, pero todavía no parece querer funcionar. De hecho, la excepción dividir por cero se genera sin control y nada lo maneja:

#include "stdafx.h"
#include <process.h>
#include <iostream>
#include <Windows.h>
#include "dbghelp.h"

using namespace std;

#define TRACE_MAX_FUNCTION_NAME_LENGTH 1024
#define TRACE_LOG_ERRORS FALSE
#define TRACE_DUMP_NAME L"Exception.dmp"

void function2()
{
    int a = 0;
    int b = 0;
    // The loop below should throw an unhandled exception.
    for (int *i = 0; *i < 100; i++)
    {
        *i = 10000;
    }
}

void function1()
{
    int a = 0;
    function2();
}

void function0()
{
    function1();
}

void ShowStackTrace(EXCEPTION_POINTERS* exception)
{
    CONTEXT context = *(exception->ContextRecord);
    HANDLE thread = GetCurrentThread();
    HANDLE process = GetCurrentProcess();
    STACKFRAME64 frame;
    memset(&frame, 0, sizeof(STACKFRAME64));
    DWORD image;
#ifdef _M_IX86
    image = IMAGE_FILE_MACHINE_I386;
    frame.AddrPC.Offset = context.Eip;
    frame.AddrPC.Mode = AddrModeFlat;
    frame.AddrFrame.Offset = context.Ebp;
    frame.AddrFrame.Mode = AddrModeFlat;
    frame.AddrStack.Offset = context.Esp;
    frame.AddrStack.Mode = AddrModeFlat;
#elif _M_X64
    image = IMAGE_FILE_MACHINE_AMD64;
    frame.AddrPC.Offset = context.Rip;
    frame.AddrPC.Mode = AddrModeFlat;
    frame.AddrFrame.Offset = context.Rbp;
    frame.AddrFrame.Mode = AddrModeFlat;
    frame.AddrStack.Offset = context.Rsp;
    frame.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64
    image = IMAGE_FILE_MACHINE_IA64;
    frame.AddrPC.Offset = context.StIIP;
    frame.AddrPC.Mode = AddrModeFlat;
    frame.AddrFrame.Offset = context.IntSp;
    frame.AddrFrame.Mode = AddrModeFlat;
    frame.AddrBStore.Offset = context.RsBSP;
    frame.AddrBStore.Mode = AddrModeFlat;
    frame.AddrStack.Offset = context.IntSp;
    frame.AddrStack.Mode = AddrModeFlat;
#else
#error "This platform is not supported."
#endif
    SYMBOL_INFO *symbol = (SYMBOL_INFO *)malloc(sizeof(SYMBOL_INFO)+(TRACE_MAX_FUNCTION_NAME_LENGTH - 1) * sizeof(TCHAR));
    symbol->MaxNameLen = TRACE_MAX_FUNCTION_NAME_LENGTH;
    symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
    IMAGEHLP_LINE64 *line = (IMAGEHLP_LINE64 *)malloc(sizeof(IMAGEHLP_LINE64));
    line->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
    DWORD displacement;
    SymInitialize(process, NULL, TRUE);
    while (StackWalk(image, process, thread, &frame, &context, NULL, NULL, NULL, NULL))
    {
        if (SymFromAddr(process, frame.AddrPC.Offset, NULL, symbol))
        {
            if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &displacement, line))
            {
                printf("\tat %s in %s: line: %lu: address: 0x%0X\n", symbol->Name, line->FileName, line->LineNumber, symbol->Address);
            }
            else if (TRACE_LOG_ERRORS)
            {
                printf("Error from SymGetLineFromAddr64: %lu.\n", GetLastError());
            }
        }
        else if (TRACE_LOG_ERRORS)
        {
            printf("Error from SymFromAddr: %lu.\n", GetLastError());
        }
    }
    DWORD error = GetLastError();
    if (error && TRACE_LOG_ERRORS)
    {
        printf("Error from StackWalk64: %lu.\n", error);
    }
    HANDLE dumpFile = CreateFile(TRACE_DUMP_NAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    MINIDUMP_EXCEPTION_INFORMATION exceptionInformation;
    exceptionInformation.ThreadId = GetCurrentThreadId();
    exceptionInformation.ExceptionPointers = exception;
    exceptionInformation.ClientPointers = FALSE;
    if (MiniDumpWriteDump(process, GetProcessId(process), dumpFile, MiniDumpNormal, exception ? &exceptionInformation : NULL, NULL, NULL))
    {
        printf("Wrote a dump.");
    }
}

void ShowStackTrace(CONTEXT *aContext)
{
    CONTEXT context = *aContext;
    HANDLE thread = GetCurrentThread();
    HANDLE process = GetCurrentProcess();
    STACKFRAME64 frame;
    memset(&frame, 0, sizeof(STACKFRAME64));
    DWORD image;
#ifdef _M_IX86
    image = IMAGE_FILE_MACHINE_I386;
    frame.AddrPC.Offset = context.Eip;
    frame.AddrPC.Mode = AddrModeFlat;
    frame.AddrFrame.Offset = context.Ebp;
    frame.AddrFrame.Mode = AddrModeFlat;
    frame.AddrStack.Offset = context.Esp;
    frame.AddrStack.Mode = AddrModeFlat;
#elif _M_X64
    image = IMAGE_FILE_MACHINE_AMD64;
    frame.AddrPC.Offset = context.Rip;
    frame.AddrPC.Mode = AddrModeFlat;
    frame.AddrFrame.Offset = context.Rbp;
    frame.AddrFrame.Mode = AddrModeFlat;
    frame.AddrStack.Offset = context.Rsp;
    frame.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64
    image = IMAGE_FILE_MACHINE_IA64;
    frame.AddrPC.Offset = context.StIIP;
    frame.AddrPC.Mode = AddrModeFlat;
    frame.AddrFrame.Offset = context.IntSp;
    frame.AddrFrame.Mode = AddrModeFlat;
    frame.AddrBStore.Offset = context.RsBSP;
    frame.AddrBStore.Mode = AddrModeFlat;
    frame.AddrStack.Offset = context.IntSp;
    frame.AddrStack.Mode = AddrModeFlat;
#else
#error "This platform is not supported."
#endif
    SYMBOL_INFO *symbol = (SYMBOL_INFO *)malloc(sizeof(SYMBOL_INFO)+(TRACE_MAX_FUNCTION_NAME_LENGTH - 1) * sizeof(TCHAR));
    symbol->MaxNameLen = TRACE_MAX_FUNCTION_NAME_LENGTH;
    symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
    IMAGEHLP_LINE64 *line = (IMAGEHLP_LINE64 *)malloc(sizeof(IMAGEHLP_LINE64));
    line->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
    DWORD displacement;
    SymInitialize(process, NULL, TRUE);
    while (StackWalk(image, process, thread, &frame, &context, NULL, NULL, NULL, NULL))
    {
        if (SymFromAddr(process, frame.AddrPC.Offset, NULL, symbol))
        {
            if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &displacement, line))
            {
                printf("\tat %s in %s: line: %lu: address: 0x%0X\n", symbol->Name, line->FileName, line->LineNumber, symbol->Address);
            }
            else if (TRACE_LOG_ERRORS)
            {
                printf("Error from SymGetLineFromAddr64: %lu.\n", GetLastError());
            }
        }
        else if (TRACE_LOG_ERRORS)
        {
            printf("Error from SymFromAddr: %lu.\n", GetLastError());
        }
    }
    DWORD error = GetLastError();
    if (error && TRACE_LOG_ERRORS)
    {
        printf("Error from StackWalk64: %lu.\n", error);
    }
    HANDLE dumpFile = CreateFile(TRACE_DUMP_NAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (MiniDumpWriteDump(process, GetProcessId(process), dumpFile, MiniDumpNormal, NULL, NULL, NULL))
    {
        printf("Wrote a dump.");
    }
}

class CustomException {
public:
    CustomException(EXCEPTION_POINTERS *exception = nullptr) 
    {
        CONTEXT context;
        ZeroMemory(&context, sizeof(CONTEXT));
        if (exception)
        {
            // In case of an SEH exception.
            ShowStackTrace(exception);
        }
        else
        {
            // In case of a C++ exception.
            RtlCaptureContext(&context);
            ShowStackTrace(&context);
        }
    }
};

void SEHExceptionTranslator(unsigned int, EXCEPTION_POINTERS *exception){
    throw CustomException(exception);
}

static void threadFunction(void *param)
{
    _set_se_translator(SEHExceptionTranslator);
    function0();
}

LONG WINAPI UnhandledExceptionFilter(PEXCEPTION_POINTERS exception)
{
    CONTEXT context = *(exception->ContextRecord);
    HANDLE thread = GetCurrentThread();
    HANDLE process = GetCurrentProcess();
    STACKFRAME64 frame;
    memset(&frame, 0, sizeof(STACKFRAME64));
    DWORD image;
#ifdef _M_IX86
    image = IMAGE_FILE_MACHINE_I386;
    frame.AddrPC.Offset = context.Eip;
    frame.AddrPC.Mode = AddrModeFlat;
    frame.AddrFrame.Offset = context.Ebp;
    frame.AddrFrame.Mode = AddrModeFlat;
    frame.AddrStack.Offset = context.Esp;
    frame.AddrStack.Mode = AddrModeFlat;
#elif _M_X64
    image = IMAGE_FILE_MACHINE_AMD64;
    frame.AddrPC.Offset = context.Rip;
    frame.AddrPC.Mode = AddrModeFlat;
    frame.AddrFrame.Offset = context.Rbp;
    frame.AddrFrame.Mode = AddrModeFlat;
    frame.AddrStack.Offset = context.Rsp;
    frame.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64
    image = IMAGE_FILE_MACHINE_IA64;
    frame.AddrPC.Offset = context.StIIP;
    frame.AddrPC.Mode = AddrModeFlat;
    frame.AddrFrame.Offset = context.IntSp;
    frame.AddrFrame.Mode = AddrModeFlat;
    frame.AddrBStore.Offset = context.RsBSP;
    frame.AddrBStore.Mode = AddrModeFlat;
    frame.AddrStack.Offset = context.IntSp;
    frame.AddrStack.Mode = AddrModeFlat;
#else
#error "This platform is not supported."
#endif
    SYMBOL_INFO *symbol = (SYMBOL_INFO *)malloc(sizeof(SYMBOL_INFO)+(TRACE_MAX_FUNCTION_NAME_LENGTH - 1) * sizeof(TCHAR));
    symbol->MaxNameLen = TRACE_MAX_FUNCTION_NAME_LENGTH;
    symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
    IMAGEHLP_LINE64 *line = (IMAGEHLP_LINE64 *)malloc(sizeof(IMAGEHLP_LINE64));
    line->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
    DWORD displacement;
    while (StackWalk(image, process, thread, &frame, &context, NULL, NULL, NULL, NULL))
    {
        if (SymFromAddr(process, frame.AddrPC.Offset, NULL, symbol))
        {
            if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &displacement, line))
            {
                printf("\tat %s in %s: line: %lu: address: 0x%0X\n", symbol->Name, line->FileName, line->LineNumber, symbol->Address);
            }
            else if (TRACE_LOG_ERRORS)
            {
                printf("Error from SymGetLineFromAddr64: %lu.\n", GetLastError());
            }
        }
        else if (TRACE_LOG_ERRORS)
        {
            printf("Error from SymFromAddr: %lu.\n", GetLastError());
        }
    }
    DWORD error = GetLastError();
    if (error && TRACE_LOG_ERRORS)
    {
        printf("Error from StackWalk64: %lu.\n", error);
    }
    HANDLE dumpFile = CreateFile(TRACE_DUMP_NAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    MINIDUMP_EXCEPTION_INFORMATION exceptionInformation;
    exceptionInformation.ThreadId = GetCurrentThreadId();
    exceptionInformation.ExceptionPointers = exception;
    exceptionInformation.ClientPointers = FALSE;
    if (MiniDumpWriteDump(process, GetProcessId(process), dumpFile, MiniDumpNormal, exception ? &exceptionInformation : NULL, NULL, NULL))
    {
        printf("Wrote a dump.");
    }
    return EXCEPTION_CONTINUE_SEARCH;
}

int _tmain(int argc, _TCHAR* argv[])
{
    SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS);
    SymInitialize(GetCurrentProcess(), NULL, TRUE);
    _set_se_translator(SEHExceptionTranslator);
    SetUnhandledExceptionFilter(UnhandledExceptionFilter);
    _beginthread(threadFunction, 0, NULL);
    printf("Press any key to exit.\n");
    cin.get();
    SymCleanup(GetCurrentProcess());
    return 0;
}

Respuestas a la pregunta(2)

Su respuesta a la pregunta