共用方式為


了解 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.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 ,供作業系統在執行時間載入相依元件。

的 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)
編輯後繼續