1. 程式人生 > >【轉】VS2008採用了新的程式部署技術(manifest清單檔案)

【轉】VS2008採用了新的程式部署技術(manifest清單檔案)

vc2005/vc2008採用了新的程式部署技術(manifest清單檔案),manifest清單檔案實際上類似於我們常用的makefile檔案,它定義了程式執行的依賴關係(程式執行所需要的dll庫的名稱、版本等)。

程式執行,首先根據manifest清單檔案(這個檔案可以嵌入到exe或dll中,或者單獨生成外部檔案,可以通過vc2005/vc2008的編譯選項控制:工程“屬性”->“配置屬性”->“清單工具”->“輸入輸出”->“嵌入清單檔案”,選擇“是”或“否”來控制)來查詢程式執行需要的dll庫的名稱、版本等,如果所在的系統中沒有程式執行所需要的dll庫和相應的manifest清單檔案,則彈出“應用程式配置不正確,程式無法啟動”對話方塊。

另外要注意,由於vc2005/vc2008與.net整合,導致出現一個新的概念:在.net中,將exe、dll都看成“程式集(assemble)”,每個程式集(assemble)都附帶有一個manifest清單檔案,因此使得vc2005/vc2008的CRT(C 執行時庫)、MFC、ATL等dll庫都附帶有一個manifest清單檔案。

歸根結底是由於老版本的系統沒有我們開發的程式執行所需要的基本執行時庫(2k、xp系統只有vc6的一些dll庫,而沒有vc2005、vc2008所需要的dll庫以及相應的manifest清單檔案,而在vista系統或者即將到來的windows 7系統上則包含有vc2005、vc2008的dll庫和manifest清單檔案)

ps:上面的那段話說的有點幼稚和簡單了,這裡涉及到很多的問題:程式的升級更新、vs的補丁、庫的版本問題等等,不是簡單的拷貝、貼上就能解決的。。。

舉個例子:(在XP SP3系統下) 

使用vc2008 express sp1版(沒有mfc、atl),新建一個“HelloWorld”的“win32控制檯應用程式”工程,在release下編譯,此時預設的編譯選項:(在這裡我們只關注與我們的問題相關的幾個選項)

1、工程“屬性”->“配置屬性”->“c/c++”->“程式碼生成”->“執行庫”

預設選項為/MD(release)、/MDd(debug),對這幾個編譯選項不清楚的可以參見: VC執行庫版本不同導致連結.LIB靜態庫時發生重複定義問題的一個案例分析和總結

2、工程“屬性”->“配置屬性”->“清單工具”->“輸入輸出”->“嵌入清單檔案”

預設選項為“是”(表示將manifest清單檔案嵌入到程式中);當然,我們也可以選擇“否”,從而單獨生成一個manifest清單檔案,不過這會增加不必要的依賴項,因此不建議選擇“否”。

編譯->連結之後在“ HelloWorld ”工程的release或debug目錄下,我們能夠看到一個HelloWorld.exe.intermediate.manifest清單檔案(根據編譯選項,見上,vc2008將manifest清單檔案嵌入到了exe程式中,HelloWorld.exe.intermediate.manifest清單檔案是一個臨時檔案,但它的內容與嵌入到exe程式的manifest檔案是一樣的),用文字編輯器開啟該檔案(用“記事本”也行,不過格式太亂,看不清楚內容,推薦使用vim或其它的文字編輯器檢視),大致內容如下:

ps:在網上看到另外的一個方法,用記事本開啟exe或dll程式,檢視嵌入到exe或dll中的manifest清單檔案,方法:“開啟記事本,然後將exe或dll拖入到記事本中,當然了,肯定會出現大段的亂碼,沒關係,直接往後看,就能發現類似於下面的內容的部分”

XML語言: HelloWorld.exe.intermediate.manifest

01 <?xml version='1.0' encoding='UTF-8' standalone='yes'?>
02 <assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
03 <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
04      <security>
05        <requestedPrivileges>
06          <requestedExecutionLevel level='asInvoker' uiAccess='false' />
07        </requestedPrivileges>
08      </security>
09 </trustInfo>
10 <dependency>
11      <dependentAssembly>
12        <assemblyIdentity type='win32' name='Microsoft.VC90.CRT' version='9.0.21022.8' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' />
13      </dependentAssembly>
14 </dependency>
15 </assembly>

我們重點檢視紅色部分,這說明編譯後的exe程式依賴於vc90(也即vc2008)的CRT(C執行時庫),版本9.0.210022.8(這是由於使用/MD選項,程式動態的依賴於CRT,如果使用/MT選項,則會將CRT靜態連結到程式中,當然,這會使程式的尺寸急劇的增長,大概有10倍的大小差距)

當exe程式執行時,它會根據嵌入的manifest檔案查詢相應的依賴項,在這個HelloWorld.exe程式中,它依賴於vc90 CRT,因此它會在“C:\WINDOWS\WinSxS”和“當前目錄”下查詢相應的dll庫以及manifest檔案,(這裡指的是xp系統,不考慮vista系統,具體的參見:程式集搜尋順序)

