VS2005連接問題: LNK2005錯誤 :error LNK2005: _free 已經在 libcmtd.lib(dbgheap.obj) 中定義

之前常常遇到這個警告信息,由於運行並無什麼問題,因此也沒深究。可是耿耿於懷那個「 0 個錯誤,0 個警告」的成功提示,在網上搜了一下。原來問題出在默認庫的引用選擇上。

VS2008,項目——屬性——配置屬性——C/C++——代碼生成:他有/MT,/MTd,/Md,/MDd四個選項,你必須讓全部使用的庫都使 用相同的配置,不然就會有相應的提示,甚至可能會出現沒法解析的函數。有時咱們使用的庫不是本身能夠控制的,那麼就只能把工程屬性設置成河你使用的庫相同 的選項。

錯誤 1 error LNK2005: _free 已經在 libcmtd.lib(dbgheap.obj) 中定義         MSVCRT.lib

錯誤 2 error LNK2005: _malloc 已經在 libcmtd.lib(dbgheap.obj) 中定義     MSVCRT.lib

.....

若是有一堆的重定義錯誤發生在同一個lib中,並且跟它衝突的也是同一個lib,那麼這個兩個lib的功能應該是同樣的,能夠2選一,只要在「忽略特定的庫」內填入須要忽略的庫。

項目屬性-配置屬性-連接器-輸入-忽略特定的庫:libcmtd.lib

項目屬性-配置屬性-常規-MFC的使用:在共享 DLL 中使用 MFC

MSVCRT.lib 和libcmt.lib的衝突仍是比較常見的。

從錯誤信息能夠看出是msvcrt.lib和libcmt.lib庫中重複定義了__isctype等符號。爲何會出現這樣的問題呢?這就要從這兩個庫的做用提及了。

msvcrt.lib是VC中的Multithreaded DLL 版本的C運行時庫,而libcmt.lib是Multithreaded的運行時庫。在同一個項目中,全部的源文件必須連接相同的C運行時庫。若是某一文 件用了Multithreaded DLL版本,而其餘文件用了Single-Threaded或者Multithreaded版本的庫,也就是說用了不一樣的庫,就會致使這個警告的出現。

告警信息的意思咱們明白以後,就要找形成這個問題的緣由了。在項目設置中咱們能夠看到當前項目使用的是Multithreaded非DLL版本的運 行時庫,這說明項目中還有其餘文件用到了不是這個版本的運行時庫。很顯然,就是openssl的靜態庫。查看openssl中ms下的nt.mak,咱們 能夠發現靜態庫版本中openssl使用編譯開關/MD進行編譯的,也就是說openssl靜態庫是默認用的Multithreaded DLL 版本的C運行時庫。

緣由找到了。那麼解決方法,很明顯有兩個。總之就是將兩個項目的運行時庫統一。

簡單的方式就是將項目的動態庫修改成使用Multithreaded DLL 版本的C運行時庫便可。

某些狀況下你的項目可能不能改變當前的運行時庫,你能夠將openssl的nt.mak中的/MD開關修改成/MT而後從新編譯openssl靜態庫就能夠了。

默認庫「library」與其餘庫的使用衝突;請使用 /NODEFAULTLIB:library LNK4098 的解決辦法

您試圖與不兼容的庫連接。

注意

運行時庫如今包含可防止混合不一樣類型的指令。若是試圖在同一個程序中使用不一樣類型的運行時庫或使用調試和非調試版本的運行時庫,則將收到此警告。例如,如 果編譯一個文件以使用一種運行時庫,而編譯另外一個文件以使用另外一種運行時庫(例如單線程運行時庫對多線程運行時庫),並試圖連接它們,則將獲得此警告。應 將全部源文件編譯爲使用同一個運行時庫。有關更多信息,請參見使用運行時庫(/MD、/MT 和 /LD)編譯器選項。

