Manifestgenerierung für C/C++-Programme

Ein Manifest ist ein XML-Dokument, das eine Assembly eindeutig identifiziert. Es enthält Informationen, die für Bindung und Aktivierung verwendet werden, z. B. COM-Klassen, Schnittstellen und Typbibliotheken. Ein Manifest kann eine externe XML-Datei oder eine Ressource sein, die innerhalb einer Anwendung oder einer Assembly eingebettet ist. Das Manifest einer isolierten Anwendung wird verwendet, um die Namen und Versionen von freigegebenen nebenseitigen Assemblys zu verwalten, an die die Anwendung zur Laufzeit gebunden werden soll. Das Manifest einer parallelen Assembly gibt deren Abhängigkeiten von Namen, Versionen, Ressourcen und anderen Assemblys an.

Es gibt zwei Möglichkeiten, ein Manifest für eine isolierte Anwendung oder eine parallele Assembly zu erstellen. Zunächst kann der Autor der Assembly manuell eine Manifestdatei erstellen, indem Sie die Regeln und Benennungsanforderungen folgen. Weitere Informationen finden Sie unter Manifestdateienverweis. Alternativ hängt ein Programm nur von MSVC Assemblys wie CRT, MFC, ATL oder anderen ab, dann kann der Linker automatisch ein Manifest generieren.

Die Kopfzeilen von MSVC Bibliotheken enthalten Assemblyinformationen, und wenn die Bibliotheken im Anwendungscode enthalten sind, wird diese Assemblyinformationen vom Linker verwendet, um ein Manifest für die endgültige Binärdatei zu bilden. Standardmäßig wird der Linker die Manifestdatei nicht innerhalb der Binärdatei einbetten. Die Verwendung eines Manifests als externe Datei funktioniert möglicherweise nicht in allen Szenarios. Es wird beispielsweise empfohlen, dass private Assemblys eingebettete Manifeste aufweisen. In Befehlszeilenbuilds wie z. B. nmAKE zum Erstellen von Code können Sie die Linkeroption verwenden, um das /MANIFEST:EMBED Manifest einzubetten. Alternativ kann ein Manifest mithilfe des Manifesttools eingebettet werden. Weitere Informationen finden Sie in der Manifestgenerierung in der Befehlszeile. Wenn Sie in Visual Studio erstellen, kann ein Manifest eingebettet werden, indem Sie eine Eigenschaft für das Manifesttool im Dialogfeld Project Eigenschaften festlegen, wie im nächsten Abschnitt beschrieben.

Manifestgenerierung in Visual Studio

Sie können Visual Studio mitteilen, eine Manifestdatei für ein bestimmtes Projekt im Dialogfeld "Eigenschaftenseiten" des Projekts zu generieren. Wählen Sie unter Konfigurationseigenschaften das Manifest " Linker>Manifest>generieren" aus. Standardmäßig werden die Projekteigenschaften neuer Projekte festgelegt, um eine Manifestdatei zu generieren. Es ist jedoch möglich, die Generierung des Manifests für ein Projekt mithilfe der Eigenschaft "Manifest generieren " des Projekts zu deaktivieren. Wenn diese Eigenschaft auf "Ja" festgelegt ist, wird das Manifest für das Projekt generiert. Andernfalls ignoriert der Linker Assemblyinformationen beim Auflösen von Abhängigkeiten des Anwendungscodes und generiert das Manifest nicht.

Das Buildsystem in Visual Studio ermöglicht, dass das Manifest in die endgültige binäre Anwendungsdatei eingebettet oder als externe Datei generiert wird. Dieses Verhalten wird durch die Option Manifest einbetten im Dialogfeld Projekteigenschaften gesteuert. Öffnen Sie den Knoten Manifesttool, und klicken Sie auf Eingabe und Ausgabe, um diese Eigenschaft festzulegen. Wenn das Manifest nicht eingebettet ist, wird sie als externe Datei generiert und imselben Verzeichnis wie die endgültige Binärdatei gespeichert. Wenn das Manifest eingebettet ist, bettet Visual Studio die endgültigen Manifeste mithilfe des folgenden Prozesses ein:

  1. Nach der Kompilierung des Quellcodes in Objektdateien sammelt der Linker abhängige Assemblyinformationen. Während sie die endgültige Binärdatei verknüpft, generiert der Linker ein Zwischenmanifest, das später zum Generieren des endgültigen Manifests verwendet wird.

  2. Nachdem das Zwischenmanifest und die Verknüpfung abgeschlossen sind, führt das Manifesttool ein endgültiges Manifest zusammen und speichert sie als externe Datei.

  3. Das Buildsystem des Projekts erkennt dann, ob das vom Manifesttool generierte Manifest andere Informationen enthält als das bereits in die Binärdatei eingebettete Manifest.

  4. Wenn das in die Binärdatei eingebettete Manifest vom Manifesttool unterscheidet oder die Binärdatei kein eingebettetes Manifest enthält, ruft Visual Studio den Linker einmal mehr auf, um die externe Manifestdatei in die Binärdatei als Ressource einzubetten.

  5. Wenn das in die Binärdatei eingebettete Manifest identisch ist wie das Manifest, das vom Manifesttool generiert wird, führt der Build die nächsten Buildschritte fort.

