C/C++ 프로그램의 매니페스트 생성 이해

매니페스트는 어셈블리를 고유하게 식별하는 XML 문서입니다. COM 클래스, 인터페이스 및 형식 라이브러리와 같은 바인딩 및 활성화에 사용되는 정보가 포함되어 있습니다. 매니페스트는 외부 XML 파일 또는 애플리케이션 또는 어셈블리 내에 포함된 리소스일 수 있습니다. 격리된 애플리케이션의 매니페스트는 런타임에 애플리케이션 이 바인딩해야 하는 공유 병렬 어셈블리의 이름과 버전을 관리하는 데 사용됩니다. side-by-side 어셈블리의 매니페스트는 이름, 버전, 리소스 및 다른 어셈블리에 대한 어셈블리의 종속성을 지정합니다.

격리된 애플리케이션 또는 side-by-side 어셈블리의 매니페스트를 만드는 방법은 두 가지입니다. 먼저 어셈블리 작성자가 규칙 및 명명 요구 사항에 따라 매니페스트 파일을 수동으로 만들 수 있습니다. 자세한 내용은 매니페스트 파일 참조를 참조하세요. 또는 프로그램이 CRT, MFC, ATL 등의 MSVC 어셈블리에만 의존하는 경우 링커는 매니페스트를 자동으로 생성할 수 있습니다.

MSVC 라이브러리의 헤더에는 어셈블리 정보가 포함되며, 라이브러리가 애플리케이션 코드에 포함되면 링커에서 이 어셈블리 정보를 사용하여 최종 이진 파일에 대한 매니페스트를 형성합니다. 기본적으로 링커는 이진 파일 내에 매니페스트 파일을 포함하지 않습니다. 매니페스트를 외부 파일로 사용하는 경우 일부 시나리오에서는 작동하지 않을 수 있습니다. 예를 들어 프라이빗 어셈블리에는 매니페스트가 포함된 것이 좋습니다. NMAKE를 사용하여 코드를 빌드하는 것과 같은 명령줄 빌드에서 링커 옵션을 사용하여 /MANIFEST:EMBED 매니페스트를 포함할 수 있습니다. 또는 매니페스트 도구를 사용하여 매니페스트를 포함할 수 있습니다. 자세한 내용은 명령줄에서 매니페스트 생성을 참조 하세요. Visual Studio에서 빌드할 때 다음 섹션에 설명된 대로 프로젝트 속성 대화 상자에서 매니페스트 도구의 속성을 설정하여 매니페스트를 포함할 수 있습니다.

Visual Studio에서 매니페스트 생성

프로젝트의 속성 페이지 대화 상자에서 특정 프로젝트에 대한 매니페스트 파일을 생성하도록 Visual Studio에 지시할 수 있습니다 . 구성 속성에서 링커>매니페스트 파일>매니페스트 생성을 선택합니다. 기본적으로 새 프로젝트의 프로젝트 속성은 매니페스트 파일을 생성하도록 설정됩니다. 그러나 프로젝트의 매니페스트 생성 속성을 사용하여 프로젝트에 대한 매니페스트 생성을 사용하지 않도록 설정할 수 있습니다. 이 속성을 Yes설정하면 프로젝트에 대한 매니페스트가 생성됩니다. 그렇지 않으면 링커는 애플리케이션 코드의 종속성을 확인할 때 어셈블리 정보를 무시하고 매니페스트를 생성하지 않습니다.

