Delphi Programming Forum
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
델파이 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
컴포넌트/라이브러리
FreePascal/Lazarus
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
델마당
볼랜드포럼 광고 모집

델파이 팁&트릭
Delphi Programming Tip&Tricks
[290] 프로그램 기동시 Runtime error 217의 원인은?
박지훈.임프 [cbuilder] 22730 읽음    2012-10-25 09:35
프로그램 기동시에 Runtime error 217 이라는 에러가 발생하는 경우를 만나게 되면, 개발자로서는 상당히 당황하게 됩니다. 어떤 브레이크포인트를 잡을 곳조차도 없이, 밑도 끝도 없이 에러만 덜렁 발생하니까요. 게다가 더 진행도 안되고 프로그램은 그대로 끝나버립니다. 즉 프로그램이 시작도 되지 않은 상태에서 종료되어버리기 때문에 황당해집니다.

이 "Runtime error 217" 에러의 원인은, 일반적으로 프로그램 초기화시의 코드 에러 때문입니다. 더 정확하게는, 프로그램의 기본적인 예외처리가 동작하기 전에 예외가 발생하는 경우입니다. 가장 흔한 경우는, initialization 섹션에서 예외가 발생한 경우입니다.

델파이 애플리케이션의 기본적인 예외처리 루틴은 SysUtils에서 초기화가 이루어지는데, 만약 SysUtils 유닛이 초기화되기 전의 시점에서 뭔가 예외가 발생해버리면, SysUtils보다 더 아래 레벨인 System 유닛의 예외 처리 루틴을 타게 되는데요. 이 System 유닛의 예외 처리에서 바로 이 "Runtime error 217" 메시지를 내보내고는 Halt, 즉 강제 종료를 하도록 되어 있습니다.

initialization 섹션
앞에서 말한대로, 이 에러가 발생할 때 가장 크게 혐의를 둘 수 있는 곳은 initialization 섹션입니다. 따라서 프로젝트에 포함된 모든 유닛들에서 initialization 섹션이 있는지, 있다면 거기에 새로 추가한 코드가 있는지 확인해야 합니다.

SysUtils의 초기화 문제와 개발자 코드의 initialization 섹션과 관련이 있는 이유는 이렇습니다. 만약 프로젝트 전체의 uses들 중에서 불러지는 순서가 SysUtils보다 더 앞에 지정된 유닛의 initialization 코드가 실행되고 있는 시점에서는, SysUtils는 uses 되지 않은 상태라 당연히 예외 처리 루틴이 초기화되지 않은 상태입니다. 그래서 이 시점에서 예외가 발생할 경우 SysUtils의 예외처리 루틴으로 정상 처리되지 못하고 'Runtime error 217' 에러가 나게 됩니다.

통상적으로 VCL 자체의 유닛들은 충분히 검증되었다고 볼 수 있으므로, VCL의 유닛이 아닌 개발자가 작성한 유닛이 uses에서 SysUtils보다 앞에 나열되어 있을 경우 의심의 대상이 됩니다.

작성한 유닛의 초기화 과정에서 꽤 복잡한 코드를 실행하는 경우에는 implementation 섹션에 함수나 프로시저로 만들고 그걸 initialization 섹션에서 호출하기만 하는 경우도 있으므로, 그런 경우라면 해당 함수까지 확인해봐야겠지요.

C++의 경우에도 델파이의 initialization 섹션에 해당하는 초기화 방법들이 있으므로, 만약 C++빌더에서 217 에러를 만났다면 이런 초기화 코드들을 확인해봐야 합니다.

윈도우7에서만 문제가 발생한다면
특히, 윈도우7이나 비스타에서 프로그램 기동시에 이 에러를 갑자기 만나고 당황하는 경우가 있는데요. 만약 동일한 프로그램이 윈도우XP에서는 이 에러가 나지 않는데 윈도우 7이나 비스타에서 기동시 이 에러가 난다면, 이것은 대부분 프로그램 초기화에서 권한이 부족한, 즉 권한 상승이 필요한 작업을 하려고 해서 예외가 발생했기 때문입니다.

윈도우7의 UAC로 인해 XP까지는 문제가 없던 코드가 문제가 생기는 경우로는, 권한이 없는 디렉토리에 파일을 쓰려고 했다든가, 권한이 없는 레지스트리 키에 값을 쓰려고 했을 경우 등이 있을 수 있습니다. XP까지는 이런 제한이 없었기 때문에 잘 돌아갔었지만 윈도우7/비스타의 권한 관리가 변경되어 생기기 시작하는 문제죠.

이것도 역시 initialization 섹션의 코드와 관련이 있습니다. initialization 과정에서 권한 상승이 필요한 동작이 있을 수 있기 때문입니다. 예를 들면 권한이 없는 디렉토리 경로의 파일을 write 하려고 한다든지 하는 코드가 권한상승이 되지 않은 일반 권한으로 실행되면 윈도우7 등에서는 예외가 발생하게 되는데, 그 코드가 initialization 섹션에 있었다면 역시 Runtime error 217이 발생하게 됩니다.

