VC 2008問題: 應用程序無法啓動,因爲應用程序的並行配置不正確

VC9編譯的程序在沒有裝過VC9(確切的說是.Net Framework3.5)的機器上運行時,如果提示“由於應用程序配置不正確,應用程序未能啓動。重新安裝應用程序可能會糾正這個問題。”這個錯誤,那麼就說明該程序動態鏈接了VC9的運行時庫,(如果還用到了MFC,那麼可能動態鏈接了VC9的MFC庫,同理還有ATL庫),以及缺少對應的manifest文件,程序在目標機器上沒有找到這些庫和配置文件,因此導致了這個錯誤。出現這種情況的VC9編譯器可能存在3個版本,接下來分別闡明:

1、沒有打過任何補丁的VS2008

該版本對應的CRT/MFC/ATL庫的版本號爲9.0.21022.8,這個版本號在後面會用到。這個版本的程序部署比較簡單,直接把VC安裝目錄下的redist目錄(C:\ProgramFiles\Microsoft Visual Studio 9.0\VC\redist)中需要的庫以及對應的manifest文件拷貝到執行程序同目錄下,這樣程序到任何機器上都能夠正常運行了。

2、打過SP1補丁的VS2008

打過該補丁後,系統中存在着兩個版本的CRT/MFC/ATL庫,版本號分別爲9.0.21022.8和9.0.30729.1,這導致了manifest文件中記錄的版本號和實際庫的版本號不一致(程序要求它們的版本號一致才能運行)。這個版本的程序部署需要兩個步驟,首先要使manifest文件中依賴項的版本號與實際庫的版本號一致,均爲9.0.30729.1,方法是在工程設置中增加一個宏定義_BIND_TO_CURRENT_VCLIBS_VERSION,該宏定義於C:\ProgramFiles\Microsoft Visual Studio 9.0\VC\include\crtassem.h文件中,然後重新編譯程序。接下來還是將VC安裝目錄下的redist目錄(C:\ProgramFiles\Microsoft Visual Studio 9.0\VC\redist)中需要的庫以及對應的manifest文件拷貝到執行程序同目錄下,然後修改manifest文件中依賴項的版本號爲9.0.21022.8,這樣使得程序誤以爲該目錄下庫的版本號爲9.0.21022.8(實際上是9.0.30729.1版本),這樣程序到任何機器上都能夠正常運行了。

3、打過SP1補丁與SP1 ATL 安全更新 (KB973675)的VS2008

這是最新的更新。在SP1補丁之後,微軟又於近日發佈了一個用於智能設備的 Microsoft Visual Studio 2008 Service Pack 1 ATL 安全更新 (KB973675), 該補丁又將CRT/MFC/ATL庫的版本號升級,爲9.0.30729.4148,這次升級比較好,manifest文件與庫的版本號一致了,不像SP1一樣升級的不徹底。這樣只需要在工程設置中增加一個宏定義_BIND_TO_CURRENT_VCLIBS_VERSION,接下來重新編譯程序,然後直接把VC安裝目錄下的redist目錄中需要的庫以及對應的manifest文件拷貝到執行程序同目錄下,這樣程序到任何機器上都能夠正常運行了。

順便提一下,如果不想在發佈程序時帶上這些庫和manifest文件(如果沒有必要的話),那麼可以採用靜態編譯CRT和MFC,然後把manifest文件添加到資源中,這樣編譯出的程序只要一個exe就可以在任何機器上直接運行了。

參考文章:

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

有的時候,你在Visual C++上面經過好幾個月的辛勤努力,終於將程序編寫完成並且測試完畢,然而當你試圖在客戶的發佈機上運行剛寫好的程序時,有可能會碰到類似下面的錯誤,操作系統告訴你“由於應用程序配置不正確,應用程序未能啓動。重新安裝應用程序可能會糾正這個問題”.

一般情況下,這個問題都是由於程序不能找到所需要的C運行庫(CRT)而引起的。

在Windows XP SP2以後,Windows引入了Side-by-Side執行的概念,這個概念本來是.NET提出來的,但是Windows後來將這個概念集成到操作系統層面上來了。大家都應該知道DllHell的問題,爲了解決DllHell的問題,Side-By-Side提出不同版本的dll文件可以同時存在於同一個系統裏面,而且依賴於不同版本dll的應用程序在運行的時候可以使用到它當初被編譯生成的dll。前面的話,有點繞,舉個例子:

