구조화 예외 처리(SEH)의 with DirectX 정보

일반 예외


제로 마이너스, 방문 위반 등 하위 등급 예외(SE)는 할 수 없다.붕괴될 거야.
try {
  throw std::exception("hoge");
} catch (exception capture) {
  std::cout << capture.what() << std::endl;
} catch (...) {
  std::cout << "Unknown" << std::endl;
} finally {
  ...
}

구조적 예외


이렇게 하면 SE를 보충할 수 있다.
단, 함수 내에서try-catch와 병용할 수 없습니다.std::function 주 설치를 외부에 두면 몇 분 정도 걸릴 수 있습니다.
_EXCEPTION_POINTERS* info = nullptr;
__try {
  auto hoge = 1 / ZERO; // 0徐算
  nullObject->Invoke(); // アクセス違反
} __except(info = GetExceptionInformation(), EXCEPTION_EXECUTE_HANDLER) {
  auto code = info->ExceptionRecord->ExceptionCode;
  std::cout << code << std::endl;
  throw exception(std::to_string(code)); // これで外側のtry-catchに伝播できる(※)
} __finally {
  ...
}

SE를 예외로 자동 변환 옵션



이 옵션은 SE가 발생할 때 자동으로 C++ 예외로 변환되어 성능상의 벌칙을 바꿀 수 있습니다.하지만 SE의 내용은 빼낼 수 없다.
또한 Debug 작성 시 옵션이 유효하지 않아도 변환됩니다.(쓸데없는 일에 참견해라...)
try { ... }
catch(std::exception e) { ... }
catch(...){
  // ここに来る、「何が起こったか」は分からない
}

_set_se_translator


EHa를 유효성화한 뒤 등록하면 선호하는 예외 형태로 전환할 수 있다.
_set_se_translator([](unsigned int code, _EXCEPTION_POINTERS* ep) -> void {
  throw exception(std::to_string(code));
});

try { ... }
catch(std::exception e) {
  // ここに来る、_set_se_translatorで、例外オブジェクト内に入れた内容が参照できる
} catch(...){ ... }

DirectX용 SEH


DirectX는 COM으로 원래 오류는 SE에서 보고되지 않고 HRESULT에서 보고된다.
다만, HRESULT 방법으로 돌아가지 않거나, COM Object의 Release에서 문제가 발생하면 SE를 보낼 수 있습니다.

SEH 블록/함수 내throw 불가


본론입니다.
COM의 모든 이야기일지도 모르지만 throw 자체가 SE여서 호출 원본으로 복구할 수 없는 함수를 제어합니다.
만약 C++ 예외로 사용할 수 없다면, 매개 변수와 되돌아오는 값으로만 SE를 전달할 수 있습니다.매개변수를 조작할 수 없습니다_set_se_translator. 사용할 수 없습니다.(전역 변수를 사용하면 안 되는 것도 아닌데...)

타협안


상술한 제약에서 나는 코드의 열화는 최소한의 통제 아래 대응할 수 있는 방법이라고 생각한다.
void HandleStructuredException(std::function<void()> *callback, unsigned int &code) {
    __try { callback->operator()(); }
    __except (EXCEPTION_EXECUTE_HANDLER) { code = GetExceptionCode(); }
}

unsigned int seCode = 0;
std::function<void()> callback = [&]() -> void {
  // メイン処理
};
HandleStructuredException(&callback, seCode);
if(seCode != 0) throw std::exception(std::to_string(seCode).data());
GetExceptionCode 대신 GetExceptionInformation를 사용하면 더 자세한 (주소 등)을 얻을 수 있지만 지침이기 때문에 딥 복제가 필요하다.
콜백은 바늘로 전달되지만 SEH를 실현하는 함수에서는 대상 각도에서 탈출할 때의 분석기를 사용할 수 없기 때문이다.
MSDN 컴파일러 오류 C2712

Widnow 메시지 루프 안팎의 예외 전파(추가)


소식 순환 중에 발생하는 예외는 순환 도로 밖으로 전파될 수 없다.더 정확히 말하면 DispatchMessage → ... → WNDCLASSEX.lpfnWndProc라고 불리지만 예외적으로 여기서 뛰어갈 수는 없다.
정보 순환 속에서 여러 윈도우가 현재와 동거하고 있다는 점을 고려하면 어쩔 수 없는 것 같다.
윈도 대상을 MSG 구조의 WHND에 연결하면 실례 필드를 사용하여 예외 인수인계를 하는 부분_set_se_translator보다 조금 낫다.
int main(int argc, char* argc[]) {
  ...
  auto windowHandle = CreateWindow( ... );
  SetWindowLongPtr(windowHandle , GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
  ...
  try {    
    MSG message;
    while (GetMessage(&message, NULL, 0, 0)) {
      auto window = reinterpret_cast<Window *>(GetWindowLongPtr(message.hwnd, GWLP_USERDATA));
      if(window != nullptr && window->Error() != nullptr) {
        throw *window->Error();
      }
      ...
    }
  } catch { ... }
  ...
}
MSDN WindowProc callback function

총결산


원래는 어디까지 처리해야 할지 문제가 있었다.
SEH를 진행해야 하는 시점에 버그가... 핸들을 통해 복구될 수도 없고.
그렇긴 하지만 할 수 있는 일이 없어도 무너지기 전에 미안하다고 말하고 싶어요.
세상에 나오는 C++에 그려진 Windows 응용 프로그램은 이 근처에서 어떻게 만들어졌을까.

참고 자료

  • 구조화 예외 처리(마름모's 기술 노트 페이지)
  • WindowProc callback function (MSDN)
  • Windows 창에서 C++ 예외 std: exceptionpta로 처리하면 (Qita)
  • _set_se_translator (MSDN)
  • SEH 예외와 C++ 예외(상상력 부족은 심각한 단점 중 하나)
  • 컴파일 오류 C2712(MSDN)
  • ()
  • 좋은 웹페이지 즐겨찾기