可使用連接器的 /VERBOSE:LIB 開關來肯定連接器搜索的庫。若是收到 LNK4098,並想建立使用如單線程、非調試運行時庫的可執行文件,請使用 /VERBOSE:LIB 選項肯定連接器搜索的庫。連接器做爲搜索的庫輸出的應是 LIBC.lib,而非 LIBCMT.lib、MSVCRT.lib、LIBCD.lib、LIBCMTD.lib 和 MSVCRTD.lib。對每一個要忽略的庫可使用 /NODEFAULTLIB,以通知連接器忽略錯誤的運行時庫。

下表顯示根據要使用的運行時庫應忽略的庫。

若要使用第一行運行時庫    請忽略第2行的這些庫

單線程 (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

例如,若是收到此警告,並但願建立使用非調試、單線程版本的運行時庫的可執行文件,能夠將下列選項與連接器一塊兒使用:

/NODEFAULTLIB:libcmt.lib /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrtd.lib

 寫進vs205\vs2008中 用分號隔開便可

 ======================================================================================================================

 

 

C2005編譯出來的程序文件,採用了manifest方式來指定dll文件。對於win9八、win2000系統,把exe文件和VC的 dll鏈接庫放到一塊兒就成了。對於winxp、win2003系統就要麻煩的多了,VC的鏈接庫默認是被放到了winsxs目錄下,結果形成在這些系統上,直接拷貝exe文件,每每是不能運行(找不到msvcr80.dll、mfc80.dll文件等),或者在事件日誌中報錯。

解決方式:
方式1、在目標系統上安裝2005版vcredist_x86.exe。

方式2、直接拷貝VS8目錄下的VC \ redist \ x86 \  目錄下的 Microsoft.VC80.MFC、Microsoft.VC80.CRT、Microsoft.VC80.MFCLOC幾個文件夾,到exe所在的目錄下,目錄結構以下:
.\myapp.exe
.\myapp.dll
.\Microsoft.VC80.CRT\
.\Microsoft.VC80.MFC\
.\Microsoft.VC80.MFC\Microsoft.VC80.MFCLOC\
而後修改Microsoft.VC80.MFCLOC目錄下的Microsoft.VC80.MFCLOC.manifest文件,將其中的version="8.0.50727.42",修改成version="8.0.50608.0"。


方式二的目錄結構,在xp和2003下是沒有問題的,可是在win98/win2000中,由於exe和dll不在同一目錄下,就會出現找不到dll的問題。

有什麼更好的解決呢?呵呵,國外的一個大牛(http://blog.kalmbachnet.de)找到一絕招:

方式3、
  一、首先編譯myapp.exe的時候,在配置中,選擇生成單獨的manifest文件,如:myapp.exe.manifest。
  二、將myapp.exe、myapp.exe.manifest拷貝到一個目錄下
  三、將Microsoft.VC80.MFC、Microsoft.VC80.CRT、Microsoft.VC80.MFCLOC幾個目錄下的文件,都拷貝到myapp.exe所在的目錄下。
  四、將Microsoft.VC80.MFCLOC.manifest文件中的version="8.0.50727.42",修改成version="8.0.50608.0"。
  五、編輯myapp.exe目錄下的全部 .manifest文件,將文件中的publicKey鍵值刪除,通常是publicKeyToken="1fc8b3b9a1e18e3b"
  六、而後運行myapp.exe看看,嗯。

 

另,其餘解決方法:

最近用vc2005寫了一個程序,拷貝到其它機器上運行時,提示「因爲應用程序配置不正確,應用程序未能啓動。從新安裝應用程序可能會糾正這個問題。」。

以爲很奇怪,依賴的dll都有在,怎麼會提示錯誤呢。立刻上網用這個錯誤查了一下,大多數人說是編譯選項的問題,如下是摘自http://bbs.mscommunity.com/forums/ShowThread.aspx?PostID=52760的解答:

在項目屬性-〉配置屬性-〉c/c++ -〉代碼生成裏,有一個運行時庫, 
.net 05 的默認選項是 多線程DLL,把他改爲多線程便可。

後來我花了一些時間測試,發現也能夠不用修改編譯選項,只要將Program Files\Microsoft Visual Studio 8\VC\redist下相應平臺的Microsoft.VC80.MFC.manifest和Microsoft.VC80.CRT.manifest拷貝到應用程序目錄便可。若是你還用到了atl庫,則還要Microsoft.VC80.ATL.manifest。

總結:解決這個問題目前我所知道的有兩種方法,

1,修改項目屬性,靜態連接mfc庫(靜態連接時,會自動修改上面提到的多線程DLL爲多線程)。

2,帶上Microsoft.VC80.MFC.manifest和Microsoft.VC80.CRT.manifest。

 

其餘人回覆:

解決VC2005程序的一個運行錯誤「因爲應用程序的配置不正確,應用程序未能啓動,從新安裝應用程序可能會糾正這個問題」 
是否是報這個錯誤啊?若是是2000系統,則會提示錯誤:"沒有找到XXXXXX.dll,所以這個應用程序未能啓動。從新安裝應用程序可能會修復此問題" 
若是你沒使用到C#在裏面的話,有種方法能夠解決,我之前也遇到過這個老是,因此寫在個人博客裏,文章地址爲:http://www.busfly.cn/post/5.html 
在這裏也簡單的說下解決方法: 
工程-》屬性-》配置屬性-》常規-》MFC的使用,選擇"在靜態庫中使用mfc" 
這樣生成的exe文件應該就能夠在其餘機器上跑了。

 

=============================================================================================

 

 

首先感謝這位幾仁兄的幾篇博客:

  http://hi.baidu.com/fairysky/blog/item/130dda13db7b050a5aaf53be.html

  http://hi.baidu.com/fairysky/blog/item/e7a8366dbaa735f3431694c8.html

  http://www.cppblog.com/lf426/archive/2008/04/12/46885.aspx

 

 時間原因:

 有時間再來整理:有點凌亂,很差意思。

 

 感謝的XCyber回覆:

  爲何這麼折騰呢?
  這樣看來,微軟發明manifest是錯誤的,由於你們都爲運行庫煩惱,真的還不如VC6,這可能嗎?
其實很簡單,打開你的vs,建立一個Setup and Development下的Setup Project項目,而後添加Merge Module,選擇你須要的運行庫,最後就是Build,生成的文件與你的程序一塊兒發佈就好了;
 

 

 

參考資料

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

二、VS2005安裝文件  "因爲應用程序配置不正確,應用程序未能啓動"

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

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

新增(先看看上面的4個連接以後,遇到問題以後再看下面的幾個連接)

5關於vs2008 sp1 C++生成的 manifest中運行庫版本號的問題 (推薦1)

六、在VC++2008的項目中,如何顯示地指定要使用的C++庫的版本? (推薦2)

七、VC9 SP1 generates manifests with the wrong version number

ps:有人認爲這是一個bug,並彙報到ms網站上,但「推薦1」認爲這不是一個bug

八、VC Runtime Binding...(ms的官方blog對這個問題的解釋)

關於VC運行時綁定(上面連接的中文翻譯)

九、部署  (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

一、使當前程序的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能夠作到,不過關於這個工具的資料十分的少)

二、將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版本)

 

說明:

一、連接4 的說法是錯誤的,根據我本身的實驗,若是採用私有程序集的部署方法,必須保證manifest文件中的版本號都是相等的,不存在要發佈的程序的manifest文件中的版本號大於等於依賴項(CRT、MFC、ATL的dll)的版本號的說法

二、採用共享並行程序集部署方式發佈的程序,會自動根據所謂的「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這個程序; ②  再嘗試着看看連接七、八、9,這些連接的文章內容十分的晦澀,有的仍是英文的,須要有點耐心看; ③ 最後仔細的看看鏈接五、6,並多多試驗(特別推薦連接5,這個連接中的內容十分的詳細)

 

 

 

 

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庫,而沒有vc200五、vc2008所需 要的dll庫以及相應的manifest清單文件,而在vista系統或者即將到來的windows 7系統上則包含有vc200五、vc2008的dll庫和manifest清單文件)

