Windows環境下でのC++におけるSEH例外のC++ catchによる捕捉

Structured Exception HandlingをC++例外に変換する手法

Windowsプラットフォームでは、メモリアクセス違反(Access Violation)やゼロ除算など、Win32の構造化例外(SEH: Structured Exception Handling)が発生することがあります。しかし、標準C++のtry-catch文はこれらの例外を直接捕捉できません。これを可能にするには、SEHをC++例外へ変換する仕組みを導入する必要があります。

Visual Studioコンパイラ設定

まず、Microsoft Visual Studioを使用する場合、C++例外とSEHの統合を有効にするため、コンパイルオプションに/EHaを指定する必要があります。これは「異種例外処理(Exception Handling for SEH)」を有効にするフラグです。

Qtプロジェクト(.proファイル)では以下のように記述します:

win32-msvc {
    QMAKE_CXXFLAGS += /EHa
}

SEHからC++例外への変換関数

Windows APIでは、_set_se_translator関数を使用して、SEH例外をC++のthrowによって投げられる例外に変換できます。このトランスレータ関数内で、特定の例外コードに基づいてカスタム例外クラスをスローします。

#include <windows.h>
#include <eh.h>
#include <stdexcept>

class Win32Exception : public std::runtime_error {
public:
    explicit Win32Exception(const std::string& msg) : std::runtime_error(msg) {}
};

void seh_to_cpp_exception(unsigned int code, _EXCEPTION_POINTERS*) {
    switch (code) {
        case EXCEPTION_ACCESS_VIOLATION:
            throw Win32Exception("Memory access violation detected");
        case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
            throw Win32Exception("Array index out of bounds");
        case EXCEPTION_INT_DIVIDE_BY_ZERO:
            throw Win32Exception("Integer division by zero");
        case EXCEPTION_STACK_OVERFLOW:
            throw Win32Exception("Stack overflow");
        default:
            throw Win32Exception("Unknown structured exception occurred");
    }
}

アプリケーション初期化時の登録

メイン関数でこのトランスレータを登録することで、以降発生するSEHが自動的にC++例外として扱われます。

int main(int argc, char *argv[]) {
    // SEHをC++例外に変換
    _set_se_translator(seh_to_cpp_exception);

    QGuiApplication app(argc, argv);
    QQmlApplicationEngine engine;

    // 例外安全なGC実行
    try {
        runGarbageCollection();
    } catch (const std::exception& e) {
        qCritical() << "GC failed:" << e.what();
    } catch (...) {
        qCritical() << "Unknown error during GC";
    }

    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    return app.exec();
}

ガベージコレクション中の例外保護

特にQMLエンジンのような複雑なメモリ管理を行うコードでは、ポインタ不整合や解放済みメモリアクセスによりアクセス違反が発生しやすいです。以下の例では、GCの実行をtry-catchでラップし、異常終了を防いでいます。

void GarbageCollector::performCollection() {
    try {
        markRoots();
        traceObjects();
        releaseUnreachable();
    } catch (const Win32Exception& winEx) {
        qWarning() << "SEH caught in GC:" << winEx.what();
        handleGcFailure();
    } catch (const std::exception& stdEx) {
        qWarning() << "Standard exception in GC:" << stdEx.what();
    }
}

注意点

  • /EHaは例外安全性を高める一方、わずかなパフォーマンスオーバーヘッドがあります。
  • スタックオーバーフローはトランスレータが呼び出されない場合があるため、別途対策が必要です。
  • この方法はWindows固有であり、クロスプラットフォームコードでは条件付きコンパイルが必要です。

タグ: C++ Windows SEH Exception Handling Qt

6月18日 00:24 投稿