共用方式為


了解 C/C++ 程式的資訊清單產生過程

指令清單是可唯一識別元件的 XML 檔。 它包含用於系結和啟用的資訊,例如 COM 類別、介面和類型連結庫。 指令清單可以是外部 XML 檔案或內嵌在應用程式或元件內的資源。 隔離應用程式的指令清單可用來管理應用程式應該在運行時間系結至之共用元件的名稱和版本。 並存元件的指令清單會指定其名稱、版本、資源和其他元件的相依性。

有兩種方式可以建立隔離應用程式或並存元件的指令清單。 首先,元件的作者可以遵循規則和命名需求,手動建立指令清單檔案。 如需詳細資訊,請參閱 指令清單檔案參考。 或者,如果程式只相依於CRT、MFC、ATL等 MSVC 元件,則連結器可以自動產生指令清單。

MSVC 連結庫的標頭包含元件資訊,以及當連結庫包含在應用程式程式代碼中時,連結器會使用此元件資訊來形成最終二進位檔的指令清單。 根據預設,連結器不會在二進位檔內嵌指令清單檔。 對於所有案例,將指令清單顯示為外部檔案可能無法運作。 例如,建議私人元件具有內嵌指令清單。 在命令列組建中,例如使用 NMAKE 來建置程式代碼的組建中,您可以使用 /MANIFEST:EMBED 連結器選項來內嵌指令清單。 或者,可以使用指令清單工具內嵌指令清單。 如需詳細資訊,請參閱 命令行上的指令清單產生。 當您在 Visual Studio 中建置時,可以在 [項目屬性] 對話框中設定指令清單工具的屬性來內嵌指令清單,如下一節所述。

在 Visual Studio 中產生資訊清單

您可以告訴 Visual Studio 在專案的 [屬性頁 ] 對話框中,為特定專案產生指令清單檔。 在 [組態屬性] 底下,選取 [鏈接器>指令清單檔>產生指令清單]。 根據預設,新專案的專案屬性會設定為產生指令清單檔。 不過,您可以使用專案的 Generate Manifest 屬性來停用產生專案的 指令清單 。 當此屬性設定為 [是] 時,會產生專案的指令清單。 否則,連結器會在解析應用程式程式碼的相依性時忽略元件資訊,而且不會產生指令清單。

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 建置應用程式,則必須對 makefile 進行一些變更。 本節說明如何變更 makefiles,讓它自動將指令清單內嵌在最終二進位檔內。

兩種方法

有兩種方式可將指令清單內嵌在應用程式或連結庫中。

  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 檔案。

    • 將指令清單轉換為資源檔。

    • 重新連結 (累加方式) 將指令清單資源內嵌至二進位檔。

下列範例示範如何變更makefiles以併入這兩種技術。

Makefiles (之前)

請考慮的 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

Makefiles (之後)

若要使用內嵌指令清單來建置,您必須對原始Makefiles進行四個小變更。 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.)

針對 MyLibrary.dll makefile:

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

makefiles 現在包含兩個執行實際工作的檔案, makefile.inc 以及 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++ 隔離應用程式和並存組件
隔離應用程式和並存元件的概念
針對 C/C++隔離的應用程式和並存元件進行疑難解答
/INCREMENTAL (以累加方式連結)
/MANIFEST (建立並存程式集清單)
強名稱元件 (元件簽署) (C++/CLI)
編輯後繼續