ps:上面的那段話說的有點幼稚和簡單了,這裏涉及到不少的問題:程序的升級更新、vs的補丁、庫的版本問題等等,不是簡單的拷貝、粘貼就能解決的。。。

 

 

 

 

舉個例子:(在XP SP3系統下)

 

使用vc2008 express sp1版(沒有mfc、atl),新建一個「HelloWorld」的「win32控制檯應用程序」工程,在release下編譯,此時默認的編譯選項:(在這裏咱們只關注與咱們的問題相關的幾個選項)

一、工程「屬性」->「配置屬性」->「c/c++」->「代碼生成」->「運行庫」

默認選項爲/MD(release)、/MDd(debug),對這幾個編譯選項不清楚的能夠參見: VC運行庫版本不一樣致使連接.LIB靜態庫時發生重複定義問題的一個案例分析和總結

二、工程「屬性」->「配置屬性」->「清單工具」->「輸入輸出」->「嵌入清單文件」

默認選項爲「是」(表示將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 sp1Microsoft Visual C++ 2008 SP1Redistributable 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

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

二、將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)

三、將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版本)

 

例子是針對release版,對於debug版本必需要採用私有程序的部署方法,這是拷貝的目錄就不是release版的Microsoft.VC90.CRT了,而是Microsoft.VC90.DebugCRT,方法同release版的是同樣的 

 

 

 Microsoft Visual C++ 2008 發佈程序的部署問題 生成Microsoft.VC90.CRT文件目錄結構:

