Manifestgenerierung für C/C++-Programme

Ein Manifest ist ein XML-Dokument, das eine Assembly eindeutig identifiziert. Sie 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 in eine Anwendung oder eine Assembly eingebettet ist. Das Manifest einer isolierten Anwendung wird verwendet, um die Namen und Versionen gemeinsam genutzter 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 die Regeln und Benennungsanforderungen eingehalten werden. Weitere Informationen finden Sie unter Referenz zu Manifestdateien. Wenn ein Programm nur von MSVC-Assemblys wie CRT, MFC, ATL oder anderen abhängt, kann der Linker automatisch ein Manifest generieren.

Die Header von MSVC-Bibliotheken enthalten Assemblyinformationen, und wenn die Bibliotheken im Anwendungscode enthalten sind, werden diese Assemblyinformationen vom Linker verwendet, um ein Manifest für die endgültige Binärdatei zu bilden. Standardmäßig bettet der Linker die Manifestdatei nicht in die Binärdatei ein. 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, z. B. mit NMAKE zum Erstellen von Code, können Sie die /MANIFEST:EMBED Linkeroption verwenden, um das Manifest einzubetten. Alternativ kann ein Manifest mithilfe des Manifesttools eingebettet werden. Weitere Informationen finden Sie unter "Manifestgenerierung" in der Befehlszeile. Wenn Sie in Visual Studio erstellen, kann ein Manifest eingebettet werden, indem eine Eigenschaft für das Manifesttool im Dialogfeld "Projekteigenschaften " festgelegt wird, wie im nächsten Abschnitt beschrieben.

Manifestgenerierung in Visual Studio

Sie können Visual Studio anweisen, eine Manifestdatei für ein bestimmtes Projekt im Dialogfeld "Eigenschaftenseiten" des Projekts zu generieren. Wählen Sie unter "Konfigurationseigenschaften" die Option "Linker>Manifestdatei>generieren" aus. Standardmäßig werden die Projekteigenschaften neuer Projekte so festgelegt, dass eine Manifestdatei generiert wird. Es ist jedoch möglich, die Generierung des Manifests für ein Projekt mithilfe der Generate Manifest-Eigenschaft 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 es als externe Datei generiert und im selben 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 es 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 vom Manifesttool generierten Manifest unterscheidet oder die Binärdatei kein eingebettetes Manifest enthält, ruft Visual Studio den Linker ein weiteres Mal auf, um die externe Manifestdatei in die Binärdatei als Ressource einzubetten.

  5. Wenn das in die Binärdatei eingebettete Manifest mit dem vom Manifesttool generierten Manifest übereinstimmt, wird der Build mit den nächsten Buildschritten fortgesetzt.

Das Manifest wird in die endgültige Binärdatei als Textressource eingebettet. Sie können sie 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 unter "Grundlegendes zu den Abhängigkeiten einer Visual C++-Anwendung" beschriebenen Schritte aus. Oder folgen Sie den Im Artikel zur Problembehandlung beschriebenen Vorschlägen.

Manifestgenerierung über die Befehlszeile

Wenn Sie C/C++-Anwendungen über die Befehlszeile mit 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 mit dem Namen <binary_name>.<extension>.manifest zur Beschreibung der endgültigen Binärdatei. Der Linker kann eine Manifestdatei in die Binärdatei einbetten, indem die /MANIFEST:EMBED Linkeroption angegeben wird.

Es gibt mehrere andere Möglichkeiten zum Einbetten eines Manifests in die endgültige Binärdatei, z. B. die Verwendung des Manifesttools (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 Fortfahren zu aktivieren. Diese Regeln und andere Optionen werden im nächsten Abschnitt erläutert.

So wird's gemacht: Einbetten eines Manifests in eine C/C++-Anwendung

Es wird empfohlen, das Manifest Ihrer Anwendung oder Bibliothek in die endgültige Binärdatei einzubetten. Dieser Ansatz garantiert das korrekte 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 als Postbuildschritt ä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, führen Sie die folgenden Schritte aus:

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

    • Konvertieren Sie das Manifest in eine Ressourcendatei.

    • Verknüpfen Sie die Manifestressource erneut (inkrementell), um die Manifestressource in die Binärdatei einzubetten.

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.exeeine 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 mit Visual Studio unverändert ausgeführt wird, wird es erfolgreich erstellt MyApp.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 nun zwei Dateien, die die eigentliche Arbeit erledigen, makefile.inc und makefile.target.inc.

Erstellen und kopieren Sie makefile.inc den folgenden Inhalt darin:

# 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 Sie nun den folgenden Inhalt, und kopieren Sie ihn 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

Siehe auch

Erstellen von isolierten Anwendungen und parallelen Assemblys (C/C++)
Konzepte isolierter Anwendungen und nebeneinander angeordneter Assemblys
Problembehandlung bei isolierten C/C++-Anwendungen und parallelen Assemblys
/INCREMENTAL (Inkrementelle Verknüpfung)
/MANIFEST (Erstellen des parallelen Assemblymanifests)
Assemblys mit starkem Namen (Assemblysignatur) (C++/CLI)
Bearbeiten und Fortfahren