vs2008 sp1 C++ 發佈程序

關於VC運行時綁定

寫在譯文之前:許多人都在納悶爲啥VS2005編譯的程序需要安裝一個運行時,而2008的卻只需要安裝2005的運行時呢?也許因此有了本文的誕生。

下面是內容:

原文作者是George Mileka,在VC team裏面寫庫的。原文鏈接:VC team blog

在四月發佈了Visual C++ 2008 Feature Pack之後我們收到了很多來自MVP和用戶的反饋。其中一項必須要澄清的問題是在嵌入程序的manifest文件中的依賴項版本的問題。

問題是這樣的: 爲什麼 VS team 提供瞭如下兩個選項:

    (1) 將程序綁定到RTM版本的VC運行時。

    (2) 將程序綁定到當前版本的VC運行時。

那麼,哪個是默認的行爲呢?

對於VS2005默認行爲(不受到SP1的影響)是場景2。

對於VS2008默認是場景1。

reference web:

http://liveck.spaces.live.com/blog/cns!B21C645F6C9CFD52!210.entry?wa=wsignin1.0&sa=935022575  

 

 

 

關於vs2008 sp1 C++ 生成的 manifest 中運行庫版本號的問題

一次偶然的發現vs2008 sp1生成manifest文件中vc++ runtime的版本號爲9.0.21022.8,而sp1的vc++ runtime的版本應該是9.0.30729.1。
google了一下,發現有人說這是vs2008 sp1的bug,並提交到ms的feedback:VC9 SP1 generates manifests with the wrong version number
然而今天查看了msdn,事實上這並不是一個bug。MSDN:重新分發應用程序並將其綁定到特定庫

其中默認綁定行爲有下面描述


本節介紹用 Visual C++ 2008 和更高發行版編譯的應用程序的默認行爲。在編譯應用程序時,它會綁定到可用庫的原始發行版。即使您的計算機上安裝了更高的發行版本,也是如此。例如,如果您的計算機上安裝了 Visual C++ 2008 SP1,則在此計算機上編譯的任何應用程序都仍將依賴 Visual C++ 2008 的原始發行版。


並附帶一個說明


如果您的應用程序依賴最新發行版中引入的功能,則必須分發並重新安裝新庫,否則應用程序將無法運行。


由此可以發現vs2008 sp1生成的manifest並不是錯誤的,而且從process explorer中查看應用程序調用的vc++ runtime的版本號你會發現實際上調用的是9.0.30729.1。至於原理請參考MSDN:Visual C++ Libraries as Shared Side-by-Side Assemblies
如果你想強制manifest中vc++ runtime爲9.0.30729.1
可以使用下面的代碼


如果希望強制應用程序要求在計算機上安裝最新的庫,請使用下面的一行或多行代碼:
#define _BIND_TO_CURRENT_CRT_VERSION 1;
#define _BIND_TO_CURRENT_ATL_VERSION 1;
#define _BIND_TO_CURRENT_MFC_VERSION 1;
#define _BIND_TO_CURRENT_OPENMP_VERSION 1;
藉助下面的代碼行,可以將應用程序綁定到所有 Visual C++ 庫的當前版本。此代碼行的效果是上面所有代碼行的效果之和。
#define _BIND_TO_CURRENT_VCLIBS_VERSION 1;
說明:
建議您在命令行級別定義這些宏。如果您更願意在代碼中包括這些定義,則必須將它們放在每個編譯單元的最前面,例如 stdafx.h 中的前幾行。
如果將應用程序綁定到一組庫,則在裝有這些庫的早期版本的計算機上,應用程序將無法運行。如果將應用程序綁定到由最少庫組成的特定版本,則建議您隨應用程序一起提供所需版本的庫。



