vs2008部署問題

轉自:http://tangxingqt.blog.163.com/blog/static/2771087220098214755269/

 

參考資料

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

2、VS2005安裝文件 "由於應用程序配置不正確,應用程序未能啓動"

3、Microsoft Visual C++ 2008發佈程序的部署問題

4VC編寫的程序不能在其他機器上運行的解決方案

新增(先看看上面的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種部署方法:共享並行程序集和私有程序集部署方法

所謂的共享並行程序集部署方法是指程序依賴的CRTMFCATLDLL和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.89.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,這個鏈接中的內容十分的詳細)

 

參考資料: 應用程序配置不正確,程序無法啓動 的解決方法資料收集

內容我就不copy了,大家可以自己去看,總來說產生這個問題的原因可以歸結如下:

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.policy

01 <?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版本

 

 

上述的解決辦法我稱之爲共享程序集部署方法,同樣的我們也可以採用私有程序集的部署方式來發布程序

Helloworld例子的私有程序集的部署方法:(針對release版本,仍然是採用上面的例子

,採用參考資料中提到的第2中私有程序集部署方法:將Microsoft.VC90.CRT目錄下的manifest文件的版本號修改爲9.0.21022.8

1、將編譯後的程序拷貝到一個目錄下,假定爲d:/helloworld

2、將vc安裝目錄下的redist/x86目錄下的Microsoft.VC90.CRT目錄拷貝到d:/helloworld(假定vs安裝在C:/Program Files/Microsoft Visual Studio 9.0,則vc安裝目錄爲C:/Program Files/Microsoft Visual Studio 9.0/VC)

3、將Microsoft.VC90.CRT目錄下的manifest文件的版本號修改爲9.0.21022.8用記事本打開修改

最終發佈程序的目錄結構

D:/helloworld

      |--helloworld.exe

      |--Microsoft.VC90.CRT

                     |--Microsoft.VC90.CRT.manifest

                    |--msvcm90.dll

                     |--msvcp90.dll

                    |--msvcr90.dll

這個時候,程序的manifest文件(已經內嵌到exe中了)依賴的vc90 CRT的版本號和Microsoft.VC90.CRT.manifest文件的版本號對應一致,都是9.0.21022.8但是要注意的是,我們的helloworld程序實際上依賴的vc90 CRT版本是9.0.30729版本這裏只是採用了一種欺騙的方法,因爲我們編譯時鏈接的CRT的版本是9.0.30729版本)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章