CLR에서 throw std::exception을 하면 SEHException이 발생합니다

안녕하세요.

CLR에서 개발하던 중 궁금한 점이 있어 질문을 남기게 됐습니다.

  • 환경
    IDE: Visual Studio 2019
    Project: CLR 클래스 라이브러리 및 Windows Forms 앱

  • 사용 목적
    네이티브 코드에서 발생한 예외(std::exception)를 C# Exception으로 전달하고 싶습니다.

  • 코드 구현
    CLR 프로젝트에서 아래와 같이 코드를 구현했습니다.

void Class1::ThrowMessage()
{
	try
	{
		throw std::exception("Throw message");
	}
	catch (const std::exception& exc)
	{
		throw gcnew System::Exception(msclr::interop::marshal_as<System::String^>(exc.what()));
	}
}
  • 결과
    WinForms에서 아래의 코드를 실행하면, 'Throw message’가 메시지박스에 표시됩니다.
private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                Class1 test = new Class1();
                test.ThrowMessage();
            }
            catch(Exception exc)
            {
                MessageBox.Show(exc.Message);
            }
        }
  • 질문
    지금까지 Native 코드에서 발생하는 예외 메시지를 위와 같이 처리했었는데요. 프로그램이 정상적으로 동작해서 문제가 없는 줄 알았는데, Visual Studio 출력 창에 아래의 메시지가 표시되더군요.
    검색을 통해 관리코드에서 비관리코드의 모든 예외를 처리하지 않아 발생한 것이라는 원인은 찾았지만, 그래서 어떻게 조치를 해야 하는지 모르겠습니다…;
    또는, 조치할 필요 없이 그냥 사용해도 되는지도 궁금하기도 합니다.

예외 발생: ‘System.Runtime.InteropServices.SEHException’(ClassLibrary1.dll)

며칠 동안 구글링만 하다가… 도저히 답을 모르겠어서 이렇게 질문을 남기게 됐습니다.

도움을 구하고 싶습니다.

감사합니다.

3개의 좋아요

c++/cli로 래핑하여 처리 하는 방식이 잘 안되신다면 아래의 콜백 함수를 지정하여 예외를 다루는 방법에 대해 고민해 보시면 좋을 것 같습니다.
정성태 MVP님이 정리하신 글이 있어 공유 드려요.
https://www.sysnet.pe.kr/2/0/11099

현재 방식을 고수 하고 싶으시다면, c++/cli로 정의하신 dll 코드를 아래 예시와 함께 리뷰해보시는 것도 좋을 것 같아 링크 공유드려요.

4개의 좋아요
void Class1::ThrowMessage()
{
	try
	{
		throw std::exception("Throw message");
	}
      catch (std::exception& ex) {
          // 예외 처리
      }
      catch (System::Exception^ ex) {
          // 예외 처리
      }
}

올려주신 예제 코드인 c++/cli 환경에서 native 예외를 잘 받고 있는걸까요?

2개의 좋아요

안녕하세요

답변 주셔서 감사합니다!

첫 번째 댓글의 ‘콜백 함수’ 예외 방법은 제가 시도해 보고 말씀 드리겠습니다.
두 번째 댓글의 native 예외는 잘 받아집니다.

추가로, 혹시 가능하시면 샘플 코드로 보여드리는 것이 나을 것 같아 프로젝트 파일을 첨부합니다.
아래 링크를 클릭하면 본문의 예제 코드를 다운로드할 수 있습니다.
(게시물에 파일 첨부가 안되는 것 같아 링크로 대체합니다 :cry: )

다운로드

x64 설정으로 WinFormsApp1을 실행하고 버튼을 클릭하면, 아래의 화면처럼 메시지가 표시됩니다.
image

그리고 출력 창을 보시면 SEHException이 발생했다는 메시지가 보입니다.
image

예외 설정에서 SEHException을 체크하고 다시 디버그 해보면
image

아래와 같이 메시지가 발생합니다.
image

참고 부탁드립니다.

감사합니다 :smile:

3개의 좋아요

SEHException이 개발 환경이 아닌 실행 환경에서도 발생되는걸까요?
실행 환경에서 발생하지 않으면 무시할 수 있는 수준이 아닌가 싶기도 해서요.

1개의 좋아요

실행 환경(Release)에서는 발생하지 않습니다.
그래서 저도 무시해도 되는 것인지, 처리되지 않은 예외가 누적 발생하여 문제가 될 가능성이 있는지 궁금하네요 ㅎ…