Visual Studio의 빌드 시스템을 사용하면 매니페스트를 최종 이진 애플리케이션 파일에 포함하거나 외부 파일로 생성할 수 있습니다. 이 동작은 프로젝트 속성 대화 상자에서 매니페스트 포함 옵션으로 제어합니다. 이 속성을 설정하려면 매니페스트 도구 노드를 연 다음 입력 및 출력을 선택합니다. 매니페스트가 포함되지 않은 경우 외부 파일로 생성되고 최종 이진 파일과 동일한 디렉터리에 저장됩니다. 매니페스트가 포함된 경우 Visual Studio는 다음 프로세스를 사용하여 최종 매니페스트를 포함합니다.

  1. 소스 코드가 개체 파일로 컴파일되면 링커는 종속 어셈블리 정보를 수집합니다. 링커는 최종 이진 파일을 연결하는 동안 나중에 최종 매니페스트를 생성하는 데 사용되는 중간 매니페스트를 생성합니다.

  2. 중간 매니페스트 및 연결이 완료되면 매니페스트 도구는 최종 매니페스트를 병합하고 외부 파일로 저장합니다.

  3. 그런 다음 프로젝트 빌드 시스템은 매니페스트 도구에서 생성된 매니페스트가 이진 파일에 이미 포함된 매니페스트 외의 다른 정보를 포함하는지 검색합니다.

  4. 이진 파일에 포함된 매니페스트가 매니페스트 도구에서 생성된 매니페스트와 다르거나 이진 파일에 포함된 매니페스트가 없는 경우 Visual Studio는 링커를 한 번 더 호출하여 이진 파일 내에 외부 매니페스트 파일을 리소스로 포함합니다.

  5. 이진 파일에 포함된 매니페스트가 매니페스트 도구에서 생성된 매니페스트와 동일한 경우 빌드는 다음 빌드 단계를 계속합니다.

매니페스트는 최종 이진 파일 내에 텍스트 리소스로 포함됩니다. Visual Studio에서 최종 이진 파일을 파일로 열어 볼 수 있습니다. 매니페스트가 올바른 라이브러리를 가리키도록 하려면 Visual C++ 애플리케이션의 종속성 이해에 설명된 단계를 따릅니다. 또는 문제 해결 문서에 설명된 제안을 따릅니다.

명령줄에서 매니페스트 생성

NMAKE 또는 유사한 도구를 사용하여 명령줄에서 C/C++ 애플리케이션을 빌드하는 경우 링커가 모든 개체 파일을 처리하고 최종 이진 파일을 빌드한 후에 매니페스트가 생성됩니다. 링커는 개체 파일에 저장된 어셈블리 정보를 수집하고 이 정보를 최종 매니페스트 파일로 결합합니다. 기본적으로 링커는 최종 이진 파일을 설명하는 파일을 <binary_name>.<extension>.manifest 생성합니다. 링커는 링커 옵션을 지정하여 이진 파일 내에 매니페스트 파일을 포함할 /MANIFEST:EMBED 수 있습니다.

매니페스트 도구(mt.exe)를 사용하거나 매니페스트를 리소스 파일로 컴파일하는 등 마지막 이진 파일 내에 매니페스트를 포함하는 다른 여러 가지 방법이 있습니다. 매니페스트를 포함할 때 특정 규칙을 따라 증분 연결, 서명 및 편집 및 계속과 같은 기능을 사용하도록 설정해야 합니다. 이러한 규칙 및 기타 옵션은 다음 섹션에서 설명합니다.

C/C++ 애플리케이션 내에 매니페스트를 포함하는 방법

최종 이진 파일 내에 애플리케이션 또는 라이브러리의 매니페스트를 포함하는 것이 좋습니다. 이 방법은 대부분의 시나리오에서 올바른 런타임 동작을 보장합니다. 기본적으로 Visual Studio는 프로젝트를 빌드할 때 매니페스트를 포함하려고 시도합니다. 그러나 NMAKE를 사용하여 애플리케이션을 빌드하는 경우 메이크파일을 몇 가지 변경해야 합니다. 이 섹션에서는 매니페스트를 최종 이진 파일 안에 자동으로 포함하도록 메이크파일을 변경하는 방법을 보여 줍니다.

두 가지 방법

애플리케이션 또는 라이브러리 내에 매니페스트를 포함하는 방법에는 두 가지가 있습니다.

  1. 증분 빌드를 수행하지 않는 경우 빌드 후 단계로 다음과 유사한 명령줄을 사용하여 매니페스트를 직접 포함할 수 있습니다.

    mt.exe -manifest MyApp.exe.manifest -outputresource:MyApp.exe;1
    

    또는

    mt.exe -manifest MyLibrary.dll.manifest -outputresource:MyLibrary.dll;2
    

    EXE에는 1을 사용하고 DLL에는 2를 사용합니다.

  2. 증분 빌드를 수행하는 경우 다음 단계를 사용합니다.

    • 파일을 생성하려면 이진 파일을 연결합니다 MyApp.exe.manifest .

    • 매니페스트를 리소스 파일로 변환합니다.

    • 매니페스트 리소스를 이진 파일에 포함하려면 (증분 방식으로) 다시 연결합니다.