이런 경우는 의외로 종종 있습니다. 예를 들면 프로그램의 초기화 과정에서 Program Files 디렉토리 아래의 프로그램이 설치된 디렉토리에 있는 ini 파일에 뭔가 내용을 쓰려고 했다면 딱 이런 케이스에 걸리게 됩니다. 또 레지스트리에서 HKEY_CURRENT_USER 이외의 위치에 내용을 쓰려고 했다면 역시 권한상승이 필요한 동작이 됩니다. 이런 경우에 Runtime error 217이 발생합니다.

이런 케이스라면, 탐색기에서 해당 실행파일에서 오른쪽 클릭하여 "관리자 권한으로 실행"을 해보면 금방 확인할 수 있습니다. 이런 경우라면 관리자 권한으로 실행했을 경우에 217 에러 없이 정상 실행이 될 것이므로, 초기화중 권한이 더 필요한 코드를 실행했다는 의미가 됩니다.

외부 코드 uses 추가로 인한 경우
initialization 섹션에 추가한 코드로 인해 217 에러가 발생한다는 것은, 다시 말해 델파이나 C++빌더에서 다른 소스를 uses(링크)하기만 해도 이 에러가 날 수 있다는 의미도 됩니다.

예를 들면, 새로운 컴포넌트나 라이브러리를 갖다 써서 uses 됐는데, 그 컴포넌트가 윈도우7의UAC 문제에 대해 대비가 되어 있지 않은 구버전 소스이고 initialization 섹션에서 그런 코드를 썼다면 역시 217 에러가 날 것입니다.

이런 경우, 개발자로서는 별다른 코딩을 한 것도 아닌데 갑자기 에러가 발생하기 시작하므로 쉽게 원인을 찾기 어려울 수 있습니다. 따라서 자신이 initialization 섹션에 새로 추가한 코드가 없는데도 잘 동작하던 프로그램이 약간의 수정 후 217 에러가 발생하기 시작한다면, 별 생각없이 갖다 쓴 라이브러리 때문일 가능성이 높습니다.

UAC를 꺼버리거나 권한 상승으로 해결한다?
적지 않은 개발자와 프로그램 개발사들이 이 에러에 대한 해결책으로 사용자들에게 윈도우의 UAC를 끄라고 조언하고 있더군요. 윈도우 UAC의 취지 면에서 봤을 때, 프로그램의 동작을 위해 사용자에게 UAC를 끄라고 하는 것은 좋은 방법이 아닙니다. 특히 UAC를 꺼버린 문제로, 장기적으로 해당 사용자 PC에서 해킹이라든지 하는 문제가 일어났을 때 법적으로나 도의적으로 책임의 문제가 발생할 수도 있습니다.

이런 경우 개발자로서는 권한 상승을 하는 방법을 먼저 찾게 되는데, 사실 그보다 먼저 권한 상승을 하지 않고 해결할 수 있는 방법이 혹시 있는지 먼저 찾아보는 것이 좋습니다. 물론 꼭 권한 상승이 필요해서 피할 수 없는 경우가 더 많습니다만, 그래도 피할 수 있다면 피하는 것이 더 좋겠습니다. 권한 상승은 그냥 Yes 한번 누르면 끝나는 일이기는 해도 사용자로서는 아무렇지도 않게 무시할 정도의 일은 아닙니다.

또한 해당 프로세스에서 로드하는 외부 exe나 dll 등의 다른 모든 모듈들의 코드가 함께 권한이 상승되므로, 보안상의 이슈가 발생할 가능성도 얼마든지 있습니다. 따라서 꼭 필요한 경우가 아니거나 회피할 방법이 있다면, 무분별한 권한 상승은 하지 않는 편이 좋겠습니다.
빌더(TWx) [builder]   2012-10-27 19:40 X
In fact, Delphi executables will die with runtime error 217 when an exception was raised before SysUtils is initialized or after it is finalized. In both situations the regular exception handling is not put in place

위와 같이 Delphi FAQ 사이트에 누군가가 올린 글을 근거로 해서 "Runtime error 217"의 원인이 SysUtils 유닛이 초기화되기 전의 시점에서 뭔가 예외가 발생해서 라는 취지로 글을 올린 거 같은데....

"Runtime error 217"의 원인은 SysUtils 유닛의 초기화(Initialization 섹션)와 관련이 없는 겁니다.
빌더(TWx) [builder]   2012-10-27 19:43 X
예외(Exception)와 관련한 모든 설정은 System.pas 유닛에서 이미 이루어지게 되고...

SysUtils 유닛의 Initialization 섹션에서 하는 것은 플렛폼과 모듈과 관련한 몇가지 처리만 할 뿐이지...
Exception 과 관련한 코드는 전혀 없습니다...

