次の方法で共有


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

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

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

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

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

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

Visual Studio のビルド システムでは、マニフェストを最終バイナリ アプリケーション ファイルに組み込むか、外部ファイルとして生成できます。 この動作は、[プロジェクトのプロパティ] ダイアログの [埋め込みマニフェスト] オプションで制御されます。 このプロパティを設定するには、[マニフェスト ツール] ノードを開き、[入出力] を選択します。 マニフェストが埋め込まれていない場合は、外部ファイルとして生成され、最終的なバイナリと同じディレクトリに保存されます。 マニフェストが埋め込まれている場合、Visual Studio では、次のプロセスを使用して最終的なマニフェストが埋め込まれます。

  1. ソース コードがコンパイルされ、オブジェクト ファイルが生成されると、リンカーによって依存アセンブリ情報が収集されます。 最終的なバイナリをリンクしている間、リンカーは、後で最終的なマニフェストを生成するために使用される中間マニフェストを生成します。

  2. 中間マニフェストとリンクが完了すると、マニフェスト ツールによって最終的なマニフェストがマージされ、外部ファイルとして保存されます。

  3. 次にプロジェクト ビルド システムによって、マニフェスト ツールで生成されたマニフェストに、バイナリに既に埋め込まれているマニフェストとは異なる情報が含まれるかどうかが検出されます。

  4. バイナリに埋め込まれたマニフェストがマニフェスト ツールによって生成されたマニフェストと異なる場合、またはバイナリに埋め込みマニフェストが含まれていない場合、Visual Studio はリンカーをもう一度呼び出して、外部マニフェスト ファイルをリソースとしてバイナリ内に埋め込みます。

  5. バイナリに埋め込まれたマニフェストがマニフェスト ツールによって生成されたマニフェストと同じ場合、ビルドは次のビルド ステップに進みます。

マニフェストは、最終的なバイナリ内にテキスト リソースとして埋め込まれます。 これを表示するには、Visual Studio でファイルとして最終的なバイナリを開きます。 マニフェストが正しいライブラリを指していることを確認するには、「 Visual C++ アプリケーションの依存関係を理解するで説明されている手順に従います。 または、 トラブルシューティング 記事で説明されている推奨事項に従ってください。

コマンド ラインでのマニフェスト生成

NMAKE などのツールを使用してコマンド ラインから C/C++ アプリケーションをビルドすると、リンカーがすべてのオブジェクト ファイルを処理し、最終的なバイナリをビルドした後にマニフェストが生成されます。 リンカーによって、オブジェクト ファイルに保存されているアセンブリ情報が収集され、この情報が最終マニフェスト ファイルに結合されます。 既定では、リンカーは最終的なバイナリを記述する <binary_name>.<extension>.manifest という名前のファイルを生成します。 リンカーは、 /MANIFEST:EMBED リンカー オプションを指定することで、バイナリ内にマニフェスト ファイルを埋め込むことができます。

Manifest ツール (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.exe用の NMAKE スクリプトについて考えてみましょう。

# 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も作成します。

MyLibrary.dllの NMAKE スクリプトは次のようになります。

# 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.incmakefile.target.incを行う 2 つのファイルが含まれるようになりました。

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)
エディット コンティニュ