不過有一個弊端,如果使用了vs2008編譯的不帶相關宏的靜態庫,這時manifest中會出現下面這種情況。只有定義相關的宏重新編譯所用到的lib纔會只有第一個Microsoft.VC90.CRT的描述。


  <dependency>
    <dependentAssembly>
      <assemblyIdentity type='win32' name='Microsoft.VC90.CRT' version='9.0.30729.1' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' />
    </dependentAssembly>
  </dependency>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type='win32' name='Microsoft.VC90.CRT' version='9.0.21022.8' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' />
    </dependentAssembly>
  </dependency>

 

 

reference web:

http://blog.squallatf.info/%E7%A8%8B%E5%BA%8F%E5%BC%80%E5%8F%91/%E5%85%B3%E4%BA%8Evs2008sp1c%E7%94%9F%E6%88%90%E7%9A%84manifest%E4%B8%AD%E8%BF%90%E8%A1%8C%E5%BA%93%E7%89%88%E6%9C%AC%E5%8F%B7%E7%9A%84%E9%97%AE%E9%A2%98/

 

 

 

 

VS2008部署問題

異常處理 2009-09-02 01:47:55 閱讀994 評論1 字號:

參考資料

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.exeMicrosoft 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版本)

reference web:

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

 

 

D:/發佈程序/Microsoft Visual C++ 2008 發佈程序的部署問題 - 我的文章 - 電腦工具箱.mht

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

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

這個問題有好多Blog和Forum已經討論過了,但都不詳盡,在具體的操作過程中還是有許多疑問。我摘錄並整合了許多網絡文章,希望能夠做個最終了結。

一、VC2005和VC2008編譯出來的程序如何發佈
http://www.cppblog.com/lf426/archive/2008/04/12/46885.aspx

VC2005和VC2008編譯出來的程序放到別人的電腦上爲什麼有可能無法運行呢?
1:Microsoft Visual C++ 2008 Express Edition可以發佈軟件嗎?
        能!
        很多人說,因爲是Express版,不是Studio,所以只是用來練習語言的,不能發佈軟件——錯!
        除了沒有MFC和ATL,基本上跟 .net 版本是一樣的。發佈出來的,是完整的可執行文件。

2:VC 2008 (2005) 發佈出來的程序必須附帶上他們特定的dll文件嗎?
        不一定。
        如果目標系統是個經常升級的系統,微軟已經爲其打上了所需要的dll文件補丁了,不需要在軟件包裏面附加特定的dll文件。特別在Vista系統中,你更是不需要VC8和VC9的dll文件。但是在一些老版本的系統中,這些文件就是必須的。

3:VC2008和VC2005特定的dll文件是哪些?

VC8: msvcm80.dll, msvcp80.dll, msvcr80.dll
VC9: msvcm90.dll, msvcp90.dll, msvcr90.dll

4:如何部署文件?

首先,請選擇release版本;在生成可執行文件(exe文件)的時候,會得到相應的部署文件(manifest文件)。比如,得到a.exe文件,就會同時生成a.exe.intermediate.manifest文件。請將這2個文件放在同一文件夾下。然後,你需要VC8和VC9的部署文件:Microsoft.VC80.CRT.manifest和Microsoft.VC90.CRT.manifest。請到你的VC安裝目錄下尋找,比如:
C:/Program Files/Microsoft Visual Studio 9.0/VC/redist/x86/Microsoft.VC90.CRT
我這裏也把2個部署文件直接貼出來,沒裝的直接用就是了:
Microsoft.VC80.CRT.manifest

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<noInheritable></noInheritable>
<assemblyIdentity type="win32" name="Microsoft.VC80.CRT" version="8.0.50727.762" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
<file name="msvcr80.dll" /> <file name="msvcp80.dll" /> <file name="msvcm80.dll" />
</assembly>


Microsoft.VC90.CRT.manifest

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- Copyright (c) Microsoft Corporation. All rights reserved. -->
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <noInheritable/>
    <assemblyIdentity
        type="win32"
        name="Microsoft.VC90.CRT"
        version="9.0.21022.8"
        processorArchitecture="x86"
        publicKeyToken="1fc8b3b9a1e18e3b"
    />
    <file name="msvcr90.dll" /> <file name="msvcp90.dll" /> <file name="msvcm90.dll" />