2개의 좋아요

런타임에서 예외가 발생되지 않으면 무시하셔도 될 것 같기는 한데요.
닷넷데브에서 SEHException에 대해 다른 분이 문의 하셨던 적이 있었네요.

원치 않은 SEHException 이시면 ErrorCode를 파악해서 원인을 찾아 봐야 될 것 같아요.

1개의 좋아요

감사합니다.
해당 게시물도 이미 봤었지만 뚜렷한 해결책을 찾지 못했습니다 ㅎㅎ;

추가로, C++ dll을 생성하고 P/Invoke로 WinForm에서 사용하는 예제 코드를 만들어 봤는데요.
동작은 정상이지만, 마찬가지로 SEHException이 출력 창에 남는군요.

std::exception을 C#으로 전달하면서 주의점이 있는 것 같은데… 도저히 모르겠네요;
저는 더 찾아보고, 여기 게시물에 업데이트 하겠습니다ㅎ

2개의 좋아요

c++ 런타임과 c#의 런타임이 다르기 때문에 c++/cli 계층에서 c++의 네이티브 예외인 std::exception을 받으면 Managed 예외 타입으로 변환해서 throw 해야 되는데요.
예제 코드에는 그러한 처리는 안되어 있는 것 같아요.
테스트 하실 때 참고해주세요~
네이티브 예외 처리에 답변이 있는데 도움이 되실까 싶어 공유드려요.

2개의 좋아요

테스트를 좀 해 봤는데요.

원인

C++/CLI Managed 런타임 환경에서 Unmanaged 예외를 발생시키면 CLR은 내부적으로 PInvoke를 통한 마샬링이 발생되고, 그 과정에서 SEHException 예외가 발생되어 유저 코드상에서는 catch가 되지 않습니다.
예외 발생 이후 catch가 가능합니다.
그래서 C++/CLI 상에서 예외를 발생시키시고 싶으시면 Managed 예외를 발생 시켜야 합니다.

image
image

Invoke 함수에서 Lib는 네이티브 클래스이며, invoke 함수 호출시 std::exception이 발생됩니다.
앞서 말씀드린 것처럼 C++/CLI 상에서 UnManaged 예외이므로 PInrovke에 의한 SEHException이 발생됩니다.

Invoke2 함수에서 gcnew를 통한 Managed 예외는 PInvoke가 발생되지 않아 c# 런타임까지 그대로 예외가 전달 됩니다.

참고자료

아래부터는 참고 했던 자료입니다.
C++/CLI 상의 rethrow 에 대한 설명 문서입니다.
image

C++/CLI 상에서 UnManaged 예외 발생시 PInvoke가 일어나는 과정에 대한 문서입니다
image

그리고 그것에 대한 질문/답변입니다.
https://social.msdn.microsoft.com/Forums/vstudio/en-US/8a710bde-d9d0-490e-8d87-4a913c9bcd37/unexpected-sehexception-in-ccli-code?forum=vcgeneral

마지막으로 혹시 codeproject에 질문하셨나요? 맞다면 무섭네요 ;; 금새 검색이 ㅎㅎ

결론

C++/CLI 상에서는 Managed 예외를 사용하세요.
불가피하게 UnManaged 예외를 받아야 되는 상황이면, SEHException은 무시하시고, Exception^으로 캐치하여 후처리를 작성하세요.
image

ps. 작성했던 예제가 필요하시면 첨부하도록 하겠습니다~

4개의 좋아요

안녕하세요.

감사합니다. 이렇게 심도 깊게 확인해 주실 줄 몰랐습니다 :smile:

남겨주신 댓글을 확인하기 전에, 여러 링크를 참고하고 테스트를 해봤습니다.
(그리고 codeproject도 제가 남긴 것이 맞습니다 ^^; 벌써 검색되다니 놀랍네요 ㅎ)

결론은, 말씀하신 것처럼 C++/CLI에서 Managed 예외만 사용하니 문제가 없더군요.
(C++ dll P/Invoke도 마찬가지로 SEHException이 발생하는 것을 확인했습니다.)

하지만 실제로는 C++ 코드를 C++/CLI에서 호출하여 사용하는 중이라… C++ 코드의 std::exception 사용을 피할 순 없을 것 같습니다.

현재로서는 말씀하신 결론대로 사용해야겠습니다. 이번 기회로 새로운 것을 배우게 됐네요 :slight_smile:

도움 주셔서 정말 정말 감사드립니다. :+1:

4개의 좋아요