다음 예제에서는 메이크파일을 변경하여 두 기법을 통합하는 방법을 보여 줍니다.

메이크파일(이전)

한 파일에서 빌드된 간단한 애플리케이션인 NMAKE 스크립트 MyApp.exe를 고려합니다.

# build MyApp.exe
!if "$(DEBUG)" == "1"
CPPFLAGS=$(CPPFLAGS) /MDd
LFLAGS=$(LFLAGS) /INCREMENTAL
!else
CPPFLAGS=$(CPPFLAGS) /MD
!endif

MyApp.exe : MyApp.obj
    link $** /out:$@ $(LFLAGS)

MyApp.obj : MyApp.cpp

clean :
    del MyApp.obj MyApp.exe

이 스크립트가 Visual Studio에서 변경되지 않고 실행되면 성공적으로 만들어집니다 MyApp.exe. 또한 운영 체제에서 런타임에 종속 어셈블리를 로드하는 데 사용할 외부 매니페스트 파일 MyApp.exe.manifest도 만듭니다.

NMAKE 스크립트 MyLibrary.dll 는 다음과 유사합니다.

# build MyLibrary.dll
!if "$(DEBUG)" == "1"
CPPFLAGS=$(CPPFLAGS) /MDd
LFLAGS=$(LFLAGS) /DLL /INCREMENTAL

!else
CPPFLAGS=$(CPPFLAGS) /MD
LFLAGS=$(LFLAGS) /DLL

!endif

MyLibrary.dll : MyLibrary.obj
    link $** /out:$@ $(LFLAGS)

MyLibrary.obj : MyLibrary.cpp

clean :
    del MyLibrary.obj MyLibrary.dll

메이크파일(이후)

포함된 매니페스트를 사용하여 빌드하려면 원래 메이크파일을 네 가지 작은 변경해야 합니다. 메이크파일의 MyApp.exe 경우:

# build MyApp.exe
!include makefile.inc
#^^^^^^^^^^^^^^^^^^^^ Change #1. (Add full path if necessary.)

!if "$(DEBUG)" == "1"
CPPFLAGS=$(CPPFLAGS) /MDd
LFLAGS=$(LFLAGS) /INCREMENTAL
!else
CPPFLAGS=$(CPPFLAGS) /MD
!endif

MyApp.exe : MyApp.obj
    link $** /out:$@ $(LFLAGS)
    $(_VC_MANIFEST_EMBED_EXE)
#^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Change #2

MyApp.obj : MyApp.cpp

clean :
    del MyApp.obj MyApp.exe
    $(_VC_MANIFEST_CLEAN)
#^^^^^^^^^^^^^^^^^^^^^^^^ Change #3

!include makefile.target.inc
#^^^^^^^^^^^^^^^^^^^^^^^^^ Change #4. (Add full path if necessary.)

MyLibrary.dll 메이크파일:

# build MyLibrary.dll
!include makefile.inc
#^^^^^^^^^^^^^^^^^^^^ Change #1. (Add full path if necessary.)

!if "$(DEBUG)" == "1"
CPPFLAGS=$(CPPFLAGS) /MDd
LFLAGS=$(LFLAGS) /DLL /INCREMENTAL

!else
CPPFLAGS=$(CPPFLAGS) /MD
LFLAGS=$(LFLAGS) /DLL

!endif

MyLibrary.dll : MyLibrary.obj
    link $** /out:$@ $(LFLAGS)
    $(_VC_MANIFEST_EMBED_DLL)
#^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Change #2.

MyLibrary.obj : MyLibrary.cpp

clean :
    del MyLibrary.obj MyLibrary.dll
    $(_VC_MANIFEST_CLEAN)
#^^^^^^^^^^^^^^^^^^^^^^^^ Change #3.

