Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 

supportedRuntime 옵션과 System.BadImageFormatException 예외

최근에 다음과 같은 오류를 만났습니다.

badformat_appconfig_1.png

Unhandled Exception: System.BadImageFormatException: Could not load file or assembly 'ConsoleApplication1.exe' or one of its dependencies. This assembly is built by a runtime newer than the currently loaded runtime and cannot be loaded.
File name: 'ConsoleApplication1.exe'


머리로는 BadImageFormatException이 x86/x64의 불일치에서 오는 문제라고만 생각하고 있어서 제법 애를 먹었는데요. 원인은, .NET 4.0 대상으로 빌드한 응용 프로그램에 다음과 같은 supportedRuntime 정책을 적용했기 때문이었습니다.

<?xml version="1.0"?>
<configuration>
    <startup>
        <supportedRuntime version="v2.0.50727"/>
        <supportedRuntime version="v4.0.30319"/>
    </startup>
</configuration>

즉, 4.0으로 빌드한 경우 v2.0.50727 항목으로 인해 오류가 발생한 것입니다. 이를 해결하려면, 다음과 같이 순서를 바꾸는 것도 가능은 합니다.

<?xml version="1.0"?>
<configuration>
    <startup>
        <supportedRuntime version="v4.0.30319"/>
        <supportedRuntime version="v2.0.50727"/>
    </startup>
</configuration>

그렇지만, 이런 식의 해결방법은 (동작은 하지만) 잘못 판단된 것입니다. 왜냐하면, 위의 프로그램을 .NET 2.0만 설치된 환경에서 실행하면 예외가 발생하기 때문입니다. 즉, 2.0의 supportedRuntime을 두 번째 선택으로 미뤄놓은 아무런 의미가 없는 것입니다.

그렇다면, supportedRuntime이 언제 의미가 있는 것일까요?




supportedRuntime은 사실, 하위 버전으로 빌드된 어셈블리를 상위버전의 환경에서 구동할 때 호환을 위해 명시해 주는 데 의미가 있습니다. 간단하게 예를 들어볼까요?

다음과 같은 Console 응용 프로그램을 만들고,

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(Environment.Version.ToString());
    }
}

이를 .NET 2.0으로 컴파일 합니다. 이 상태에서, 아래와 같은 app.config 구성으로 .NET 2.0/4.0이 모두 설치된 컴퓨터에서 실행하면 어떤 결과가 나올까요?

<?xml version="1.0"?>
<configuration>
    <startup>
        <supportedRuntime version="v4.0.30319"/> 
        <supportedRuntime version="v2.0.50727"/>
    </startup>
</configuration>

"v4.0.30319"가 위에 있으니 Environment.Version 출력값은 (제 컴퓨터 기준으로) "4.0.30319.239"입니다. 반면, 버전 번호의 자리를 바꿔서 출력하면 이번엔 "v2.0.50727"이 위에 있으므로 (제 컴퓨터 기준으로) "2.0.50727.5448" 결괏값이 나옵니다.

정리해 보면, supportedRuntime은 하위 버전의 응용 프로그램이 어떤 .NET Framework 런타임에서 실행할지를 결정하는 옵션입니다. 따라서, 처음 이 글의 문제로 돌아가서, supportedRuntime의 순서를 바꿀 것이 아니라 해당 응용 프로그램을 .NET 2.0 대상으로 빌드하는 것이 적절한 해결책입니다. (아니면, 아예 모든 supportedRuntime 옵션을 제거하든가.)

참고로, 기타 supportedRuntime의 종류에 관해서는 예전에 정리둔 것이 있으니 한번 봐두시는 것도 좋겠습니다. ^^

app.config의 supportedRuntime 종류
; https://www.sysnet.pe.kr/2/2/178




간단한 문제를 내 볼까요? ^^ 그렇다면 .NET 2.0으로 빌드된 exe를 아무런 app.config 파일 없이 .NET 4.0만 설치된 컴퓨터에서 실행하면 어떻게 될까?

그런 경우, 다음과 같이 예외가 발생합니다.

badformat_appconfig_2.png

.NET Framework Initialization Error
Unable to find a version of the runtime to run this application.

만약, 이 응용 프로그램을 정상적으로 구동하고 싶다면 app.config에 다음과 같은 설정을 추가해 주면 됩니다.

<?xml version="1.0"?>
<configuration>
    <startup>
        <supportedRuntime version="v4.0.30313"/>
    </startup>