VC2005和VC2008編譯出來的程序放到別人的電腦上爲何有可能沒法運行呢?
        這個問題無數人在問,可是很遺憾,沒有人給出完整的解釋和完美的解決方案。其實我也只有一臺電腦,並且裝了VC了,這個問題必需要臺沒有裝這類軟件的電腦 才容易去分析。感謝那些爲了測試我小程序的朋友,是大家一次次在如此惡劣的網絡速度下收取我一次次修改的dll包和部署文件,才讓這個問題的完美解決方案 浮出水面。這裏就把個人經驗給你們分享吧。

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了。
至於爲何VC9編譯的程序要用VC8的dll,你們能夠看看我例程部署文件:
<? 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 >
  
< dependency >
    
< dependentAssembly >
      
< assemblyIdentity  type ='win32'  name ='Microsoft.VC80.CRT'  version ='8.0.50727.762'  processorArchitecture ='x86'  publicKeyToken ='1fc8b3b9a1e18e3b'  />
    
</ dependentAssembly >
  
</ dependency >
</ assembly >
VC 2008生成出來就須要VC90和VC80的CRT,咱們能有什麼脾氣呢……
也就是說,還別管你exe文件多大,要保證正常運行,咱們須要首先拷貝這8個文件……
MinGW(gcc)編譯的就沒這些麻煩。因此,我如今都是用兩個編譯器編譯兩個exe以供發佈了。  

=====================================================================================================

 

 

1、MD(d)、MT(d)編譯選項的區別

一、編譯選項的位置

以VS2005爲例,這樣子打開:

1)         打開項目的Property Pages對話框

2)         點擊左側C/C++節

3)         點擊Code Generation節

4)         右側第六行Runtime Library項目

二、各個設置選項表明的含義

編譯選項

包含

靜態連接的lib

說明

/MD

_MT、_DLL

MSVCRT.lib

多線程、Release、DLL版本的運行時庫

/MDd

_DEBUG、_MT、_DLL

MSVCRTD.lib

多線程、Debug、DLL版本的運行時庫

/MT

_MT

LIBCMT.lib

多線程、Release版本的運行時庫

/MTd

_DEBUG、_MT

LIBCMTD.lib

多線程、Debug版本的運行時庫

 

簡單的說:

(1)/MD,表示運行時庫由操做系統提供一個DLL,程序裏不集成。

(2)/MT,表示運行時庫由程序集成。

 

