天天看點

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

<!-- Copyright (c) Microsoft Corporation. All rights reserved. -->

<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" />

然後将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>

我們重點檢視紅色部分,這說明編譯後的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)

<assemblyIdentity type="win32-policy" name="policy.9.0.Microsoft.VC90.CRT" version="9.0.30729.1" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"/>

<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"/>

如果我們将這個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

二者同時使用 _BIND_TO_CURRENT_VCLIBS_VERSION=1,但這個參數有問題最好不要用。

本文轉自feisky部落格園部落格,原文連結:http://www.cnblogs.com/feisky/archive/2009/11/26/1611595.html,如需轉載請自行聯系原作者

繼續閱讀