C/C++ プログラムのマニフェスト生成についての理解

マニフェストは、アセンブリを一意に識別する XML ドキュメントです。 これには、COM クラス、インターフェイス、タイプ ライブラリなど、バインドとアクティブ化に使用される情報が含まれています。 マニフェストには、外部 XML ファイル、またはアプリケーションまたはアセンブリ内に埋め込まれたリソースを指定できます。 分離アプリケーションの マニフェストは、アプリケーション が実行時にバインドする必要がある共有サイド バイ サイド アセンブリの名前とバージョンを管理するために使用されます。 side-by-side アセンブリのマニフェストによって、名前、バージョン、リソース、その他のアセンブリに対するその依存関係が指定されます。

分離アプリケーションまたは side-by-side アセンブリには 2 つの方法でマニフェストを作成できます。 まず、アセンブリの作成者は、規則と名前付けの要件に従ってマニフェスト ファイルを手動で作成できます。 詳細については、「マニフェスト ファイルリファレンス」を参照してください。 または、プログラムが CRT、MFC、ATL などの MSVC アセンブリのみに依存している場合、リンカーはマニフェストを自動的に生成できます。

MSVC ライブラリのヘッダーにはアセンブリ情報が含まれており、ライブラリがアプリケーション コードに含まれている場合、このアセンブリ情報はリンカーによって使用され、最終的なバイナリのマニフェストが形成されます。 既定では、リンカーはバイナリ内にマニフェスト ファイルを埋め込むことはありません。 外部ファイルとしてマニフェストを用意することは、シナリオによってはうまく機能しない場合があります。 たとえば、プライベート アセンブリにはマニフェストが埋め込まれたものにすることをお勧めします。 NMAKE を使用してコードをビルドするコマンド ライン ビルドでは、リンカー オプションを /MANIFEST:EMBED 使用してマニフェストを埋め込むことができます。 または、マニフェスト ツールを使用してマニフェストを埋め込むことができます。 詳細については、コマンド ラインでのマニフェストの生成を参照してください。 Visual Studio でビルドする場合は、次のセクションで説明するように、[プロジェクトのプロパティ] ダイアログでマニフェスト ツールのプロパティを設定することで、マニフェストを埋め込むことができます。

Visual Studio でのマニフェスト生成

プロジェクトの [プロパティ ページ] ダイアログで、特定のプロジェクトのマニフェスト ファイルを生成するように Visual Studio に指示できます。 [構成プロパティ] で、[リンカー>マニフェスト ファイル>のマニフェストの生成] を選択します。 既定では、新しいプロジェクトのプロジェクト プロパティはマニフェスト ファイルを生成するように設定されます。 ただし、プロジェクトのマニフェスト生成プロパティを使用して、プロジェクトの マニフェスト の生成を無効にすることはできます。 このプロパティを [はい] に設定すると、プロジェクトのマニフェストが生成されます。 それ以外の場合、リンカーは、アプリケーション コードの依存関係を解決するときにアセンブリ情報を無視し、マニフェストを生成しません。

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 を使用してアプリケーションをビルドする場合は、メイクファイルにいくつかの変更を加える必要があります。 このセクションでは、マニフェストを最終的なバイナリに自動的に埋め込むようにメイクファイルを変更する方法を示します。

2 つの方法

アプリケーションまたはライブラリにマニフェストを埋め込む方法は 2 つあります。

  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

    • マニフェストをリソース ファイルに変換します。

    • マニフェスト リソースをバイナリに埋め込むために (増分的に) 再リンクします。

次の例では、両方の手法を組み込むようにメイクファイルを変更する方法を示します。

メイクファイル (変更前)

1 つのファイルから構築された単純なアプリケーションの MyApp.exeNMAKE スクリプトを考えてみましょう。

# 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

メイクファイル (変更後)

埋め込みマニフェストを使用してビルドするには、元のメイクファイルに 4 つの小さな変更を加える必要があります。 メイクファイルの 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 を行う2つのファイルが 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 アセンブリのビルド
分離されたアプリケーションとサイド バイ サイド アセンブリの概念
C/C++ 分離アプリケーションとサイド バイ サイド アセンブリのトラブルシューティング
/INCREMENTAL (増分リンク)
/MANIFEST (サイド バイ サイド アセンブリ マニフェストを作成する)
厳密な名前アセンブリ (アセンブリ署名) (C++/CLI)
エディット コンティニュ