Environment.Exit ()가 더 이상 프로그램을 종료하지 않는 이유는 무엇입니까?


134

이것은 며칠 전에 발견 한 것입니다 . 이 질문 에서 내 컴퓨터에만 국한되지 않는다는 확인을 받았습니다 .

이를 재현하는 가장 쉬운 방법은 Windows Forms 응용 프로그램을 시작하고 단추를 추가 한 후 다음 코드를 작성하는 것입니다.

    private void button1_Click(object sender, EventArgs e) {
        MessageBox.Show("yada");
        Environment.Exit(1);         // Kaboom!
    }

Exit () 문이 실행 된 프로그램이 실패합니다 . Windows Forms에서 "창 핸들 작성 오류"가 표시됩니다.

관리되지 않는 디버깅을 활성화하면 진행 상황이 어느 정도 명확 해집니다. COM의 모달 루프가 실행하고 WM_PAINT 메시지가 전달 될 수있다. 폐기 된 형태에 치명적입니다.

내가 지금까지 모은 유일한 사실은 다음과 같습니다.

  • 디버거로 실행하는 것만으로는 제한되지 않습니다. 이것 없이도 실패합니다. 오히려 WER 크래시 대화 상자가 두 번 나타납니다 .
  • 프로세스의 비트와 관련이 없습니다. wow64 레이어는 꽤 악명 높지만 AnyCPU 빌드는 같은 방식으로 충돌합니다.
  • .NET 버전 4.5와 3.5는 같은 방식으로 충돌하지 않습니다.
  • 종료 코드는 중요하지 않습니다.
  • Exit ()를 호출하기 전에 Thread.Sleep ()를 호출해도 문제가 해결되지 않습니다.
  • 이는 64 비트 버전의 Windows 8에서 발생하며 Windows 7은 동일한 방식으로 영향을받지 않는 것 같습니다.
  • 이것은 비교적 새로운 행동이어야합니다. 이전에 본 적이 없었습니다. 내 컴퓨터에서 업데이트 기록이 더 이상 정확하지 않지만 Windows Update를 통해 제공되는 관련 업데이트 는 없습니다.
  • 이것은 엄청나게 파괴적인 행동입니다. AppDomain.UnhandledException의 이벤트 처리기에서 이와 같은 코드를 작성하면 같은 방식으로 충돌합니다.

이 충돌을 피하기 위해 할 수있는 일에 특히 관심이 있습니다. 특히 AppDomain.UnhandledException 시나리오가 저를 방해합니다. .NET 프로그램을 종료하는 방법은 많지 않습니다. UnhandledException에 대한 이벤트 처리기에서는 Application.Exit () 또는 Form.Close () 호출이 유효하지 않으므로 해결 방법이 아닙니다.


업데이트 : Mehrdad는 finalizer 스레드가 문제의 일부일 수 있다고 지적했습니다. 나는 이것을보고 있다고 생각하고 CLR이 finalizer 스레드가 실행을 끝내게한다는 2 초 타임 아웃에 대한 증거를보고있다.

종료자는 NativeWindow.ForceExitMessageLoop () 안에 있습니다. 32 비트 모드에서 기계어 코드를 볼 때 코드 위치와 대략 0x3c 오프셋 인 IsWindow () Win32 함수가 있습니다. IsWindow ()가 교착 상태 인 것 같습니다. 내부에 대해 좋은 스택 추적을 얻을 수는 없지만 디버거는 P / Invoke 호출이 방금 반환 된 것으로 생각합니다 . 이것은 설명하기 어렵다. 더 나은 스택 추적을 얻을 수 있다면 그것을보고 싶습니다. 나의 것:

System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.ForceExitMessageLoop() + 0x3c bytes
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.Finalize() + 0x16 bytes
[Native to Managed Transition]
kernel32.dll!@BaseThreadInitThunk@12()  + 0xe bytes
ntdll.dll!___RtlUserThreadStart@8()  + 0x27 bytes
ntdll.dll!__RtlUserThreadStart@8()  + 0x1b bytes

관리되지 않는 디버거를 사용하도록 설정 한 ForceExitMessageLoop 호출 이상은 없습니다.


2
방금 .NET 4, 4 Client Profile, 3.5, 3.5 Client Profile, 3.0 및 2.0으로 시도했지만 오류가 발생하지 않았습니다. 64 비트 Windows 7은 VS2010을 사용하는 OS입니다.
Steve

2
@Steve This happens on the 64-bit version of Windows 8Hans는 그렇게 말했습니다!
Parimal Raj

7
이것을 재현 (Win 8, 64 비트)하고 코드를 복사 / 붙여 넣기하고 버튼을 연결하면 정확한 증상이 나타납니다.
keyboardP

3
콘솔 모드 앱은이 문제를 보여줄 수 없습니다. Exit ()가 메시지를 계속 펌핑 할 때 아무 문제가 없습니다.
Hans Passant

3
Exit(0)64 비트 Win7에서 조금 전에 이런 종류의 동작이 발생했습니다 . 변경 ExitCodeProcess.GetCurrentProcess().Kill()문제없이 작동하는 데 도움이되지 않습니다.
Sriram Sakthivel

답변:


85

나는이 문제에 대해 Microsoft에 연락했고 그 결과는 대가를 치렀습니다. 적어도 나는 그것이했다고 생각하고 싶다 :). 나는 그들로부터 해상도의 확인을 얻지 못했지만 Windows 그룹은 직접 연락하기가 어려우며 중개자를 사용해야했습니다.