!include makefile.target.inc
#^^^^^^^^^^^^^^^^^^^^^^^^^ Change #4. (Add full path if necessary.)

이제 메이크파일에는 실제 작업을 makefile.inc 수행하는 두 개의 파일이 포함됩니다 makefile.target.inc.

다음 콘텐츠를 만들어 makefile.inc 복사합니다.

# makefile.inc -- Include this file into existing makefile at the very top.

# _VC_MANIFEST_INC specifies whether build is incremental (1 - incremental).
# _VC_MANIFEST_BASENAME specifies name of a temporary resource file.

!if "$(DEBUG)" == "1"
CPPFLAGS=$(CPPFLAGS) /MDd
LFLAGS=$(LFLAGS) /INCREMENTAL
_VC_MANIFEST_INC=1
_VC_MANIFEST_BASENAME=__VC90.Debug

!else
CPPFLAGS=$(CPPFLAGS) /MD
_VC_MANIFEST_INC=0
_VC_MANIFEST_BASENAME=__VC90

!endif

####################################################
# Specifying name of temporary resource file used only in incremental builds:

!if "$(_VC_MANIFEST_INC)" == "1"
_VC_MANIFEST_AUTO_RES=$(_VC_MANIFEST_BASENAME).auto.res
!else
_VC_MANIFEST_AUTO_RES=
!endif

####################################################
# _VC_MANIFEST_EMBED_EXE - command to embed manifest in EXE:

!if "$(_VC_MANIFEST_INC)" == "1"

#MT_SPECIAL_RETURN=1090650113
#MT_SPECIAL_SWITCH=-notify_resource_update
MT_SPECIAL_RETURN=0
MT_SPECIAL_SWITCH=
_VC_MANIFEST_EMBED_EXE= \
if exist $@.manifest mt.exe -manifest $@.manifest -out:$(_VC_MANIFEST_BASENAME).auto.manifest $(MT_SPECIAL_SWITCH) & \
if "%ERRORLEVEL%" == "$(MT_SPECIAL_RETURN)" \
rc /r $(_VC_MANIFEST_BASENAME).auto.rc & \
link $** /out:$@ $(LFLAGS)

!else

_VC_MANIFEST_EMBED_EXE= \
if exist $@.manifest mt.exe -manifest $@.manifest -outputresource:$@;1

!endif

####################################################
# _VC_MANIFEST_CLEAN - command to clean resources files generated temporarily:

!if "$(_VC_MANIFEST_INC)" == "1"

_VC_MANIFEST_CLEAN=-del $(_VC_MANIFEST_BASENAME).auto.res \
    $(_VC_MANIFEST_BASENAME).auto.rc \
    $(_VC_MANIFEST_BASENAME).auto.manifest

!else

_VC_MANIFEST_CLEAN=

!endif

# End of makefile.inc
####################################################

이제 다음 콘텐츠를 만들어 makefile.target.inc 복사합니다.

# makefile.target.inc - include this at the very bottom of the existing makefile

####################################################
# Commands to generate initial empty manifest file and the RC file
# that references it, and for generating the .res file:

$(_VC_MANIFEST_BASENAME).auto.res : $(_VC_MANIFEST_BASENAME).auto.rc

$(_VC_MANIFEST_BASENAME).auto.rc : $(_VC_MANIFEST_BASENAME).auto.manifest
    type <<$@
#include <winuser.h>
1RT_MANIFEST"$(_VC_MANIFEST_BASENAME).auto.manifest"
<< KEEP

$(_VC_MANIFEST_BASENAME).auto.manifest :
    type <<$@
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
</assembly>
<< KEEP

# end of makefile.target.inc

참고 항목

C/C++ 격리된 애플리케이션 및 side-by-side 어셈블리 빌드
격리된 애플리케이션 및 side-by-side 어셈블리의 개념
C/C++ 격리된 애플리케이션 및 병렬 어셈블리 문제 해결
/INCREMENTAL (증분 방식으로 연결)
/MANIFEST (병렬 어셈블리 매니페스트 만들기)
강력한 이름 어셈블리(어셈블리 서명)(C++/CLI)
편집하며 계속하기