C++ 在SetUnhandledExceptionFilter的处理程序中,如何打印stacktrace?

C++ 在SetUnhandledExceptionFilter的处理程序中,如何打印stacktrace?,c++,winapi,C++,Winapi,当发生未处理的异常时,我希望打印stacktrace,而不仅仅是终止。我已尝试使用SetUnhandledExceptionFilter来完成此操作: SetUnhandledExceptionFilter(UnhandledException); ... LONG WINAPI UnhandledException(LPEXCEPTION_POINTERS exceptionInfo) { printf("An exception occurred which wasn

当发生未处理的异常时,我希望打印stacktrace,而不仅仅是终止。我已尝试使用
SetUnhandledExceptionFilter
来完成此操作:

SetUnhandledExceptionFilter(UnhandledException);
    ...
LONG WINAPI UnhandledException(LPEXCEPTION_POINTERS exceptionInfo)  
{  
    printf("An exception occurred which wasn't handled!\nCode: 0x%08X\nAddress: 0x%08X",   
        exceptionInfo->ExceptionRecord->ExceptionCode,  
        exceptionInfo->ExceptionRecord->ExceptionAddress);
    return EXCEPTION_EXECUTE_HANDLER;  
}
我发现这段代码很好用。但是,由于系统“事件查看器”中仍然打印了
ExceptionCode
ExceptionAddress
,因此没有添加信息。 是否可以打印完整的堆栈跟踪,以便确定异常发生的确切点


我发现这段代码部分解决了我的问题。仅打印发生异常的方法。但是异常类型和行号并没有打印出来。

这里是我几年前为Windows编写的一些堆栈遍历代码。以下是它产生的输出类型:

Walking stack.
0       DebugBreak
1       ThreadFunc2     e:\c\source\stackwalk2a.cpp(72)
2       ThreadFunc1     e:\c\source\stackwalk2a.cpp(79)
3       TargetThread    e:\c\source\stackwalk2a.cpp(86)
4       BaseThreadInitThunk
5       RtlUserThreadStart
End of stack walk.
缺少的主要内容是关于异常类型的任何内容。如果您谈论的是一个本机结构化/向量化异常,我非常确定它也应该是可检索的。检索C++异常类型可能会有点困难(但我不确定——可能很简单)。
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#pragma注释(lib,“psapi.lib”)
#pragma注释(lib,“dbghelp.lib”)
//imagehlp.dll的某些版本本身缺乏正确的打包指令
//所以我们必须这样做。
#pragma包(推送,在图像HLP之前,8)
#包括
#pragma包(pop,在图像HLP之前)
结构模块数据{
std::字符串图像名称;
std::字符串模块名称;
void*基址;
DWORD负载大小;
};
typedef std::向量模块列表;
准备好手柄;
boolshow_堆栈(std::ostream&,handlehthread,CONTEXT&c);
DWORD uu stdcall TargetThread(void*arg);
void ThreadFunc1();
void ThreadFunc2();
DWORD过滤器(异常指针*ep);
void*加载模块符号(HANDLE hProcess,DWORD pid);
内部主(空){
德沃德螺纹;
thread_ready=CreateEvent(NULL,false,false,NULL);
HANDLE thread=CreateThread(NULL,0,TargetThread,NULL,0,&thread\u id);
WaitForSingleObject(线程就绪,无限);
闭合手柄(螺纹准备就绪);
返回0;
}
//如果使用C++异常处理:安装翻译器函数
//使用set_se_translator()。在该函数的上下文中(但*不是*
//之后),您可以执行堆栈转储,也可以保存上下文
//记录为本地副本。请注意,您必须在
//最早的机会,以避免有趣的堆栈帧消失
//等你倒垃圾的时候。
DWORD筛选器(异常指针*ep){
手柄螺纹;
DuplicateHandle(GetCurrentProcess(),GetCurrentThread(),
GetCurrentProcess(),&线程,0,false,重复(相同访问);
std::cout ContextRecord);
标准::cout SizeOfStruct=sizeof(*sym);
sym->MaxNameLength=最大名称长度;
DWORD64位移;
if(!SymGetSymFromAddr64(进程、地址和置换,sym))
抛出(标准::逻辑错误(“坏符号”);
}
std::string name(){返回std::string(sym->name);}
std::字符串未修饰的_name(){
标准::矢量和名称(最大名称);
取消装饰符号名称(符号->名称,&und\u名称[0],最大名称,取消名称完成);
返回std::string(&und_name[0],strlen(&und_name[0]);
}
};
boolshow_堆栈(std::ostream&os、handlehthread、CONTEXT&c){
HANDLE process=GetCurrentProcess();
int frame_number=0;
从符号开始的双字偏移量=0;
IMAGEHLP_LINE64 line={0};
SymHandler(进程);
sym_选项(SYMOPT_载重线| SYMOPT_UNDDNAME);
void*base=加载模块符号(进程,GetCurrentProcessId());
STACKFRAME64 s=初始堆栈帧(c);
line.SizeOfStruct=行的大小;
图像标题*h=图像标题(基本);
DWORD image_type=h->FileHeader.Machine;
做{
if(!StackWalk64(映像类型、进程、hThread、&s、&c、NULL、SymFunctionTableAccess64、SymGetModuleBase64、NULL))
返回false;

你可以随时让自己从操作系统中得到启发,或者仅仅为了它方便的自由许可而使用它;)…我认为它正是你想要的。行号等是当你加载一个事后转储并拥有完整的PDB时附带的东西。这可能是最终用户无论如何都不会拥有的。@0xC000002L没有第三部分的解决方案很好我现在想要的是打印一个堆栈跟踪。当我移植到Linux.if时,我会把它扔掉一段时间,你甚至认为MS的LIBs是第三方的(它们是必需的,需要安装来解释这些符号,除非你阅读Sven Schreiber的无文件的Windows 2000秘密)。你运气不好。否则你仍然可以重用这些东西的一部分,只需将其链接到你的二进制文件中。BSD许可证只需要归属。当它仅用于内部使用时,你就更不必关心它了。
#include <windows.h>
#include <winnt.h>

#include <string>
#include <vector>
#include <Psapi.h>
#include <algorithm>
#include <iomanip>
#include <iostream>
#include <stdexcept>
#include <iterator>

#pragma comment(lib, "psapi.lib")
#pragma comment(lib, "dbghelp.lib")

// Some versions of imagehlp.dll lack the proper packing directives themselves
// so we need to do it.
#pragma pack( push, before_imagehlp, 8 )
#include <imagehlp.h>
#pragma pack( pop, before_imagehlp )

struct module_data {
    std::string image_name;
    std::string module_name;
    void *base_address;
    DWORD load_size;
};
typedef std::vector<module_data> ModuleList;

HANDLE thread_ready;

bool show_stack(std::ostream &, HANDLE hThread, CONTEXT& c);
DWORD __stdcall TargetThread( void *arg );
void ThreadFunc1();
void ThreadFunc2();
DWORD Filter( EXCEPTION_POINTERS *ep );
void *load_modules_symbols( HANDLE hProcess, DWORD pid );

int main( void ) {
    DWORD thread_id;

    thread_ready = CreateEvent( NULL, false, false, NULL );

    HANDLE thread = CreateThread( NULL, 0, TargetThread, NULL, 0, &thread_id );

    WaitForSingleObject( thread_ready, INFINITE );
    CloseHandle(thread_ready);
    return 0;
}

// if you use C++ exception handling: install a translator function
// with set_se_translator(). In the context of that function (but *not*
// afterwards), you can either do your stack dump, or save the CONTEXT
// record as a local copy. Note that you must do the stack dump at the
// earliest opportunity, to avoid the interesting stack-frames being gone
// by the time you do the dump.
DWORD Filter(EXCEPTION_POINTERS *ep) {
    HANDLE thread;

    DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
        GetCurrentProcess(), &thread, 0, false, DUPLICATE_SAME_ACCESS);
    std::cout << "Walking stack.";
    show_stack(std::cout, thread, *(ep->ContextRecord));
    std::cout << "\nEnd of stack walk.\n";
    CloseHandle(thread);

    return EXCEPTION_EXECUTE_HANDLER;
}

void ThreadFunc2() {
    __try { DebugBreak(); }
    __except (Filter(GetExceptionInformation())) {  }
    SetEvent(thread_ready);
}

void ThreadFunc1(void (*f)()) {
    f();
}

// We'll do a few levels of calls from our thread function so 
//     there's something on the stack to walk...
//
DWORD __stdcall TargetThread(void *) {
    ThreadFunc1(ThreadFunc2);
    return 0;
}

class SymHandler { 
    HANDLE p;
public:
    SymHandler(HANDLE process, char const *path=NULL, bool intrude = false) : p(process) { 
        if (!SymInitialize(p, path, intrude)) 
            throw(std::logic_error("Unable to initialize symbol handler"));
    }
    ~SymHandler() { SymCleanup(p); }
};

#ifdef _M_X64
STACKFRAME64 init_stack_frame(CONTEXT c) {
    STACKFRAME64 s;
    s.AddrPC.Offset = c.Rip;
    s.AddrPC.Mode = AddrModeFlat;
    s.AddrStack.Offset = c.Rsp;
    s.AddrStack.Mode = AddrModeFlat;    
    s.AddrFrame.Offset = c.Rbp;
    s.AddrFrame.Mode = AddrModeFlat;
    return s;
}
#else
STACKFRAME64 init_stack_frame(CONTEXT c) {
    STACKFRAME64 s;
    s.AddrPC.Offset = c.Eip;
    s.AddrPC.Mode = AddrModeFlat;
    s.AddrStack.Offset = c.Esp;
    s.AddrStack.Mode = AddrModeFlat;    
    s.AddrFrame.Offset = c.Ebp;
    s.AddrFrame.Mode = AddrModeFlat;
    return s;
}
#endif

void sym_options(DWORD add, DWORD remove=0) {
    DWORD symOptions = SymGetOptions();
    symOptions |= add;
    symOptions &= ~remove;
    SymSetOptions(symOptions);
}

class symbol { 
    typedef IMAGEHLP_SYMBOL64 sym_type;
    sym_type *sym;
    static const int max_name_len = 1024;
public:
    symbol(HANDLE process, DWORD64 address) : sym((sym_type *)::operator new(sizeof(*sym) + max_name_len)) {
        memset(sym, '\0', sizeof(*sym) + max_name_len);
        sym->SizeOfStruct = sizeof(*sym);
        sym->MaxNameLength = max_name_len;
        DWORD64 displacement;

        if (!SymGetSymFromAddr64(process, address, &displacement, sym))
            throw(std::logic_error("Bad symbol"));
    }

    std::string name() { return std::string(sym->Name); }
    std::string undecorated_name() { 
        std::vector<char> und_name(max_name_len);
        UnDecorateSymbolName(sym->Name, &und_name[0], max_name_len, UNDNAME_COMPLETE);
        return std::string(&und_name[0], strlen(&und_name[0]));
    }
};

bool show_stack(std::ostream &os, HANDLE hThread, CONTEXT& c) {
    HANDLE process = GetCurrentProcess();
    int frame_number=0;
    DWORD offset_from_symbol=0;
    IMAGEHLP_LINE64 line = {0};

    SymHandler handler(process);

    sym_options(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);

    void *base = load_modules_symbols(process, GetCurrentProcessId());

    STACKFRAME64 s = init_stack_frame(c);

    line.SizeOfStruct = sizeof line;

    IMAGE_NT_HEADERS *h = ImageNtHeader(base);
    DWORD image_type = h->FileHeader.Machine;

    do {
        if (!StackWalk64(image_type, process, hThread, &s, &c, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
            return false;

        os << std::setw(3) << "\n" << frame_number << "\t";
        if ( s.AddrPC.Offset != 0 ) {
            std::cout << symbol(process, s.AddrPC.Offset).undecorated_name();

            if (SymGetLineFromAddr64( process, s.AddrPC.Offset, &offset_from_symbol, &line ) ) 
                    os << "\t" << line.FileName << "(" << line.LineNumber << ")";
        }
        else
            os << "(No Symbols: PC == 0)";
        ++frame_number;
    } while (s.AddrReturn.Offset != 0);
    return true;
}

class get_mod_info {
    HANDLE process;
    static const int buffer_length = 4096;
public:
    get_mod_info(HANDLE h) : process(h) {}

    module_data operator()(HMODULE module) { 
        module_data ret;
        char temp[buffer_length];
        MODULEINFO mi;

        GetModuleInformation(process, module, &mi, sizeof(mi));
        ret.base_address = mi.lpBaseOfDll;
        ret.load_size = mi.SizeOfImage;

        GetModuleFileNameEx(process, module, temp, sizeof(temp));
        ret.image_name = temp;
        GetModuleBaseName(process, module, temp, sizeof(temp));
        ret.module_name = temp;
        std::vector<char> img(ret.image_name.begin(), ret.image_name.end());
        std::vector<char> mod(ret.module_name.begin(), ret.module_name.end());
        SymLoadModule64(process, 0, &img[0], &mod[0], (DWORD64)ret.base_address, ret.load_size);
        return ret;
    }
};

void *load_modules_symbols(HANDLE process, DWORD pid) {
    ModuleList modules;

    DWORD cbNeeded;
    std::vector<HMODULE> module_handles(1);

    EnumProcessModules(process, &module_handles[0], module_handles.size() * sizeof(HMODULE), &cbNeeded);
    module_handles.resize(cbNeeded/sizeof(HMODULE));
    EnumProcessModules(process, &module_handles[0], module_handles.size() * sizeof(HMODULE), &cbNeeded);

    std::transform(module_handles.begin(), module_handles.end(), std::back_inserter(modules), get_mod_info(process));
    return modules[0].base_address;
}