在我的機器上有2個版本的vc90 CRT(由於安裝了vc2008 express sp1)

vc90 CRT的dll庫位於(9.0.21022.8版本)“C:\WINDOWS\WinSxS\x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_x-ww_d08d0375”

相應的manifest檔案則位於“C:\WINDOWS\WinSxS\Manifests\x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_x-ww_d08d0375.manifest”

vc90 CRT的dll庫位於(9.0.30729版本)“C:\WINDOWS\WinSxS\x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.30729.1_x-ww_6f74963e”

相應的manifest檔案則位於“C:\WINDOWS\WinSxS\Manifests\x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.30729.1_x-ww_6f74963e.manifest”

在這裡我們就有一個疑問了,我們的開發環境是vc2008 express sp1,那麼我們的程式連結的CRT版本應該是9.0.30729版本的啊?(這個不是我瞎說的,大家可以用dependency walker來檢視程式實際連結的DLL版本),為什麼在manifest檔案中依賴的CRT卻是9.0.21022.8版本的?這裡就涉及到一個新的名詞“policy ",作業系統會根據C:\WINDOWS\WinSxS\Policies\x86_policy.9.0.Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_x-ww_b7353f75\9.0.30729.1.policy檔案的內容,進行dll版本的跳轉(重點看深藍斜體字部分)從而選擇了9.0.30729版本的vc90 CRT (這個所謂的“policy跳轉”是道聽途說來的,具體的英文資料藏在microsoft的什麼地方我就不得而知了。裡面夾帶了一些我自己的主觀猜測,不然的話,沒有辦法解釋manifest版本號9.0.21022.8是,而實際連結的dll的版本號卻是9.0.30729)

XML語言: 9.0.30729.1.policy01 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
02 <!-- Copyright (c) Microsoft Corporation. All rights reserved. -->
03 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
04     <assemblyIdentity type="win32-policy" name="policy.9.0.Microsoft.VC90.CRT" version="9.0.30729.1" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"/>
05     <dependency>
06         <dependentAssembly>
07             <assemblyIdentity type="win32" name="Microsoft.VC90.CRT" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"/>
08             <bindingRedirect oldVersion="9.0.20718.0-9.0.21022.8" newVersion="9.0.30729.1"/>
09             <bindingRedirect oldVersion="9.0.30201.0-9.0.30729.1" newVersion="9.0.30729.1"/>
10         </dependentAssembly>
11     </dependency>
12 </assembly>

如果我們將這個HelloWorld.exe拷貝到其它的機器上(沒有安裝vc2008 sp1或Microsoft Visual C++ 2008 SP1 Redistributable Package (x86)),則程式因為沒能找到vc90 CRT,而不能執行,彈出“應用程式配置不正確,程式無法啟動”對話方塊。

根據參考資料的文章中的內容,對於release版程式,有一個簡單的辦法就是安裝“vcredist_x86.exe”,檔案大小4M左右,自動安裝在“C:\WINDOWS\WinSxS”目錄下,包含了CRT、MFC、ATL等庫的dll和manifest清單檔案;整個安裝時間不到1分鐘。

如果機器上安裝了vc2005/vc2008,則會自動的安裝vcredist_x86.exe程式,安裝後在“控制面板”->“新增刪除程式”中有一項“Microsoft Visual c++ 2008 Redistributable - x86 9.0.3.729”(我安裝的是Microsoft Visual C++ 2008 SP1 Redistributable Package (x86) 版本)

注意:要根據編譯器版本以及vc2005/vc2008是否安裝了sp1補丁進行選擇對應的vcredist.exe版本

來源:http://hi.baidu.com/fairysky/blog/item/e7a8366dbaa735f3431694c8.html

參考資料

1、VS2005解決"應用程式配置不正確,程式無法啟動"問題

2、VS2005安裝檔案 "由於應用程式配置不正確,應用程式未能啟動"

3、Microsoft Visual C++ 2008釋出程式的部署問題

4、VC編寫的程式不能在其他機器上執行的解決方案

新增(先看看上面的4個連結之後,遇到問題之後再看下面的幾個連結)

5、關於vs2008 sp1 C++生成的 manifest中執行庫版本號的問題 (推薦1)

6、在VC++2008的專案中,如何顯示地指定要使用的C++庫的版本? (推薦2)

7、VC9 SP1 generates manifests with the wrong version number

ps:有人認為這是一個bug,並彙報到ms網站上,但“推薦1”認為這不是一個bug

8、VC Runtime Binding...(ms的官方blog對這個問題的解釋)

關於VC執行時繫結(上面連結的中文翻譯)

9、部署 (C++)(推薦,比較難看懂)

關於連結9下幾個比較有用的連結:

程式集搜尋順序(英文),主要講的是CRT、MFC等的DLL和manifest檔案的部署方式

選擇部署方法

