Fonctionnement de la génération de manifeste pour les programmes C/C++

Un manifeste est un document XML qui identifie de façon unique un assembly. Il contient des informations utilisées pour la liaison et l’activation, telles que les classes COM, les interfaces et les bibliothèques de types. Un manifeste peut être un fichier XML externe ou une ressource incorporée à l’intérieur d’une application ou d’un assembly. Le manifeste d’une application isolée est utilisé pour gérer les noms et les versions des assemblys côte à côte partagés auxquels l’application doit être liée au moment de l’exécution. Le manifeste d’un assembly côte à côte spécifie ses dépendances sur les noms, versions, ressources et autres assemblys.

Il existe deux façons de créer un manifeste pour une application isolée ou un assembly côte à côte. Tout d’abord, l’auteur de l’assembly peut créer manuellement un fichier manifeste en suivant les règles et les exigences de nommage. Pour plus d’informations, consultez informations de référence sur les fichiers manifestes. Sinon, si un programme dépend uniquement des assemblys MSVC tels que CRT, MFC, ATL ou d’autres, l’éditeur de liens peut générer automatiquement un manifeste.

Les en-têtes des bibliothèques MSVC contiennent des informations d’assembly et lorsque les bibliothèques sont incluses dans le code de l’application, ces informations d’assembly sont utilisées par l’éditeur de liens pour former un manifeste pour le fichier binaire final. Par défaut, l’éditeur de liens n’incorpore pas le fichier manifeste à l’intérieur du fichier binaire. L’utilisation d’un manifeste en tant que fichier externe peut ne pas fonctionner pour tous les scénarios. Par exemple, il est recommandé que les assemblys privés aient des manifestes incorporés. Dans les builds de ligne de commande telles que celles qui utilisent NMAKE pour générer du code, vous pouvez utiliser l’option /MANIFEST:EMBED éditeur de liens pour incorporer le manifeste. Vous pouvez également incorporer un manifeste à l’aide de l’outil manifeste. Pour plus d’informations, consultez Génération du manifeste sur la ligne de commande. Lorsque vous générez dans Visual Studio, un manifeste peut être incorporé en définissant une propriété pour l’outil manifeste dans la boîte de dialogue Propriétés du projet, comme décrit dans la section suivante.

Génération de manifeste dans Visual Studio

Vous pouvez indiquer à Visual Studio de générer un fichier manifeste pour un projet particulier dans la boîte de dialogue Pages de propriétés du projet. Sous Propriétés de configuration, sélectionnez Manifeste>de l’éditeur de>liens Générer le manifeste. Par défaut, les propriétés du projet de nouveaux projets sont définies pour générer un fichier manifeste. Toutefois, il est possible de désactiver la génération du manifeste pour un projet à l’aide de la propriété Générer le manifeste du projet. Lorsque cette propriété est définie sur Oui, le manifeste du projet est généré. Sinon, l’éditeur de liens ignore les informations d’assembly lors de la résolution des dépendances du code de l’application et ne génère pas le manifeste.

Le système de génération dans Visual Studio permet au manifeste d’être incorporé dans le fichier d’application binaire final ou généré en tant que fichier externe. Ce comportement est contrôlé par l’option Incorporer le manifeste dans la boîte de dialogue Propriétés du projet. Pour définir cette propriété, ouvrez le nœud Outil manifeste, puis sélectionnez Entrée et Sortie. Si le manifeste n’est pas incorporé, il est généré en tant que fichier externe et enregistré dans le même répertoire que le fichier binaire final. Si le manifeste est incorporé, Visual Studio incorpore les manifestes finaux à l’aide du processus suivant :

  1. Une fois le code source compilé dans des fichiers objet, l’éditeur de liens collecte les informations d’assembly dépendantes. Bien qu’il lie le fichier binaire final, l’éditeur de liens génère un manifeste intermédiaire utilisé ultérieurement pour générer le manifeste final.

  2. Une fois le manifeste intermédiaire et la liaison terminés, l’outil manifeste fusionne un manifeste final et l’enregistre en tant que fichier externe.

  3. Le système de génération de projet détecte ensuite si le manifeste généré par l’outil manifeste contient des informations différentes de celles déjà incorporées dans le fichier binaire.

  4. Si le manifeste incorporé dans le fichier binaire est différent du manifeste généré par l’outil manifeste, ou si le fichier binaire ne contient pas de manifeste incorporé, Visual Studio appelle l’éditeur de liens une fois de plus pour incorporer le fichier manifeste externe à l’intérieur du fichier binaire en tant que ressource.

  5. Si le manifeste incorporé dans le fichier binaire est identique au manifeste généré par l’outil manifeste, la build passe aux étapes de génération suivantes.

