vc 编译的那些事儿 - wowolook的专栏 - CSDN博客
最近又被ms的編譯選譯糾結了一下,運行程序是老是彈出0x14b1 or 71 這類的錯誤,,之前也遇到過,不過都很快解決,這次花了好幾個小時,也是以前從來總結的結果,今天在這兒小結一下。
如果不依賴mfc 開發環境,如開發功能dll,
??配置屬性-常規-mfc的使用 ??選“使用標準的windows庫”,這兒就別改別的了。
然后就是運行時庫的問題?
在發布時配置 ?c/c++ -- 代碼生成 -- 運行時庫 一般都用md or mt 兩個,靜態編譯問題就少點,不需要目標機子上裝有所需的運行時庫,但是體積大,也可能帶來一些其它擴展、更新維護的問題,使用md 就是要求目標機上裝有運行時庫。
???????manifest 文件問題,有些開發圖方便,就直接在exe目錄下放入一個manifest文件如:Microsoft.VC80.CRT.manifest,和相應的運行時庫文件,這樣可能在團隊開發中遇到一些問題,因為你的dll會在不同機子上跑,OS版本、開發壞境不同,?Microsoft.VC80.CRT.manifest 文件就可能和已嵌入的清單文件的dll ?沖突等問題。自己機器上能跑并不代表別人機從上也能跑,所以在開發時配置dll清單文件(鏈接器--清單文件--生成清單 )時最好選”否“可以避勉一些問題,mt 編譯的都選擇"否".這樣Microsoft.VC80.CRT.manifest ?獲許才有效。
如果是用到mfc的庫
就涉及到"mfc的使用"這個選項了。在共享中使用mfc ?or 在靜態庫中使用mfc 只是看目標機子上是否裝有所依賴庫的問題了,問題沒用mt md那么多,它單純一點。
推薦搭配:
dll (沒用mfc中的東西)組件:mt / mtd+?使用標準的windows庫 (嵌入清單“否”) ?or ?md / mdd +?使用標準的windows庫 ?
mfc程序 : mt / mtd +?在靜態庫中使用mfc ?????or ?md / mdd + "大共享庫中使用mfc"
要了解相關原理及忽略庫的問題看下面網上的資料,本人并不是全部認可,不過可以參考:
vc8.0設置依賴庫
vc6設置依賴庫的辦法是 Project -> Settings -> Link選項卡 -> Object/library modules
vc8.0設置依賴庫的辦法是 項目 -> **(項目名)屬性 -> 鏈接器節點 -> 輸入 -> 附加依賴項
另外還可以使用pragma comment語句設置依賴庫
??#pragma comment(lib, ??"yourlib.lib") //設置依賴庫
??#pragma comment(linker, ??"LIBPATH:YourDir") //設置目錄
當出現類似以下鏈接錯誤時,可能是由于依賴庫設置不正確引起
錯誤????1????error LNK2019: 無法解析的外部符號 ***, 該符號在函數 "public: __cdecl ***::***(void)" (??1CSMS@@QAA@XZ) 中被引用????***.obj????
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
大家都知道現在的vs2010馬上就要推出了,但是還有很多人喜歡使用vc6進行編程,原因很簡單,就是因為用vc6編譯的程序,依賴的庫msvcrt42。mfc42等在winxp之后的版本都自動集成了,所以,用戶使用時像綠色軟件一樣。但是vc6早已是過時的開發環境了,他的界面效果不好,語法檢測不嚴格。如果我們在用vs2003之后的所有開發環境開發出的程序也像vc6一樣,不需要安裝任何運行時庫就可以綠色使用那該是多么幸福的事呀。下面我就介紹三種不依賴這些庫的方法。
?
?
在一個純win32程序里,假如我們只使用標準的API,那么編譯出來的程序依然需要運行時的庫,原因很簡單,因為windows默認的winmain函數是由運行時函數startup調用的,所以在這種情況下必然會用到msvcrt80、90的庫。解決的辦法是
?
方法一:
修改項目屬性-》c/C++->代碼生成->運行時庫 (將調試dll或dll修改為相應版本的非dll),這種方法就相當于將動態連接修改為靜態鏈接。當然了這么做會使程序本身體積變大。
?
方法二:
利用宏#pragma comment(linker,"/entry:startup")
這個宏修改了函數的入口點函數,這樣,windows加載進程時就不會先進入crt函數了。
?
以上兩種方法都存在一些局限性。比如方法二中的代碼必須不能使用任何crt函數,而面對mfc編寫的程序兩種方法就都無能為力了,下面介紹一種萬能的方法:
?
?
方法三:
通過使用WDK或者DDK對程序進行編譯。驅動編譯的優點是我們可以指定所有的鏈接lib庫,通常情況大家都會選擇系統使用的庫。而如果是mfc的類庫在vc8或vc9中是根本不能自由選擇的,編譯都是設置死的。而采用驅動編譯,就可以將這些庫統統修改為mfc42.dll的依賴,這樣就不存在高級版本靜態鏈接和打包運行庫的痛苦了。當然了采用這種方法時,一些vc6沒有帶的高級的方法是調用不了的。所以,其實也都或多或少的存在些不足。
===================================================================================
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文件。對于win98、win2000系統,把exe文件和VC的 dll連接庫放到一起就成了。對于winxp、win2003系統就要麻煩的多了,VC的連接庫默認是被放到了winsxs目錄下,結果造成在這些系統上,直接拷貝exe文件,往往是不能運行(找不到msvcr80.dll、mfc80.dll文件等),或者在事件日志中報錯。
解決方式:
方式一、在目標系統上安裝2005版vcredist_x86.exe。
方式二、直接拷貝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)找到一絕招:
方式三、
??1、首先編譯myapp.exe的時候,在配置中,選擇生成單獨的manifest文件,如:myapp.exe.manifest。
??2、將myapp.exe、myapp.exe.manifest拷貝到一個目錄下
??3、將Microsoft.VC80.MFC、Microsoft.VC80.CRT、Microsoft.VC80.MFCLOC幾個目錄下的文件,都拷貝到myapp.exe所在的目錄下。
??4、將Microsoft.VC80.MFCLOC.manifest文件中的version="8.0.50727.42",修改為version="8.0.50608.0"。
??5、編輯myapp.exe目錄下的所有 .manifest文件,將文件中的publicKey鍵值刪除,一般是publicKeyToken="1fc8b3b9a1e18e3b"
??6、然后運行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,生成的文件與你的程序一起發布就行了;
?
?
?
參考資料
1、VS2005解決"應用程序配置不正確,程序無法啟動"問題
2、VS2005安裝文件 "由于應用程序配置不正確,應用程序未能啟動"
3、Microsoft Visual C++ 2008發布程序的部署問題
4、VC編寫的程序不能在其他機器上運行的解決方案
新增(先看看上面的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種部署方法:共享并行程序集和私有程序集部署方法
所謂的共享并行程序集部署方法是指程序依賴的CRT、MFC、ATL的DLL和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.8和9.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,這個鏈接中的內容十分的詳細)
?
?
?
?
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?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)
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版本)
?
例子是針對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以供發布了。 ?
=====================================================================================================
一、MD(d)、MT(d)編譯選項的區別
1、編譯選項的位置
以VS2005為例,這樣子打開:
1)?????????打開項目的Property Pages對話框
2)?????????點擊左側C/C++節
3)?????????點擊Code Generation節
4)?????????右側第六行Runtime Library項目
2、各個設置選項代表的含義
編譯選項
包含
靜態鏈接的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,表示運行時庫由程序集成。
?
二、/MD、/MT的選擇
??????1、為什么選擇/MD,不選/MT?
?????????(1)程序就不需要靜態鏈接運行時庫,可以減小軟件的大小;
?????????(2)所有的模塊都采用/MD,使用的是同一個堆,不存在A堆申請,B堆釋放的問題。
??????2、為什么選擇/MT,不選擇/MD?
?????????(1)有些系統可能沒有程序所需要版本的運行時庫,程序必須把運行時庫靜態鏈接上。
(2)減少模塊對外界的依賴。
??????3、多個模塊,必須選擇相同的運行時庫。
三、選擇/MT需要解決的堆空間釋放問題
?????????不同的模塊各自有一份C運行時庫代碼、或者根本沒有C運行時庫,導致了各個模塊會有各自的堆。如果在A堆中申請空間,到B堆中釋放就會有崩潰,在模塊A申請的空間,必須在模塊A中釋放。
?????????附錄的DLL以及DLLUser代碼,以STL的string為例,通過修改編譯選項驗證了這個問題。string在賦值的時候需要釋放掉原來的空間,然后再申請新的空間存儲新的內容。
?
四、選擇/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(會導致膨脹?)。
四、參考資料
1、微軟關于MT、MD等的詳細介紹
http://msdn.microsoft.com/en-us/library/2kzt1wy3(v=VS.71).aspx
2、不要出現A模塊申請,B模塊釋放的情況
http://www.cnblogs.com/minggoddess/archive/2010/12/15/1907179.html
3、運行時庫有哪些版本
http://www.cppblog.com/MichaelLiu/articles/10607.html
4、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
5、不同模塊不同的堆
http://www.cnblogs.com/WengYB/archive/2011/08/18/2144727.html
?
6、因為運行時庫版本問題導致加載失敗的分享
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
---------------------?
作者:愛滄海?
來源:CSDN?
原文:https://blog.csdn.net/wowolook/article/details/8077153?
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!
總結
以上是生活随笔為你收集整理的vc 编译的那些事儿 - wowolook的专栏 - CSDN博客的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: P2678 [NOIP2015 提高组]
- 下一篇: 手动卸载 出错 无法卸载的 VC++ 运