使用 Program Files\Microsoft Visual Studio 8\VC\Redist目錄中提供的檔案將特定 Visual C++程式集作為應用程式的私有程式集安裝。允許沒有管理員許可權的使用者安裝應用程式或可以通過共享執行應用程式時,建議使用這種方法。有關示例,請參見如何:使用 XCopy進行部署。(摘自:選擇部署方法)

總結如下:

使用vs2008/vs2008開發的程式有2種部署方法:共享並行程式集和私有程式集部署方法

所謂的共享並行程式集部署方法是指程式依賴的CRT、MFC、ATL的DLL和manifest檔案位於目標機器上的c:\windows\winsxs目錄中,釋出程式的時候只需要將程式拷貝到目標機器上就可以了;私有程式集部署方法指的是釋出程式程式的時候,將所依賴的crt、mfc、atl的dll放在程式的當前目錄下

對於release版程式

比較的簡單的方法是採用共享程式集的方式來部署,安裝vcredist.exe (Microsoft Visual C++ 2008 SP1 Redistributable Package (x86)

也可以採用下面debug程式的私有程式集的部署方法

對於debug版本程式

◆ 若目標機器安裝了VS開發環境(vs2005 sp1/vs2008 sp1),則在機器上同時也安裝了共享並行程式集,包含各個版本的dll(8.0、9.0版本,位於C:\Windows\Winsxs目錄下),則不需做任何的部署,直接將需要釋出的程式拷貝到目標機器上就可以了,這和release版程式的釋出方式是一樣的

◆ 在沒有安裝VS開發環境(安裝了vs2005 sp1/vs2008 sp1)的機器上,只能採用私有程式集的方式來部署(因為vcredist.exe只安裝了release版的CRT、MFC、ATL的DLL和manifest檔案,沒有對應的debug版本)

已知的2種方法:(針對vs2008 sp1,安裝了sp1之後,在系統上會存在兩個版本的CRT、MFC、ATL的DLL:9.0.21022.8和9.0.30729.1)

1、使當前程式的manifest檔案中的依賴項的版本號與vc安裝目錄下的redist目錄下的dll的版本一致,均為9.0.30729.1

方法:

a、在編譯專案時定義一個符號_BIND_TO_CURRENT_VCLIBS_VERSION,該符號定義於C:\Program Files\Microsoft Visual Studio 9.0\VC\include\crtassem.h 檔案中(假設VC安裝在c盤),這樣使得編譯後的程式的manifest依賴於CRT 9.0.30729.1版本(同樣的,對於MFC也應該定義一個類似的符號,大家可以自己在VC的include目錄下搜尋“9.0.30729.1”或“9.0.21022.8”,就可以找到對應的定義該符號的標頭檔案)

b、通過外部工具修改生成的exe或dll中manifest檔案(好像windows sdk中的mt.exe可以做到,不過關於這個工具的資料十分的少)

2、將VC安裝目錄下的redist目錄下(C:\Program Files\Microsoft Visual Studio 9.0\VC\redist)的Microsoft.VC90.CRT拷貝到要釋出的程式的當前目錄下,修改Microsoft.VC90.CRT目錄中的Microsoft.VC90.CRT.manifest檔案中的版本號,改成9.0.21022.8,這樣使得程式誤以為該目錄下的vc的dll版本是9.0.21022.8(實質上仍然是9.0.30729.1版本)

說明:

1、連結4 的說法是錯誤的,根據我自己的實驗,如果採用私有程式集的部署方法,必須保證manifest檔案中的版本號都是相等的,不存在要釋出的程式的manifest檔案中的版本號大於等於依賴項(CRT、MFC、ATL的dll)的版本號的說法

2、採用共享並行程式集部署方式釋出的程式,會自動根據所謂的“policy”(位於C:\WINDOWS\WinSxS\Policies目錄下)進行跳轉(由低版本號向高版本號跳轉);例如程式中的manifest的版本號為9.0.21022.8,而實際上程式是用vc2008 sp1編譯的(版本號為9.0.30729.1),在程式實際執行的時候,會根據

x86_policy.9.0.Microsoft.VC90.DebugCRT_1fc8b3b9a1e18e3b_x-ww_037be232目錄下的9.0.30729.1.policy檔案(可以用記事本開啟該檔案)中的內容選擇9.0.30729.1版本的debugCRT

我個人推薦的閱讀順序:① 先看前面的4個連結,大致有點印象,知道什麼是manifest、如何檢視manifest檔案的內容(能力強的話,也可以自己編寫manifest檔案)、在vc中如何檢視編譯過程中生成的manifest檔案內容、知道C:\WINDOWS\WinSxS\目錄是幹什麼的、知道vcredist.exe這個程式; ② 再嘗試著看看連結7、8、9,這些連結的文章內容十分的晦澀,有的還是英文的,需要有點耐心看; ③ 最後仔細的看看連結5、6,並多多試驗(特別推薦連結5,這個連結中的內容十分的詳細)