Le manifeste est incorporé à l’intérieur du fichier binaire final en tant que ressource de texte. Vous pouvez l’afficher en ouvrant le fichier binaire final en tant que fichier dans Visual Studio. Pour vous assurer que le manifeste pointe vers les bibliothèques correctes, suivez les étapes décrites dans Présentation des dépendances d’une application Visual C++. Ou suivez les suggestions décrites dans l’article résolution des problèmes .

Génération de manifeste au niveau de la ligne de commande

Lorsque vous générez des applications C/C++ à partir de la ligne de commande à l’aide de NMAKE ou d’outils similaires, le manifeste est généré une fois que l’éditeur de liens a traité tous les fichiers objet et généré le fichier binaire final. L’éditeur de liens collecte les informations d’assembly stockées dans les fichiers objet et combine ces informations dans un fichier manifeste final. Par défaut, l’éditeur de liens génère un fichier nommé <binary_name>.<extension>.manifest pour décrire le fichier binaire final. L’éditeur de liens peut incorporer un fichier manifeste à l’intérieur du fichier binaire en spécifiant l’option /MANIFEST:EMBED éditeur de liens.

Il existe plusieurs autres façons d’incorporer un manifeste à l’intérieur du fichier binaire final, comme l’utilisation de l’outil Manifeste (mt.exe) ou la compilation du manifeste dans un fichier de ressources. Vous devez suivre des règles spécifiques lorsque vous incorporez un manifeste pour activer des fonctionnalités telles que la liaison incrémentielle, la signature et la modification et continuer. Ces règles et d’autres options sont abordées dans la section suivante.

Comment incorporer un manifeste à l’intérieur d’une application C/C++

Nous vous recommandons d’incorporer le manifeste de votre application ou bibliothèque dans le fichier binaire final. Cette approche garantit un comportement d’exécution correct dans la plupart des scénarios. Par défaut, Visual Studio tente d’incorporer le manifeste lorsqu’il génère un projet. Toutefois, si vous générez votre application à l’aide de NMAKE, vous devez apporter des modifications au fichier makefile. Cette section montre comment modifier les makefiles afin qu’il incorpore automatiquement le manifeste à l’intérieur du fichier binaire final.

Deux approches

Il existe deux façons d’incorporer le manifeste à l’intérieur d’une application ou d’une bibliothèque.

  1. Si vous n’effectuez pas de build incrémentielle, vous pouvez incorporer directement le manifeste à l’aide d’une ligne de commande similaire à l’étape suivante :

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

    or

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

    Utilisez 1 pour un EXE et 2 pour une DLL.

  2. Si vous effectuez une build incrémentielle, procédez comme suit :

    • Lier le fichier binaire pour générer le MyApp.exe.manifest fichier.

    • Convertissez le manifeste en fichier de ressources.

    • Relinkez (de façon incrémentielle) pour incorporer la ressource de manifeste dans le fichier binaire.

Les exemples suivants montrent comment modifier les fichiers makefile pour incorporer les deux techniques.

Makefiles (avant)

Considérez le script NMAKE pour MyApp.exe, une application simple créée à partir d’un fichier :

# 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

Si ce script est exécuté sans modification avec Visual Studio, il crée MyApp.execorrectement . Il crée également le fichier MyApp.exe.manifestmanifeste externe, à utiliser par le système d’exploitation pour charger des assemblys dépendants au moment de l’exécution.

Le script NMAKE ressemble MyLibrary.dll à ceci :

# 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

Makefiles (après)

Pour générer avec des manifestes incorporés, vous devez apporter quatre petites modifications aux makefiles d’origine. Pour le 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.)

Pour le 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.)

Les makefiles incluent désormais deux fichiers qui effectuent le travail réel, makefile.inc et makefile.target.inc.

Créez et copiez makefile.inc le contenu suivant :

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

Créez et copiez makefile.target.inc maintenant le contenu suivant :

# 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

Voir aussi

Génération d’applications isolées et d’assemblys côte à côte C/C++
Concepts des applications isolées et des assemblys côte à côte
Résolution des problèmes liés aux applications isolées C/C++ et aux assemblys côte à côte
/INCREMENTAL (Lier de façon incrémentielle)
/MANIFEST (Créer un manifeste d’assembly côte à côte)
Assemblys de nom fort (signature d’assembly) (C++/CLI)
Modifier & Continuer