1.         假定你編寫了一個C++程序A,是使用MFC 8.0(這個版本是隨着Visual Studio 2005)發佈的。

2.         之後你的機器升級了Visual Studio的版本,從2005升級到2008,2008的MFC庫是9.0版本的,這個時候你的操作系統裏面安裝了兩個版本的MFC,分別是8.0和9.0。

3.         你在Visual Studio 2008編寫了另外一個C++程序B,B依賴與MFC 9.0。

4.         如果你運行程序A的話,操作系統會將MFC 8.0加載到A的進程裏面。

5.         如果你這時同時運行程序B,操作系統會將MFC 9.0加載到B的進程裏面。這就是Side-by-side的執行概念。

 

操作系統之所以能夠這樣做,是因爲它在加載程序A和B之前,除了查看PE格式裏面A和B所依賴的Dll信息,都會查看A和B的manifest文件。Manifest文件保存了Windows可執行文件(包括exe和dll文件)要運行起來的環境設置信息,文件名一般是可執行文件的文件全名加上.manifest。例如notepad.exe的manifest文件就應該是notepad.exe.manifest。例外有的程序將manifest文件直接嵌入到可執行文件的資源裏面了,這也就是爲什麼有的時候你看不到程序的manifest文件的原因。通常來說,一個manifest文件的內容如下(test.exe.manifest文件):

 

<?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.DebugCRT' version='9.0.21022.8'

                        processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' />

    </dependentAssembly>

</dependency>

</assembly>

上面的例子裏面,就說明這個程序依賴於CRT 9.0,而且是調試版的,CPU架構是32位的CPU。對於將manifest文件嵌入到資源文件的程序我們也有辦法看到manifest的信息。

1.         一種是使用mt.exe(Visual Studio自帶的manifest處理程序):

mt -inputresource:test.exe;#1 /out:test.manifest

2.         另外一種是使用dumpbin程序將整個exe的內容打印到一個文件,然後用文本編輯器打開,搜索Assem字符串樣式就能找到manifest信息:

 

解決方案

知道了程序依賴於具體哪一個dll以後,你可以將所依賴的dll拷貝到程序的安裝文件夾裏面,以CRT庫綁定失敗爲例,介紹解決步驟:

1.從上例中我們知道程序依賴的Microsoft.VC90.DebugCRT庫,版本號是9.0.21022.8,需要32位機器版本的CRT。這個依賴項一般是因爲你的程序是調試版,所以Visual Studio在編譯的時候,將調試版的CRT加入程序的依賴項。

2.從Visual Studio的安裝文件夾裏面將D:"Program Files"Microsoft Visual Studio9.0"VC"redist"Debug_NonRedist"x86中的Microsoft.VC90.DebugCRT整個文件夾拷貝到應用程序所在的文件夾裏面,注意:

a)如果你的程序依賴的是32位的CRT,則要拷貝x86文件夾裏面的Microsoft.VC90.DebugCRT文件夾,如果是先x64程序,則要拷貝x64文件夾裏面。

b)你需要確定Microsoft.VC90.DebugCRT文件夾裏面的Microsoft.VC90.DebugCRT.manifest文件裏面保存的版本信息而你程序依賴的版本信息匹配,Microsoft.VC90.DebugCRT.manifest裏面的版本信息大版本號一定要一致,小版本號一定要等於或者大於你程序依賴的CRT的小版本號。比如上例中,我們的程序是依賴於CRT 9.0.21022.8,而我們的Microsoft.VC90.DebugCRT.manifest的版本是9.0.30729.1,這樣是可以的;而8.0.30729.1就會有問題。如果大版本號一樣,小版本號不一致的話,一個比較簡單的方案就是修改程序的manifest文件,使其互相匹配就可以了。

3.如果你的程序不是依賴調試版本的CRT,而是release版本的CRT,直接去微軟的官方網站下載一個crt redist包安裝上就可以了。

 

附:解決方案參考:

方案一:


方法一:
在C:\Program Files\Microsoft Visual Studio 8\VC\redi
st\Debug_NonRedist\x86\Microsoft.VC80.DebugCRT 下找到了下列文件:

msvcm80d.dll
msvcp80d.dll
msvcr80d.dll
Microsoft.VC80.DebugCRT.manifest

把這幾個文件拷貝到目標機器上,與運行程序同一文件夾或放到system32下,就可以運行那個程序了。

其他release版,MFC程序什麼的都是拷redist下相應文件夾下的文件就可以了,文件夾後都有標識!