Windows Update를 통해 제공되는 업데이트로 문제가 해결되었습니다. 충돌 전 2 초의 현저한 지연이 더 이상 존재하지 않으므로 IsWindow () 교착 상태가 해결되었음을 나타냅니다. 그리고 프로그램은 깨끗하고 안정적으로 종료됩니다. 이 업데이트는 Windows Defender, wdboot.sys, wdfilter.sys, tcpip.sys, rpcrt4.dll, uxtheme.dll, crypt32.dll 및 wintrust.dll 용 패치를 설치했습니다.

Uxtheme.dll은 홀수입니다. 비주얼 스타일 테마 API를 구현하며이 테스트 프로그램에서 사용됩니다. 확신 할 수는 없지만 문제의 원인으로 돈이 있습니다. C : \ WINDOWS \ system32의 사본은 내 컴퓨터에서 2013 년 8 월 14 일에 작성된 버전 번호 6.2.9200.16660을 갖습니다.

경우 폐쇄.


11
내 컴퓨터에서 Windows Update 기록이 더 이상 정확하지 않습니다. 내가 아는 것은 8 월 14 일에 설치되었다는 것입니다.
Hans Passant

51

"더 이상" 작동하지 않는지 모르겠지만 Environment.Exit보류중인 종료자를 실행 한다고 생각 합니다. Environment.FailFast하지 않습니다.

(기괴한 이유로) 이후에 실행되어야하는 이상한 보류중인 종료자가있을 수 있습니다.


2
당신은 뭔가에있을 수 있습니다. 종료자는 NativeWindow.ForceExitMessageLoop () 실행 중입니다. 이상하게도 어떤 전화에도 중첩되지 않습니다.
Hans Passant

@ HansPassant : 문제를 재현 할 수 있기를 원하지만 그것을 볼 수는 없습니다. 호출이 NativeWindow.ForceExitMessageLoop관리 코드 또는 관리되지 않는 코드 에 걸려 있습니까? 메시지가 고착되어 있거나 메시지를 기다리는 중이거나 기다리는 중입니까?
user541686

이것은 분명히 핵심 문제를 지적하는 것 같습니다. 문제의 근원이되는 IsWindow () winapi 함수라고 생각합니다. 나는 또한 finalizer 스레드에서 2 초 시간 초과를보고 있다고 생각합니다. 그 후 모든 것이 지옥에 빠집니다. 디버거는 IsWindow () 호출을 실행하는 것을 보여 주지 않지만 Windows에서 중요한 코드를 입력 할 때 스위치를 전환하기 전에 Windows가 트릭을 사용하는 것을 보았습니다.
Hans Passant

4
주어진 처리되지 않은 예외의 경우 Environment.FailFast () 메소드가 어쨌든 사용하는 가장 좋은 방법이라고 생각합니다. (나는 그것을 몰랐다-고마워!) 그러나 불행히도 어색하게 충돌 할 Environment.Exit ()를 사용하는 많은 레거시 코드가 있습니다 :(
Ian Yates

2
당신은 가장 확실하게 무언가에 있습니다. 필자의 경우 통합 테스트를 수행하기 위해 IHost.StartAsync를 사용하여 IHost를 시작했지만 IHost.StopAsync를 호출 한 후 대기 한 후에도 프로세스가 여전히 종료되지 않았습니다. IHost.Dispose를 호출 한 후에 만 ​​프로세스가 종료됩니다. 팁 주셔서 감사합니다
Malte R

6

이것은 왜 그런 일이 일어나고 있는지 설명하지 않지만 Environment.Exit샘플과 같은 버튼 이벤트 핸들러를 호출하지 않고 대신 rene 's answer 에서 제안 된 기본 양식을 닫으십시오 .

에 관해서는 AppDomain.UnhandledException핸들러, 어쩌면 당신은 설정할 수 Environment.ExitCode호출보다는 Environment.Exit.

나는 당신이 여기서 무엇을 성취하려고하는지 잘 모르겠습니다. Windows Forms 응용 프로그램에서 종료 코드를 반환하려는 이유는 무엇입니까? 일반적으로 종료 코드는 콘솔 응용 프로그램에서 사용됩니다.

이 충돌을 피하기 위해 할 수있는 일에 특히 관심이 있습니다. WER 대화 상자가 표시되지 않도록 Calling Environment.Exit ()가 필요합니다.

Main 메소드에 try / catch가 있습니까? Windows Forms 응용 프로그램의 경우 항상 메시지 루프와 처리되지 않은 예외 처리기 주위에 try / catch가 있습니다.


Application.Exit대신에 전화해야합니다 Environment.Exit.
user541686

7
죄송합니다. 해결 방법이 아닙니다. WER 대화 상자가 표시되지 않도록하려면 Environment.Exit () 호출이 필요합니다. "알려진 사실"에 유의하십시오. 종료 코드는 중요하지 않습니다.
Hans Passant

7
@Hans : AppDomain.UnhandledException을 잡아서 WER 대화 상자가 처음부터 합법적이지 않게하려고합니까? 처리되지 않은 예외가있는 경우 WER 대화 상자가 표시 되어야 합니까?
Harry Johnston

2

앱에서 동일한 문제를 발견했으며 다음 구문으로 문제를 해결했습니다.

Environment.ExitCode=1;
Application.Exit();
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.