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

原文出處: http://hi.baidu.com/fairysky/blog/item/130dda13db7b050a5aaf53be.html

 

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

 

內容我就不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版本)

 

 

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