Das Manifest wird in die endgültige Binärdatei als Textressource eingebettet. Sie können es anzeigen, indem Sie die endgültige Binärdatei als Datei in Visual Studio öffnen. Um sicherzustellen, dass das Manifest auf die richtigen Bibliotheken verweist, führen Sie die schritte aus, die in der Beschreibung der Abhängigkeiten einer Visual C++-Anwendung beschrieben sind. Oder folgen Sie den im Artikel "Problembehandlung " beschriebenen Vorschlägen.

Manifestgenerierung über die Befehlszeile

Wenn Sie C/C++-Anwendungen aus der Befehlszeile mithilfe von NMAKE oder ähnlichen Tools erstellen, wird das Manifest generiert, nachdem der Linker alle Objektdateien verarbeitet und die endgültige Binärdatei erstellt hat. Der Linker erfasst die in den Objektdateien gespeicherten Assemblyinformationen und fasst sie in einer endgültigen Manifestdatei zusammen. Standardmäßig generiert der Linker eine Datei namens <binary_name>.<extension>.manifest zum Beschreiben der endgültigen Binärdatei. Der Linker kann eine Manifestdatei innerhalb der Binärdatei einbetten, indem Sie die /MANIFEST:EMBED Linkeroption angeben.

Es gibt mehrere andere Möglichkeiten, ein Manifest innerhalb der endgültigen Binärdatei einzubetten, z. B. das Manifesttool (mt.exe) oder das Kompilieren des Manifests in eine Ressourcendatei. Sie müssen bestimmte Regeln befolgen, wenn Sie ein Manifest einbetten, um Features wie inkrementelle Verknüpfungen, Signieren und Bearbeiten und Fortsetzen zu aktivieren. Diese Regeln und andere Optionen werden im nächsten Abschnitt erläutert.

Einbetten eines Manifests in eine C/C++-Anwendung

Es wird empfohlen, das Manifest Ihrer Anwendung oder Bibliothek innerhalb der endgültigen Binärdatei einzubetten. Dieser Ansatz garantiert das richtige Laufzeitverhalten in den meisten Szenarien. Standardmäßig versucht Visual Studio, das Manifest einzubetten, wenn ein Projekt erstellt wird. Wenn Sie ihre Anwendung jedoch mithilfe von NMAKE erstellen, müssen Sie einige Änderungen an der Makefile vornehmen. In diesem Abschnitt wird gezeigt, wie Sie das Makefile ändern, sodass das Manifest automatisch in die endgültige Binärdatei eingebettet wird.

Zwei Ansätze

Es gibt zwei Möglichkeiten, das Manifest in eine Anwendung oder eine Bibliothek einzubetten.

  1. Wenn Sie keinen inkrementellen Build ausführen, können Sie das Manifest direkt mit einer Befehlszeile einbetten, die dem folgenden nach dem Buildschritt ähnelt:

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

    oder

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

    Verwenden Sie 1 für eine EXE und 2 für eine DLL.

  2. Wenn Sie einen inkrementellen Build ausführen, verwenden Sie die folgenden Schritte:

    • Verknüpfen Sie die Binärdatei, um die MyApp.exe.manifest Datei zu generieren.

    • Konvertieren Sie das Manifest in eine Ressourcendatei.

    • Relink (inkrementell) zum Einbetten der Manifestressource in die Binärdatei.

In den folgenden Beispielen wird gezeigt, wie Makefiles geändert werden, sodass sie beide Methoden integrieren.

Makefiles (vorher)

Betrachten Sie das NMAKE-Skript für MyApp.exe, eine einfache Anwendung, die aus einer Datei erstellt wurde:

# 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

Wenn dieses Skript unverändert mit Visual Studio ausgeführt wird, wird es erfolgreich erstelltMyApp.exe. Außerdem wird die externe Manifestdatei MyApp.exe.manifesterstellt, die vom Betriebssystem zum Laden abhängiger Assemblys zur Laufzeit verwendet wird.

Das NMAKE-Skript sieht MyLibrary.dll ähnlich aus:

# 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 (nachher)

Um mit eingebetteten Manifesten zu erstellen, müssen Sie vier kleine Änderungen an den ursprünglichen Makefiles vornehmen. Für die 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.)

Für das 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.)

Die Makefiles enthalten jetzt zwei Dateien, die die eigentliche Arbeit ausführen, makefile.inc und makefile.target.inc.

Erstellen und Kopieren Sie makefile.inc den folgenden Inhalt in sie:

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

Erstellen makefile.target.inc und kopieren Sie nun den folgenden Inhalt in ihn:

# 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

Weitere Informationen

Erstellen von isolierten Anwendungen und parallelen Assemblys (C/C++)
Konzept der isolierten Anwendungen und der parallelen Assemblys
Problembehandlung bei isolierten Anwendungen und parallelen Assemblys (C/C++)
/INCREMENTAL (Inkrementelle Verknüpfung)
/MANIFEST (Erstellen von nebenseitigem Assemblymanifest)
Starke Name-Assemblys (Assemblysignierung) (C++/CLI)
Bearbeiten und Fortfahren