</configuration>

또 다른 경우를 생각해 볼까요? .NET 1.1 exe 파일을 .NET 1.1/4.0은 설치되었지만 .NET 2.0이 설치되지 않은 컴퓨터에서 다음과 같이 2.0을 지원한다고 지정해 두면 어떻게 될까요?

<?xml version="1.0"?>
<configuration>
    <startup>
        <supportedRuntime version="v2.0.50727"/>
    </startup>
</configuration>

이런 경우에도 역시 예외가 발생하면서 실행은 안되지만, 다행히 2.0을 설치할 것인지를 물어보는 대화상자가 뜹니다.

badformat_appconfig_3.png

To run this application, you first must install one of the following versions of the .NET Framework: v2.0.50727

Would you like to download and install version v2.0.50727 of the .NET Framework now?

이번엔 닷넷 프레임워크 버전 격차를 늘려서, .NET 1.1 exe 파일을 .NET 4.0만 설치된 컴퓨터에서 다음과 같이 4.0을 지원한다고 지정해 두면 어떻게 될까요?

<?xml version="1.0"?>
<configuration>
    <startup>
        <supportedRuntime version="v4.0.30319"/>
    </startup>
</configuration>

걱정하지 마세요. ^^ (대부분의 경우) 실행이 아주 잘됩니다. 여기서 supportedRuntime을 가장 극적으로 사용할 수 있는 사례가 나옵니다. 즉, .NET 1.x/2.0/3.0/3.5/4.0에 상관없이 실행되어야 할 프로그램을 만들어야 한다면 해당 응용 프로그램은 .NET 1.1 버전으로 빌드하고 다음과 같이 supportedRuntime 목록을 구성해 주면 되는 것입니다.

<?xml version="1.0"?>
<configuration>
  <startup>
    <supportedRuntime version="v1.1.4322"/>
    <supportedRuntime version="v2.0.50727"/>
    <supportedRuntime version="v4.0.30319"/>
  </startup>
</configuration>

또는 일반적으로 최신 버전의 닷넷 프레임워크가 성능이 더 나아지기 때문에 다중 CLR이 설치된 경우에는 가장 최신의 것을 적용받도록 다음과 같이 거꾸로 순서를 지정하는 것도 좋습니다.

<?xml version="1.0"?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0.30319"/>
    <supportedRuntime version="v2.0.50727"/>
    <supportedRuntime version="v1.1.4322"/>
  </startup>
</configuration>

어떤 설정이든, .NET 1.1로 빌드된 응용 프로그램은 어떤 버전의 닷넷 프레임워크라도 설치되어 있으면 정상적으로 실행이 될 것입니다.




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]


donaricano-btn