2、/MD、/MT的選擇

      一、爲何選擇/MD,不選/MT?

         (1)程序就不須要靜態連接運行時庫,能夠減少軟件的大小;

         (2)全部的模塊都採用/MD,使用的是同一個堆,不存在A堆申請,B堆釋放的問題。

      二、爲何選擇/MT,不選擇/MD?

         (1)有些系統可能沒有程序所須要版本的運行時庫,程序必須把運行時庫靜態連接上。

(2)減小模塊對外界的依賴。

      三、多個模塊,必須選擇相同的運行時庫。

3、選擇/MT須要解決的堆空間釋放問題

         不一樣的模塊各自有一份C運行時庫代碼、或者根本沒有C運行時庫,致使了各個模塊會有各自的堆。若是在A堆中申請空間,到B堆中釋放就會有崩潰,在模塊A申請的空間,必須在模塊A中釋放。

         附錄的DLL以及DLLUser代碼,以STL的string爲例,經過修改編譯選項驗證了這個問題。string在賦值的時候須要釋放掉原來的空間,而後再申請新的空間存儲新的內容。

 

4、選擇/MD須要注意多個模塊使用不一樣版本運行時庫的問題

     (2012-9-17補充)

     多個dll被一個exe LoadLibrary加載,若是這些dll使用的運行時庫是不一樣的,那麼可能出現加載失敗,緣由多是舊版本的運行時庫已經在了,而某個dll它須要的是新版本的運行時庫,舊版本不符合要求。

     若是工程裏全部的模塊都是本身寫的或者能夠徹底控制的,那麼這個問題不難解決,只須要在工程設置裏都設置/MD,而後在相同的環境下編譯一次就行。可是假如這個模塊是外界提供的呢?

     可能存在這種狀況:A動態庫使用了B靜態庫,B靜態庫使用了C動態庫,B靜態庫是外界提供的,咱們要使用它,但沒法修改它,咱們也沒法接觸到C動態庫。若是C動態庫使用的運行時庫版本跟編譯A動態庫的本地使用的不一致,那麼A動態庫裏的嵌入信息就會記錄兩個不一樣版本的運行時庫,它被加載的時候,可能會選擇版本新的。假設A動態庫被一個exe LoadLibrary加載,而這個exe自己的運行時庫是舊的,這樣就會致使A動態庫加載失敗,即使把新的運行時庫拷貝到目錄下也不行,由於exe這個進程已經加載了那個舊的運行時庫。這時候必須使用manifest文件指定嵌入到A動態庫裏的運行時庫爲某個版本,忽略掉C動態庫使用的運行時庫版本。

     這個問題挺複雜的,我心思沒去驗證windows的PE文件加載會對運行時庫作什麼樣的優先選擇、運行時庫在靜態庫裏的記錄…。只要記住,給外界使用的版本避免使用/MD(會致使膨脹?)。

4、參考資料

一、微軟關於MT、MD等的詳細介紹

http://msdn.microsoft.com/en-us/library/2kzt1wy3(v=VS.71).aspx

二、不要出現A模塊申請,B模塊釋放的狀況

http://www.cnblogs.com/minggoddess/archive/2010/12/15/1907179.html

三、運行時庫有哪些版本

http://www.cppblog.com/MichaelLiu/articles/10607.html

四、CSDN上關於堆空間釋放的討論

http://topic.csdn.net/t/20010112/09/57983.html

http://topic.csdn.net/t/20031009/17/2338051.html

http://topic.csdn.net/u/20090502/00/bf1602e3-ddf5-49b0-af81-8a23383f9ddc.html

http://blog.csdn.net/blz_wowar/article/details/2176536

五、不一樣模塊不一樣的堆

http://www.cnblogs.com/WengYB/archive/2011/08/18/2144727.html

 

六、由於運行時庫版本問題致使加載失敗的分享

http://blog.csdn.net/dev_yarin/article/details/6768373

http://blog.163.com/henan_lujun/blog/static/19538333200611485511640/

 

附錄:

下載地址:http://files.cnblogs.com/cswuyg/Test_MD_and_MT.rar