方法二:
修改編譯選項,將/MD或/MDd 改爲 /MT或/MTd,這樣就實現了對VC運行時庫的靜態鏈接,在運行時就不再需要VC的dll了。

方法三:

工程-》屬性-》配置屬性-》常規-》MFC的使用,選擇"在靜態庫中使用mfc"
這樣生成的exe文件應該就可以在其他機器上跑了。

方法四:

你的vc8安裝盤上找到再分發包vcredist_xxx.exe和你的程序捆綁安裝

 

我逐一測試下來,直到第三個方法才成功.第二個方法不知道在哪裏修改編譯選項所以放棄了,第四個方法不喜歡,這跟直接安裝.netframework 2.0 有什麼區別嗎?還不如直接安裝.netframework 2.0 呢.

 

     方案二:

最早出現這個錯誤我和許多人認爲的一樣
認爲是缺乏DLL庫文件導致.但是在測試機複製了DLL甚至安裝了.net framework 2.0以後
都無法解決問題,最後確認不是由缺乏DLL所致
因爲程序是純win32的應用程,非託管代碼,所以也無需.net framework

Visual C++2003/2005默認的MFC程序是使用動態MFC庫(Use MFC in a Shared DLL)來鏈接的
而動態MFC庫使用的是Multi-threaded DLL(/MD)
由於XP對於PE文件格式監測更加嚴格.
就會導致部分使用多線程DLL的可執行文件在調用的時候出錯
修改項目屬性的編譯開關
Project->Property->configuration Properties->C/C++->CodeGeneration->Runtime Library
修改成Multi-threaded (/MT)
修改了Runtime類型以後
需要將MFC的編譯類型也改成靜態庫
Project->Property->configuration Properties->General->Use of MFC
修改成Use MFC in a Static Library
一部分情況下在這步就能解決問題
另外一部分情況會遇見如下情況
編譯器報錯

nafxcw.lib(afxmem.obj) : error LNK2005: "void * __cdecl operatornew[](unsigned int)" (??_U@YAPAXI@Z) already defined inlibcpmt.lib(newaop.obj)

產生這個問題的原因是庫依賴關係
在Project->Property->configurationProperties->Linker->Command Line
加入編譯開關/verbose:lib可以顯示詳細的庫鏈接順序

------ Build started: Project: PerfMonDemo, Configuration: Release Win32 ------
Linking...
Searching libraries
Searching d:\Program Files\Microsoft Visual Studio8\VC\PlatformSDK\lib\pdh.lib:
Searching d:\Program Files\Microsoft Visual Studio 8\VC\lib\DelayImp.lib:
.................
Searching d:\Program Files\Microsoft Visual Studio 8\VC\atlmfc\lib\nafxcw.lib:
Finished searching libraries
.\Release/PerfMonDemo.exe : fatal error LNK1169: one or more multiply definedsymbols found
Build log was saved at "file://d:\Dev\PerformanceMonitor\Release\BuildLog.htm"
PerfMonDemo - 2 error(s), 0 warning(s)
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

我們發現在libcpmt.lib聲明過的operatornew在nafxcw.lib中再次定義
解決方法如下
Project->Property->configurationProperties->Linker->Input->Additional Dependencies
加入
nafxcw.lib
libcpmt.lib
Project->Property->configurationProperties->Linker->Input->Ignore Specific Library
加入
nafxcw.lib
libcpmt.lib
這樣鏈接程序就不會先按照默認順序來連接這兩個庫文件
而是在最後在加入對他們的引用.這樣就避免了這個問題
下面是一張可能發生衝突的列表
若要使用此運行時庫 請忽略這些庫
單線程 (libc.lib) libcmt.lib、msvcrt.lib、libcd.lib、libcmtd.lib、msvcrtd.lib
多線程 (libcmt.lib) libc.lib、msvcrt.lib、libcd.lib、libcmtd.lib、msvcrtd.lib
使用 DLL 的多線程 (msvcrt.lib)libc.lib、libcmt.lib、libcd.lib、libcmtd.lib、msvcrtd.lib
調試單線程 (libcd.lib) libc.lib、libcmt.lib、msvcrt.lib、libcmtd.lib、msvcrtd.lib
調試多線程 (libcmtd.lib) libc.lib、libcmt.lib、msvcrt.lib、libcd.lib、msvcrtd.lib
使用 DLL 的調試多線程 (msvcrtd.lib)libc.lib、libcmt.lib、msvcrt.lib、libcd.lib、libcmtd.lib

 

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