[최초 등록일: ]
[최종 수정일: 6/26/2021

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 쓴 사람
 



2014-04-29 02시29분
[spowner] 항상 간지러운 곳을 긁어 주셔서 감사합니다.
[손님]
2017-01-05 05시40분
[이지영] 프로그램 실행하는데 아래 같은 CLR20r3 오류가 나와 검색 끝에 여기까지 오게 되었는데요.

혹시 아래 오류 해결 조언 좀 부탁드려도 될까요? 위에 있는 방법으로 Transistor.exe.config라는 파일을 txt로 수정해서 해봤는데 설치 메세지만 뜨고..

어떻게 해야 할지를 모르겠습니다. 넷프레임은 1.1하고 4.6.2가 설치 되어 있습니다.. 이쪽 분야에 전문가분이신 것 같은데 조언 좀 부탁드려요.


문제 서명:
  문제 이벤트 이름: CLR20r3
  문제 서명 01: Transistor.exe
  문제 서명 02: 1.0.0.0
  문제 서명 03: 53b44d4e
  문제 서명 04: MonoGame.Framework.Windows
  문제 서명 05: 2.0.0.0
  문제 서명 06: 539b9ca8
  문제 서명 07: d7d
  문제 서명 08: cf
  문제 서명 09: U5BUGYMB24YGE00VAJPF2KCRJNMKDEEE
  OS 버전: 6.1.7601.2.1.0.256.1
  로캘 ID: 1042
  추가 정보 1: 8eed
  추가 정보 2: 8eed98f8b73ca44fd30d1b635a59cc65
  추가 정보 3: ff9f
  추가 정보 4: ff9f26c412fe1114ca0cbb0f7bc711a5
[손님]
2017-01-06 12시35분
CLR20r3 문제는 해당 프로그램을 만든 개발자도 해결하기 힘든 문제에 속합니다. 외부 개발자의 경우 어찌어찌 문제의 원인은 파악할 수 있어도 그렇다고 해결할 수 있는 것은 아닙니다. 경우에 따라 소스 코드를 변경해야 하기 때문인데요. 따라서 이런 문제는 Transistor.exe를 개발한 측에 물어보는 것이 가장 좋습니다.
정성태
2017-01-06 05시01분
[이지영] 아주 해결하기 어려운 문제인가 보군요. 조언 정말 감사합니다.
[손님]
2017-01-09 05시38분
[정환나라] // 이지영 //
.Net Framework를 4.0이나 2.0을 추가로 설치해 보세요.
[손님]

[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
12749정성태8/2/202123개발 환경 구성: 590. Visual Studio 2017부터 단위 테스트에 DataRow 특성 지원
12748정성태8/2/20218개발 환경 구성: 589. Azure Active Directory - tenant의 관리자(admin) 계정 로그인 방법
12747정성태8/1/202128오류 유형: 748. 오류 기록 - MICROSOFT GRAPH – HOW TO IMPLEMENT IAUTHENTICATIONPROVIDER파일 다운로드1
12746정성태7/31/202175개발 환경 구성: 588. 네트워크 장비 환경을 시뮬레이션하는 Packet Tracer 프로그램 소개
12745정성태7/31/202121개발 환경 구성: 587. Azure Active Directory - tenant의 관리자 계정 로그인 방법
12744정성태7/30/202127개발 환경 구성: 586. Azure Active Directory에 연결된 App 목록을 확인하는 방법?
12743정성태7/30/202147.NET Framework: 1083. Azure Active Directory - 외부 Token Cache 저장소를 사용하는 방법파일 다운로드1
12742정성태7/30/202125개발 환경 구성: 585. Azure AD 인증을 위한 사용자 인증 유형
12741정성태7/29/202182.NET Framework: 1082. Azure Active Directory - Microsoft Graph API 호출 방법파일 다운로드1
12740정성태7/29/202138오류 유형: 747. SharePoint - InvalidOperationException 0x80131509
12739정성태7/28/202137오류 유형: 746. Azure Active Directory - IDW10106: The 'ClientId' option must be provided.
12738정성태7/28/202141오류 유형: 745. Azure Active Directory - Client credential flows must have a scope value with /.default suffixed to the resource identifier (application ID URI).
12737정성태7/28/202139오류 유형: 744. Azure Active Directory - The resource principal named api://...[client_id]... was not found in the tenant
12736정성태7/28/202145오류 유형: 743. Active Azure Directory에서 "API permissions"의 권한 설정이 "Not granted for ..."로 나오는 문제
12735정성태7/27/202181.NET Framework: 1081. C# - Azure AD 인증을 지원하는 데스크톱 애플리케이션 예제(Windows Forms)파일 다운로드1
12734정성태7/26/202186스크립트: 20. 특정 단어로 시작하거나/끝나는 문자열을 포함/제외하는 정규 표현식 - Look-around
12733정성태7/23/2021107.NET Framework: 1081. Self-Contained/SingleFile 유형의 .NET Core/5+ 실행 파일을 임베딩한다면?파일 다운로드2
12732정성태7/23/202149오류 유형: 742. SharePoint - The super user account utilized by the cache is not configured.
12731정성태7/23/202171개발 환경 구성: 584. Add Internal URLs 화면에서 "Save" 버튼이 비활성화 된 경우
12730정성태7/23/202173개발 환경 구성: 583. Visual Studio Code - Go 코드에서 입력을 받는 경우
12729정성태7/22/202181.NET Framework: 1080. xUnit 단위 테스트에 메서드/클래스 수준의 문맥 제공 - Fixture
12728정성태7/22/202174.NET Framework: 1079. MSTestv2 단위 테스트에 메서드/클래스/어셈블리 수준의 문맥 제공
12727정성태7/21/2021147.NET Framework: 1078. C# 단위 테스트 - MSTestv2/NUnit의 Assert.Inconclusive 사용법(?)
12726정성태7/21/2021131VS.NET IDE: 169. 비주얼 스튜디오 - 단위 테스트 선택 시 MSTestv2 외의 xUnit, NUnit 사용법
12725정성태7/21/202173오류 유형: 741. Failed to find the "go" binary in either GOROOT() or PATH
[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...