Qt 应用程序中的 C++ Crash/exception 处理程序在 Windows 上支持线程

C++ Crash/exception handler in Qt application with thread support on Windows

我想为我的 Qt 应用程序创建一个 crash/exception 处理程序。我已经让处理程序工作了(不包括在下面的代码中)。问题出在 Windows 上,它仅在崩溃发生在调用 signal()std::set_terminate() 的同一线程中时才有效。

在 Linux 上,它似乎默认适用于所有线程。

有没有办法让它适用于 Windows 上的所有应用程序线程?

#include "mainwindow.h"
#include <QApplication>
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <exception>

void seg_handler(int sig)
{
    // Crash/exception handling code
    // ...
    exit(1);
}

void std_handler( void ) {
     seg_handler(1);
}

int main(int argc, char *argv[]) {

    signal(SIGSEGV, seg_handler);
    std::set_terminate( std_handler );

    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

根据MSDN

In a multithreaded environment, terminate functions are maintained separately for each thread. Each new thread needs to install its own terminate function. Thus, each thread is in charge of its own termination handling.

我想你必须为每个线程单独设置它。

经过大量研究,再加上我对 Windows OS 缺乏了解,我终于找到了一个可行的解决方案,可以自动为新线程设置终止函数。

诀窍是创建一个 dll 来处理您的 crashes/exceptions。在您的 dll 中实现保留的 DllMain() 函数。每当新线程或进程附加到主进程时,此函数都会收到通知。 fdwReason 参数保存有关事件的信息。

  • 新话题:DLL_THREAD_ATTACH
  • 新进程:DLL_PROCESS_ATTACH

在 dll 中实现终止处理程序,并为每个启动的线程或进程调用 signal()std::set_terminate()

BOOL WINAPI DllMain(
  _In_ HINSTANCE hinstDLL,
  _In_ DWORD     fdwReason,
  _In_ LPVOID    lpvReserved) {
    if (fdwReason==DLL_THREAD_ATTACH) {
        signal(SIGSEGV, seg_handler);
        std::set_terminate( std_handler );
    }
    if (fdwReason==DLL_PROCESS_ATTACH) {
        signal(SIGSEGV, seg_handler);
        std::set_terminate( std_handler );
    }
    return TRUE;
}

我的处理程序是这样的:

#include <windows.h>
#include "StackWalker.h"
#include <signal.h>
#include <DbgHelp.h>
#include <iostream>
#include <qdebug.h>

void seg_handler(int sig) {
    // Simple callstack
    unsigned int   i;
    void         * stack[ 100 ];
    unsigned short frames;
    SYMBOL_INFO  * symbol;
    HANDLE         process;

    process = GetCurrentProcess();
    SymInitialize( process, NULL, TRUE );
    frames               = CaptureStackBackTrace( 0, 100, stack, NULL );
    symbol               = ( SYMBOL_INFO * )calloc( sizeof( SYMBOL_INFO ) + 256 * sizeof( char ), 1 );
    symbol->MaxNameLen   = 255;
    symbol->SizeOfStruct = sizeof( SYMBOL_INFO );

    for( i = 0; i < frames; i++ ) {
        SymFromAddr( process, ( DWORD64 )( stack[ i ] ), 0, symbol );
        printf( "%i: %s - 0x%0X\n", frames - i - 1, symbol->Name, symbol->Address );
    }

    free( symbol );

    // Detailed callstack/crashinfo
    StackWalker sw;
    sw.ShowCallstack(GetCurrentThread());
    exit(1);
}

void std_handler( void ) {
     seg_handler(1);
}

上面的处理程序使用 StackWalker

编辑:

当然,您还需要将终止处理程序添加到您的主线程中:

int main(int argc, char *argv[]) {
    CALL_LOGGER();
    signal(SIGSEGV, seg_handler);
    std::set_terminate( std_handler );
    return runapp(argc, argv);
}

希望这对某人有所帮助

-雅各布

我怀疑 SetUnhandledExceptionFiltersignal 更能捕获 Windows 上的崩溃。它是全局设置的(因此您不需要 DLL 在每个线程中安装处理程序)并且不会丢弃有关故障的信息(可能有某种方法可以恢复 SIGSEGV 的真正原因,但我不知道马上就知道怎么做了)。