빌더(TWx) [builder]   2012-10-27 19:47 X
"Runtime error 217"의 의미는 Exception과 관련이 있는 게 아니고... System Registry 와 같은 시스템 리소스가 깨져 있을 때...
Critical Error 상황으로 보고 RTL에서 Runtime error 메세지를 출력하는 것일 뿐입니다...

UAC와 관련해서 언급되고 있는 내용도... 문제의 본질과는 무관한 겁니다.


"
박지훈.임프 [cbuilder]   2012-12-06 23:33 X
제 생각엔... 아무래도 빌더님이 잘못 알고 계신 것 같은데요.

먼저, 위에 인용하신 영문 부분은 처음 보는데, 제가 쓴 위 본문은 VCL의 예외처리 루틴을 다 추적해본 결과 내린 결론입니다.
(저는 14년 전에 이 사이트를 처음 열 때부터, 어디서 배우거나 줏어들은 지식인 경우 반드시 출처를 밝히고 있습니다)

SysUtils가 예외와 전혀 무관하다고 하셨는데, 전혀 그렇지 않습니다.
SysUtils 유닛은 VCL의 통상적인 예외 처리 루틴이 대부분 들어있는 유닛인데요.
VCL의 기본 예외처리 루틴들이 모두 이 SysUtils에 있고, Exception 클래스를 비롯한 많은 예외 클래스들도 다 여기에 정의되어 있죠.

애플리케이션 코드에서 명시적으로 예외가 처리되지 못하는 경우에 이 SysUtils의 예외처리 루틴으로 넘어오게 되어 있습니다.
더 정확하게 말하면, 말씀하신 System 유닛에서 예외 처리가 시작되기는 하지만, 실제로는 SysUtils로 넘어오게 됩니다.

그렇게 되는 이유는... System의 실제 예외 처리 루틴은 ExceptProc 함수 포인터가 가리키는 루틴을 호출하게 되는데요.
이 ExceptProc 루틴은 초기화가 되어 있지 않아 System 유닛 레벨에서는 nil 값인데요.
SysUtils가 initialize될 때 SysUtils의 ExceptHandler 루틴을 가리키게 됩니다.

이후로 SysUtils의 ExceptHandler 루틴은 SysUtils의 여러 예외 처리 루틴들을 거치면서 예외를 실질적으로 처리합니다.
개발자 코드에서 예외처리를 하지 않았을 때 나타나는 예외 메시지박스도 여기에 포함됩니다.

다시 정리하면, 개발자 코드에서 핸들링되지 않은 예외는, SysUtils 유닛이 initialize된 상태에서는 아래와 같은 순서로 처리가 되죠.
1. System 유닛의 _ExceptionHandler 루틴
2. System 유닛의 ExceptProc 함수 포인터
3. SysUtils의 ExceptHandler 루틴
4. SysUtils의 ShowException 루틴
5. SysUtils의 ExceptionErrorMessage 루틴 및 MessageBox 에러메시지 혹은 콘솔에러

그런데, SysUtils의 initialize가 이루어지지 않은 상태에서, 즉 SysUtils의 루틴들이 미처 로드되기 전에 예외가 발생하면, System 유닛의 ExceptProc 함수 포인터가 nil인 상태이므로, 이런 정상적인 예외처리가 이루어지지 못하게 됩니다.

System 유닛의 ExceptProc 함수 포인터가 nil인 경우에는 메시지박스로 "Runtime error 217 at..." 메시지를 뿌린 후 Halt 하게 되어 있습니다. 더 자세하게는, 217이라는 명시적인 숫자 상수값 에러 코드가 어셈블리 코드에 박혀있고, 이 에러코드값을 가지고 "Runtime error ??? at..." 메시지텍스트와 조합해서 메시지를 뿌리는 코드도 나옵니다. "Runtime error ??? at..." 메시지는 System 유닛의 runErrMsg이라는 문자 배열 변수로 저장되어 있습니다.

이런 코드는 System 유닛의 _ExceptionHandler 루틴의 어셈블리 코드 부분을 보시면 확인하실 수 있습니다. (조금 복잡하게 호출되지만 어쨌든 제가 설명한 이런 과정으로 호출됩니다)

위 본문에서는 제가 간략하게만 설명했습니다만, SysUtils와 System 유닛을 다시 분석해봐도 제가 쓴 내용이 맞는 것 같은데요. ^^;;
김도완 [purplecofe2]   2012-12-09 14:17 X
제 경험으로는 Sysutils가 들어가야 비로소 제대로 익셥센 핸들링이 되는게 맞다고 봅니다. DLL을 만들 때 sysutils를 명시하지 않으면 제대로 처리를 하지 못하더군요. 그리고 DEP의 경우에도 윈도우즈 64비트에서는 runtime error 217를 뿜어내기도 한다고 알고 있습니다.

+ -

관련 글 리스트
290 프로그램 기동시 Runtime error 217의 원인은? 박지훈.임프 22730 2012/10/25
(링크)     C++Builder Tip'N Tricks > 프로그램 기동시 Runtime error 217의 원인은?
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.