</assembly>


然後將VC8的3個dll以及這個manifest裝到一個文件夾裏,並將文件夾命名爲Microsoft.VC80.CRT。
同樣將VC9的3個dll以及這個manifest裝到一個文件夾裏,並將文件夾命名爲Microsoft.VC90.CRT。
將這2個文件夾放到與exe文件(及其部署文件)所在目錄下就OK了。

二、“應用程序配置不正確,程序無法啓動”的原因
http://hi.baidu.com/fairysky/blog/item/130dda13db7b050a5aaf53be.html

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清單文件)


舉個例子:(在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或其它的文本編輯器查看),大致內容如下:

<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
     <security>
       <requestedPrivileges>
         <requestedExecutionLevel level='asInvoker' uiAccess='false' />
       </requestedPrivileges>
     </security>
</trustInfo>
<dependency>
     <dependentAssembly>
       <assemblyIdentity type='win32' name='Microsoft.VC90.CRT' version='9.0.21022.8' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' />
     </dependentAssembly>
</dependency>
</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 version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- Copyright (c) Microsoft Corporation. All rights reserved. -->
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <assemblyIdentity type="win32-policy" name="policy.9.0.Microsoft.VC90.CRT" version="9.0.30729.1" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"/>
    <dependency>
        <dependentAssembly>
            <assemblyIdentity type="win32" name="Microsoft.VC90.CRT" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"/>
            <bindingRedirect oldVersion="9.0.20718.0-9.0.21022.8" newVersion="9.0.30729.1"/>
            <bindingRedirect oldVersion="9.0.30201.0-9.0.30729.1" newVersion="9.0.30729.1"/>
        </dependentAssembly>
    </dependency>
</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版本)

三、發生二個DLL版本錯亂的原因

http://blogs.msdn.com/vcblog/archive/2008/05/15/vc-runtime-binding.aspx
Virtual C++ 2008提供一個選項,可以選擇使用那個版本的DLL,是9.0.21022.8還是9.0.30729.1
1. 9.0.21022.8也稱RTM版本
2. 9.0.30729.1也稱Current當前版本
VC提供ploicy DLL版本策略轉發功能,可以將老版本的DLL請求轉發到新版本上,這意味着
1. 使用RTM版本的庫可以被指向RTM版本或更高版本
2. 使用當前版本的庫可以被指向當前版本或以後新出的版本

對於下面情況,一般使用RTM版本的庫
alan使用RTM版本的產品,併發布給客戶
MS釋放出VS SP1, 客戶安裝並使用了新版本的庫
alan發現他的產品有問題,修復並將其再次發給客戶
在這種情況下,alan不知道客戶使用的是那個版本的庫,他發佈的產品使用RTM版本的庫,如果有策略轉發,則使用新版本的庫

另外一種情況
jim使用RTM版本的庫開發
jim發現RTM版本庫中有bug,並將其提交給MS
MS修復,併發布新版本庫
jim使用新版本庫,工作正常。
jim的產品是否正常依賴於是否使用新版本的庫,如果不能正常使用,還不如不讓程序運行
因此我們提供多個版本庫,並向後兼容。

四、人工指定使用那個版本的庫

在編譯時,使用編譯選項
1. MFC使用 _BIND_TO_CURRENT_MFC_VERSION=1
見afx.h

2. CRT使用 _BIND_TO_CURRENT_CRT_VERSION=1
見 crtdefs.h 和 crtassem.h

總共有如下的設置
#define _BIND_TO_CURRENT_CRT_VERSION 1
#define _BIND_TO_CURRENT_ATL_VERSION 1
#define _BIND_TO_CURRENT_MFC_VERSION 1
#define _BIND_TO_CURRENT_OPENMP_VERSION 1

其實,我們可以用_BIND_TO_CURRENT_VCLIBS_VERSION=1 來代替以上四項。
但這個參數在可能會有問題

五、鏈接多種庫的情況
http://www.codeguru.com/cpp/v-s/devstudio_macros/visualstudionet/print.php/c15611

