Основные сведения о создании манифестов для программ на C/C++

Манифест — это XML-документ, который однозначно идентифицирует сборку. Он содержит сведения, используемые для привязки и активации, такие как COM-классы, интерфейсы и библиотеки типов. Манифест может быть внешним XML-файлом или ресурсом, внедренным в приложение или сборку. Манифест изолированного приложения используется для управления именами и версиями общих параллельных сборок, к которым приложение должно привязаться во время выполнения. Манифест параллельной сборки задает зависимости от имен, версий, ресурсов и других сборок.

Существует два способа создания манифеста для изолированного приложения или параллельной сборки. Во-первых, автор сборки может вручную создать файл манифеста, следуя правилам и требованиям к именованию. Дополнительные сведения см. в справочнике по файлам манифеста. Кроме того, если программа зависит только от сборок MSVC, таких как CRT, MFC, ATL или другие, компоновщик может создать манифест автоматически.

Заголовки библиотек MSVC содержат сведения о сборке, а если библиотеки включены в код приложения, эти сведения сборки используются компоновщиком для формирования манифеста для окончательного двоичного файла. По умолчанию компоновщик не внедряет файл манифеста в двоичный файл. Наличие манифеста в качестве внешнего файла может подходить не для всех сценариев. Например, рекомендуется, чтобы частные сборки имели внедренные манифесты. В сборках из командной строки, например в тех, которые используют NMAKE для сборки кода, можно использовать /MANIFEST:EMBED параметр компоновщика для внедрения манифеста. Кроме того, манифест можно внедрить с помощью средства манифеста. Дополнительные сведения см. в разделе Создание манифеста в командной строке. При сборке в Visual Studio манифест можно внедрить, задав свойство для средства манифеста в диалоговом окне Свойства проекта , как описано в следующем разделе.

Создание манифестов в Visual Studio

Вы можете указать Visual Studio создать файл манифеста для конкретного проекта в диалоговом окне Страницы свойств проекта. В разделе Свойства конфигурации выберитеФайл>манифеста компоновщика>Создать манифест. По умолчанию свойства проекта новых проектов задаются для создания файла манифеста. Однако можно отключить создание манифеста для проекта с помощью свойства Generate Manifest проекта. Если для этого свойства задано значение Да, создается манифест проекта. В противном случае компоновщик игнорирует сведения о сборке при разрешении зависимостей кода приложения и не создает манифест.

Система сборки в Visual Studio позволяет внедрять манифест в окончательный двоичный файл приложения или создавать его как внешний файл. Это поведение управляется параметром Внедрить манифест в диалоговом окне Свойства проекта. Чтобы задать это свойство, откройте узел Инструмент манифеста, а затем выберите Вход и выход. Если манифест не внедрен, он создается как внешний файл и сохраняется в том же каталоге, что и окончательный двоичный файл. Если манифест внедрен, Visual Studio внедряет окончательные манифесты с помощью следующего процесса.

  1. После компиляции исходного кода в объектные файлы компоновщик собирает сведения о зависимых сборках. Связав последний двоичный файл, компоновщик создает промежуточный манифест, который будет использоваться позже для создания окончательного манифеста.

  2. После завершения промежуточного манифеста и связывания средство манифеста объединяет окончательный манифест и сохраняет его как внешний файл.

  3. Затем система сборки проекта определяет, отличается ли информация в манифесте, сгенерированном инструментом манифеста, от той, которая содержится в манифесте, уже внедренном в двоичный файл.

  4. Если манифест, внедренный в двоичный файл, отличается от манифеста, созданного средством манифеста, или двоичный файл не содержит внедренного манифеста, Visual Studio вызывает компоновщик еще раз, чтобы внедрить внешний файл манифеста в двоичный файл в качестве ресурса.

  5. Если манифест, внедренный в двоичный файл, совпадает с манифестом, созданным средством манифеста, сборка переходит к следующим шагам сборки.

Манифест внедряется в окончательный двоичный файл в виде текстового ресурса. Его можно просмотреть, открыв окончательный двоичный файл в Виде файла в Visual Studio. Чтобы убедиться, что манифест указывает на правильные библиотеки, выполните действия, описанные в разделе Основные сведения о зависимостях приложения Visual C++. Или следуйте рекомендациям, описанным в статье Устранение неполадок .

Создание манифеста в командной строке

При сборке приложений C/C++ из командной строки с помощью NMAKE или аналогичных средств манифест создается после того, как компоновщик обработает все объектные файлы и создаст окончательный двоичный файл. Компоновщик собирает сведения о сборке, хранящиеся в объектных файлах, и объединяет эту информацию в окончательный файл манифеста. По умолчанию компоновщик создает файл с именем <binary_name>.<extension>.manifest для описания окончательного двоичного файла. Компоновщик может внедрить файл манифеста в двоичный файл, указав /MANIFEST:EMBED параметр компоновщика.

Существует несколько других способов внедрения манифеста в окончательный двоичный файл, например с помощью средства манифеста (mt.exe) или компиляция манифеста в файл ресурсов. При внедрении манифеста необходимо следовать определенным правилам, чтобы включить такие функции, как добавочное связывание, подписывание и изменение и продолжение. Эти правила и другие параметры рассматриваются в следующем разделе.

Внедрение манифеста в приложение C/C++

Рекомендуется внедрить манифест приложения или библиотеки в окончательный двоичный файл. Такой подход гарантирует правильное поведение среды выполнения в большинстве сценариев. По умолчанию Visual Studio пытается внедрить манифест при сборке проекта. Однако при сборке приложения с помощью NMAKE необходимо внести некоторые изменения в файл makefile. В этом разделе показано, как изменить файлы makefile таким образом, чтобы манифест автоматически внедрялся в конечный двоичный файл.

Два подхода

Существует два способа внедрения манифеста в приложение или библиотеку.

  1. Если вы не выполняете добавочную сборку, вы можете напрямую внедрить манифест с помощью командной строки, аналогичной следующему шагу после сборки:

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

    or

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

    Используйте значение 1 для файла EXE и 2 для библиотеки DLL.

  2. Если вы выполняете добавочную сборку, выполните следующие действия.

    • Свяжите двоичный файл, чтобы создать MyApp.exe.manifest файл.

    • Преобразуйте манифест в файл ресурсов.

    • Повторное связывание (добавочное), чтобы внедрить ресурс манифеста в двоичный файл.

В приведенных ниже примерах показано, как изменить файлы makefile для применения обоих методов.

Файлы makefile (до изменения)

Рассмотрим сценарий 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

Файлы makefile (после изменения)

Для сборки с помощью внедренных манифестов необходимо внести четыре небольших изменения в исходные файлы makefile. MyApp.exe Для makefile:

# 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.)

Для файла makefile библиотеки 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 теперь включают два файла, которые выполняют реальную работу, 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++
Основные понятия, связанные с изолированными приложениями и параллельными сборками
Устранение неполадок в изолированных приложениях и параллельных сборках на C/C++
/INCREMENTAL (Постепенное связывание)
/MANIFEST (Создание параллельного манифеста сборки)
Сборки со строгими именами (подписывание сборок) (C++/CLI)
Изменить и продолжить