在現實中,一個應用程序需要鏈接多個庫,這要求所有的庫都使用同樣版本的VC DLL,否則會這樣

 

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel>
      </requestedPrivileges>
    </security>
  </trustInfo>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="Microsoft.VC90.CRT" version="9.0.30729.1" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
    </dependentAssembly>
  </dependency>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="x86" publicKeyToken="6595b64144ccf1df" language="*"></assemblyIdentity>
    </dependentAssembly>
  </dependency>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="Microsoft.VC90.CRT" version="9.0.21022.8" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
    </dependentAssembly>
  </dependency>
</assembly>



應用程序即依賴於9.0.30729.1的DLL,還依賴於9.0.21022.8版本的
如何判斷庫函數的版本呢,使用VC自帶的dumpbin

dumpbin /directives <name>.lib
會輸出如下信息,
 /manifestdependency:"type='win32'
 name='Microsoft.VC90.CRT'
 version='9.0.30729.1'
 processorArchitecture='x86'
 publicKeyToken='1fc8b3b9a1e18e3b'"
 /manifestdependency:"type='win32'

綜述,請確保應用程序,所有庫函數都使用相同版本的VC DLL



比較潦草,從不同的地方整理而來。不明白請看原文

 

 

 

 

說明本程序需要引用版本號爲9.0.21022.8的MFC庫和運行時庫。

問題一:爲什麼程序在運行的時候,載入的不是9.0.21022.8版本的卻是9.0.30729.1版本的運行庫?這是在哪裏進行設置的?如何改變這個版本號?

問題二:(雖然貌似已經解決,但是我想知道的更深一點)
佈署的時候,把X:/VS安裝目錄/VC/redist下對應的運行庫和.manifest文件(都是9.0.30729.1版本的)複製到與test1.exe同一目錄下。在一臺完全乾淨的新系統中,會出現“應用程序配置不正確,應用程序未能啓動”的錯誤。

後來,經過不斷的試驗,發現應用程序中的MANIFEST文件指定的是9.0.21022.8版本的運行庫,然後我使用9.0.21022.8版本的Microsoft.VC90.CRT.manifest文件替換9.0.30729.1版本的Microsoft.VC90.CRT.manifest,配合上30729版本的MFC90.DLL,程序就可以正常運行了。

如果以上說的還不清楚的話,說的簡單一點,就是,對於一個使用了新版本DLL功能的應用程序,有以下情況:
應用程序中的MANIFEST文件[使用了使用了新版本DLL的功能 卻指定了舊的版本號] + 新版本的運行庫MANIFEST文件 + 新版本的運行庫DLL --> 不可以運行
應用程序中的MANIFEST文件[使用了使用了新版本DLL的功能 卻指定了舊的版本號] + 舊版本的運行庫MANIFEST文件 + 舊版本的運行庫DLL --> 不可以運行
應用程序中的MANIFEST文件[使用了使用了新版本DLL的功能 卻指定了舊的版本號] + 舊版本的運行庫MANIFEST文件 + 新版本的運行庫DLL --> 可以運行

注:運行庫MANIFEST文件是指Microsoft.VC90.CRT.manifest和Microsoft.VC90.MFC.manifest,運行庫DLL是指MSVCR90.DLL MFC90.DLL

 

 

又做了個實驗:
程序的清單文件不要內嵌,而使用外置的MANIFEST文件,然後手工把生成的text1.exe.manifest中的version='9.0.21022.8'改成新版本號version='9.0.30729.1' ,就可以達到以下目標了:
應用程序 外置的MANIFEST文件[使用了使用了新版本DLL的功能 卻指定了的版本號] + 新版本的運行庫MANIFEST文件 + 新版本的運行庫DLL --> 可以運行!

不過有個外置的文件,確實不爽啊。。。。。

注意:在Microsoft  Vista下編譯的項目 在XP下有可能不能運行!!!

 

 

 

發佈了35 篇原創文章 · 獲贊 12 · 訪問量 43萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章