
1. 為什么老項(xiàng)目遷移總在“最后一公里”翻車接手一個(gè)跑了十幾年的老工程代碼能編、功能能用唯獨(dú)開發(fā)環(huán)境還鎖在VC6.0上這種場景在工業(yè)控制、醫(yī)療設(shè)備、傳統(tǒng)制造業(yè)的軟件維護(hù)里太常見了。VC6.0是1998年的產(chǎn)物它的編譯器對(duì)C標(biāo)準(zhǔn)的支持停留在C98之前IDE沒有智能提示、沒有重構(gòu)、沒有像樣的調(diào)試器連工程文件格式都是私有的.dsp和.dsw。而Visual Studio從2002年之后一路演進(jìn)編譯器、標(biāo)準(zhǔn)庫、工程系統(tǒng)、調(diào)試工具全部換了好幾代。把老工程從VC6.0搬到Visual Studio表面上是換個(gè)IDE打開文件實(shí)際上是一次涉及編譯模型、字符編碼、庫依賴、工程配置的系統(tǒng)性重構(gòu)。我前后處理過七八個(gè)這類遷移項(xiàng)目規(guī)模從幾萬行到上百萬行不等涉及MFC桌面程序、純Win32服務(wù)、還有混著匯編和第三方靜態(tài)庫的工業(yè)控制軟件。每次遷移都會(huì)遇到一批高度相似的問題有些是編譯器行為差異導(dǎo)致的編譯錯(cuò)誤有些是運(yùn)行時(shí)才暴露的崩潰還有些是工程配置層面的坑。這些問題在官方文檔里往往一筆帶過但在實(shí)際項(xiàng)目里每一個(gè)都可能卡住你半天甚至幾天。這篇文章面向的是需要把VC6.0工程遷移到Visual Studio的開發(fā)者不管你用的是VS2010、VS2015還是VS2019/2022核心問題都差不多。我會(huì)把遷移過程中最常見的幾類問題拆開講清楚為什么會(huì)出問題、怎么定位、怎么解決、以及哪些地方容易反復(fù)踩坑。每個(gè)問題我都會(huì)給出具體的操作步驟和參數(shù)說明你可以直接照著做。文章里涉及的工具選型、參數(shù)配置都是基于我實(shí)際項(xiàng)目中的做法不一定唯一但都是驗(yàn)證過能跑通的方案。2. 遷移前的整體評(píng)估與準(zhǔn)備工作2.1 先搞清楚你的工程到底依賴什么很多人拿到老工程第一反應(yīng)是直接用新版Visual Studio打開.dsw文件讓IDE自動(dòng)轉(zhuǎn)換。這個(gè)做法在簡單工程上能省事但在復(fù)雜工程上往往會(huì)引入一堆隱藏問題。我的習(xí)慣是先做一次依賴盤點(diǎn)把工程的“家底”摸清楚。具體要盤點(diǎn)的內(nèi)容包括工程里引用了哪些第三方庫靜態(tài)庫.lib、動(dòng)態(tài)庫.dll、頭文件目錄、用了哪些Windows SDK的API、有沒有內(nèi)嵌匯編、有沒有依賴MFC或ATL、有沒有用到已經(jīng)廢棄的編譯器擴(kuò)展比如__export、_export這類VC6特有的關(guān)鍵字。這些信息決定了遷移的難度等級(jí)。我一般會(huì)用一個(gè)簡單的辦法快速摸底在VC6里打開工程看工程設(shè)置里的Link選項(xiàng)卡把所有Additional Library Path和Object/Library Modules記下來再看C/C選項(xiàng)卡里的Preprocessor Definitions和Additional Include Directories。這些配置項(xiàng)直接反映了工程的依賴關(guān)系。同時(shí)用文本編輯器打開.dsp文件搜索SOURCE和# ADD BASE這類標(biāo)記能快速列出所有源文件和配置分組。提示如果工程里有匯編文件.asm要特別標(biāo)記出來。VC6用的是MASM 6.11左右的版本新版Visual Studio默認(rèn)不安裝匯編器需要單獨(dú)配置。2.2 備份策略別在原始工程上直接動(dòng)手這一步看起來是廢話但我見過太多人直接在原工程上操作結(jié)果轉(zhuǎn)換失敗后連回退都回不去。正確的做法是把整個(gè)工程目錄完整復(fù)制一份在副本上做遷移。副本目錄名加上_vs_migration后綴方便區(qū)分。如果工程用了版本控制比如SVN或Git先提交一個(gè)干凈的基線版本然后開一個(gè)專門的分支做遷移。這樣即使遷移過程中改亂了也能隨時(shí)對(duì)比原始狀態(tài)。我一般還會(huì)在遷移前用VC6完整編譯一次確認(rèn)原始工程在當(dāng)前環(huán)境下能正常構(gòu)建排除“遷移前就有問題”的干擾。2.3 選擇目標(biāo)Visual Studio版本不是越新的版本越好。VS2019和VS2022對(duì)C標(biāo)準(zhǔn)的支持更完整但對(duì)老代碼的兼容性反而可能更嚴(yán)格。VS2010和VS2015在兼容性和工具鏈成熟度之間平衡得比較好很多工業(yè)控制領(lǐng)域的項(xiàng)目至今還在用VS2015。選擇版本時(shí)考慮幾個(gè)因素工程用到的第三方庫有沒有對(duì)應(yīng)版本的預(yù)編譯庫、團(tuán)隊(duì)成員的開發(fā)環(huán)境統(tǒng)一情況、目標(biāo)運(yùn)行環(huán)境比如Windows 7還是Windows 10。如果第三方庫只有VS2010的版本那強(qiáng)行上VS2022就得自己重新編譯庫工作量會(huì)成倍增加。我個(gè)人的經(jīng)驗(yàn)是如果工程規(guī)模不大幾萬行以內(nèi)直接上VS2019/2022一次性把標(biāo)準(zhǔn)升級(jí)到位如果工程規(guī)模大、依賴復(fù)雜先用VS2010或VS2015過渡等編譯通過、運(yùn)行穩(wěn)定后再考慮升級(jí)工具集。3. 工程文件轉(zhuǎn)換與編譯模型差異3.1 .dsp到.vcxproj的轉(zhuǎn)換機(jī)制Visual Studio打開.dsw文件時(shí)會(huì)啟動(dòng)一個(gè)轉(zhuǎn)換向?qū)О裋C6的工程文件轉(zhuǎn)換成MSBuild格式的.vcxproj。這個(gè)轉(zhuǎn)換過程本質(zhì)上是把VC6的配置項(xiàng)映射到MSBuild的屬性組里。大部分常見配置能自動(dòng)映射但有幾類配置會(huì)丟失或映射錯(cuò)誤。第一類是自定義的編譯選項(xiàng)。VC6的工程設(shè)置里如果手動(dòng)填了一些非標(biāo)準(zhǔn)的編譯參數(shù)比如/GF、/Gy這類優(yōu)化選項(xiàng)轉(zhuǎn)換后可能被丟棄。第二類是預(yù)編譯頭文件的配置。VC6的預(yù)編譯頭設(shè)置和MSBuild的模型不一樣轉(zhuǎn)換后經(jīng)常出現(xiàn)stdafx.h找不到或者預(yù)編譯頭失效的問題。第三類是輸出目錄和中間目錄的相對(duì)路徑轉(zhuǎn)換后可能變成絕對(duì)路徑導(dǎo)致?lián)Q臺(tái)機(jī)器就編譯不了。轉(zhuǎn)換完成后我建議立刻打開.vcxproj文件用文本編輯器或VS的“編輯項(xiàng)目文件”功能逐項(xiàng)核對(duì)以下配置ClCompile節(jié)點(diǎn)下的PreprocessorDefinitions、AdditionalIncludeDirectories、PrecompiledHeaderLink節(jié)點(diǎn)下的AdditionalDependencies、AdditionalLibraryDirectoriesOutDir和IntDir。這些是出問題最集中的地方。3.2 字符集問題多字節(jié)與Unicode的抉擇VC6時(shí)代大部分工程用的是多字節(jié)字符集MBCS而新版Visual Studio默認(rèn)用Unicode字符集。這個(gè)差異會(huì)導(dǎo)致大量編譯錯(cuò)誤典型的表現(xiàn)是C2664: cannot convert parameter、C2440: cannot convert from const char * to LPCWSTR這類類型不匹配的錯(cuò)誤。處理方式有兩種。第一種是保持多字節(jié)字符集在工程屬性里把Character Set改成Use Multi-Byte Character Set。這個(gè)做法改動(dòng)最小適合不想動(dòng)代碼的情況。但要注意VS2019之后微軟對(duì)MBCS的支持變成了可選組件安裝時(shí)需要勾選“C MFC for latest v142 build tools (x86 x64) with MBCS support”之類的選項(xiàng)否則會(huì)報(bào)找不到MFC庫。第二種是遷移到Unicode把代碼里的char改成TCHAR、strcpy改成_tcscpy、字符串字面量加_T()宏。這個(gè)做法工作量大但長期來看更規(guī)范。我的建議是如果工程還要長期維護(hù)趁遷移的機(jī)會(huì)一次性改成Unicode如果只是臨時(shí)維護(hù)、不打算大改先用MBCS把編譯跑通。注意改成Unicode后所有和外部系統(tǒng)交互的接口比如讀寫文件、調(diào)用Win32 API、網(wǎng)絡(luò)通信都要檢查字符編碼轉(zhuǎn)換。特別是用fopen、fwrite這類C標(biāo)準(zhǔn)庫函數(shù)的地方Unicode下要用_wfopen或者做顯式轉(zhuǎn)換。3.3 預(yù)編譯頭文件的配置調(diào)整VC6的預(yù)編譯頭機(jī)制和MSBuild不同。VC6里每個(gè).cpp文件通過#include stdafx.h來使用預(yù)編譯頭工程設(shè)置里指定stdafx.h和stdafx.cpp。轉(zhuǎn)換到MSBuild后需要在每個(gè).cpp文件的屬性里設(shè)置Precompiled Header為Use同時(shí)stdafx.cpp設(shè)置為Create。常見的問題是轉(zhuǎn)換后所有文件的預(yù)編譯頭設(shè)置都變成了Not Using Precompiled Headers導(dǎo)致編譯變慢或者部分文件報(bào)fatal error C1010: unexpected end of file while looking for precompiled header。解決辦法是批量選中所有.cpp文件在屬性里統(tǒng)一設(shè)置預(yù)編譯頭為Use然后單獨(dú)把stdafx.cpp設(shè)為Create。如果工程里有些文件本來就不該用預(yù)編譯頭比如純C文件或者第三方庫的源文件要單獨(dú)把這些文件設(shè)為Not Using Precompiled Headers否則會(huì)報(bào)錯(cuò)。4. 編譯器行為差異引發(fā)的典型錯(cuò)誤4.1 for循環(huán)變量作用域的變化這是VC6遷移到新版VS后最常見的編譯錯(cuò)誤之一。VC6遵循的是老標(biāo)準(zhǔn)for循環(huán)里聲明的變量作用域會(huì)泄漏到循環(huán)外部。比如for (int i 0; i 10; i) { // ... } if (i 10) { // VC6里合法新版VS報(bào)錯(cuò) // ... }新版Visual Studio默認(rèn)遵循C標(biāo)準(zhǔn)for循環(huán)里的i作用域僅限于循環(huán)體內(nèi)循環(huán)外訪問會(huì)報(bào)C2065: undeclared identifier。這個(gè)問題在大工程里可能涉及幾百處手動(dòng)改很痛苦。解決辦法是在工程屬性里把C/C - Language - Conformance mode設(shè)為No或者在Command Line里加/Zc:forScope-。這個(gè)選項(xiàng)讓編譯器恢復(fù)VC6的作用域行為。但要注意這只是兼容手段新寫的代碼還是應(yīng)該遵循標(biāo)準(zhǔn)作用域規(guī)則。如果選擇改代碼可以用一個(gè)簡單的正則表達(dá)式批量處理搜索for\s*\(\s*int\s(\w)把變量聲明提到循環(huán)外面。但這個(gè)方法有風(fēng)險(xiǎn)因?yàn)橛行┭h(huán)里的變量名會(huì)重復(fù)提到外面會(huì)沖突。更穩(wěn)妥的做法是逐個(gè)文件檢查用編譯器的錯(cuò)誤列表定位每一處。4.2 模板和STL的兼容性問題VC6的STL實(shí)現(xiàn)非常古老有很多不符合標(biāo)準(zhǔn)的地方。遷移到新版VS后常見的錯(cuò)誤包括std::vector的迭代器類型不匹配、std::map的erase返回值差異、std::string的c_str()返回類型變化等。一個(gè)典型問題是VC6里std::vector::iterator可以直接和整數(shù)比較新版VS不允許。還有VC6的std::map::erase(iterator)返回void新版返回迭代器如果代碼里依賴了返回值就會(huì)出問題。處理這類問題沒有捷徑只能根據(jù)編譯錯(cuò)誤逐個(gè)修改。我的經(jīng)驗(yàn)是先把所有和STL相關(guān)的錯(cuò)誤集中處理因?yàn)檫@類錯(cuò)誤往往集中在少數(shù)幾個(gè)頭文件或工具類里。改完后重新編譯再處理下一批。提示如果工程里用了大量STL建議在遷移前先確認(rèn)代碼里有沒有依賴VC6特有行為的寫法。比如std::vector的resize在VC6里不會(huì)對(duì)新增元素做值初始化新版會(huì)。這類行為差異可能導(dǎo)致運(yùn)行時(shí)邏輯變化不只是編譯錯(cuò)誤。4.3 異常處理和RTTI的配置差異VC6默認(rèn)關(guān)閉RTTI運(yùn)行時(shí)類型識(shí)別新版VS默認(rèn)開啟。如果代碼里用了dynamic_cast或者typeid在VC6里可能編譯不過但在新版里能編譯。反過來如果代碼里依賴了關(guān)閉RTTI時(shí)的行為比如某些類型轉(zhuǎn)換的隱式處理開啟RTTI后可能行為不一致。異常處理方面VC6的/GX選項(xiàng)對(duì)應(yīng)新版的/EHsc。如果工程里用了C異常要確保Exception Handling Model設(shè)置為Yes (/EHsc)。如果用了SEH結(jié)構(gòu)化異常處理要設(shè)置為Yes with SEH Exceptions (/EHa)。這個(gè)配置在工程屬性的C/C - Code Generation里。4.4 棄用函數(shù)和安全性檢查新版Visual Studio默認(rèn)啟用了SDL checks安全開發(fā)生命周期檢查會(huì)把很多C標(biāo)準(zhǔn)庫函數(shù)標(biāo)記為不安全報(bào)C4996警告或錯(cuò)誤。比如strcpy、sprintf、strcat這些函數(shù)都會(huì)觸發(fā)警告。處理方式有三種一是在工程屬性里把SDL Checks設(shè)為No二是在文件開頭加#define _CRT_SECURE_NO_WARNINGS三是把Treat Warnings as Errors關(guān)掉。我一般推薦第二種因?yàn)楦膭?dòng)最小而且不影響其他安全檢查。如果工程里用了fopen、fread這些文件操作函數(shù)還會(huì)遇到_CRT_SECURE_NO_WARNINGS之外的問題。新版VS對(duì)文件操作的參數(shù)檢查更嚴(yán)格比如fopen的路徑參數(shù)如果是char*在Unicode工程里會(huì)報(bào)類型錯(cuò)誤。5. 鏈接與庫依賴的坑5.1 運(yùn)行時(shí)庫的版本匹配VC6用的是msvcrt.dllMicrosoft Visual C Runtime新版VS用的是vcruntime140.dll、msvcp140.dll這些版本化的運(yùn)行時(shí)庫。如果工程里鏈接了第三方靜態(tài)庫而這些庫是用VC6編譯的鏈接時(shí)會(huì)出現(xiàn)符號(hào)沖突或運(yùn)行時(shí)崩潰。典型的表現(xiàn)是鏈接時(shí)報(bào)LNK2005: _malloc already defined in libcmt.lib或者運(yùn)行時(shí)在malloc/free處崩潰。原因是VC6的靜態(tài)庫和VS的運(yùn)行時(shí)庫用了不同的堆管理機(jī)制混用會(huì)導(dǎo)致內(nèi)存分配和釋放不在同一個(gè)堆上。解決辦法是要么用新版VS重新編譯所有第三方庫要么把工程配置成使用VC6的運(yùn)行時(shí)庫/MD改成/MT并鏈接VC6的libcmt.lib。但后者在新版VS里越來越難做因?yàn)閂S2015之后不再提供VC6的運(yùn)行時(shí)庫。最徹底的辦法還是重新編譯第三方庫。5.2 鏈接器選項(xiàng)的差異VC6的鏈接器選項(xiàng)和新版MSBuild的鏈接器選項(xiàng)有很多名字不一樣。比如VC6的/subsystem:windows在新版里是SubSystem屬性VC6的/incremental:yes對(duì)應(yīng)Link Incrementally。轉(zhuǎn)換后要檢查這些選項(xiàng)有沒有正確映射。一個(gè)常見問題是入口點(diǎn)設(shè)置。VC6的Win32程序默認(rèn)入口是WinMainCRTStartup新版VS如果工程類型識(shí)別錯(cuò)誤可能把入口設(shè)成mainCRTStartup導(dǎo)致鏈接時(shí)報(bào)LNK2019: unresolved external symbol _WinMain。解決辦法是在Linker - Advanced - Entry Point里顯式指定WinMainCRTStartup。另一個(gè)問題是/OPT:NOWIN98這類VC6特有的優(yōu)化選項(xiàng)新版VS不支持轉(zhuǎn)換后會(huì)報(bào)未知選項(xiàng)。直接在鏈接器命令行里刪掉即可。5.3 依賴庫的搜索路徑VC6的庫搜索路徑是在Tools - Options - Directories里全局配置的新版VS改成了工程級(jí)別的Additional Library Directories。轉(zhuǎn)換后如果路徑?jīng)]映射過來鏈接時(shí)會(huì)報(bào)LNK1104: cannot open file xxx.lib。解決辦法是在工程屬性的Linker - General - Additional Library Directories里手動(dòng)添加庫路徑。如果路徑里包含環(huán)境變量比如$(MYLIB)要確保這些環(huán)境變量在新環(huán)境下也定義了。注意新版VS對(duì)路徑中的空格和中文支持更好但有些老庫的路徑里如果有特殊字符還是可能出問題。建議把所有庫路徑改成純英文、無空格的路徑。6. 運(yùn)行時(shí)行為差異與調(diào)試技巧6.1 內(nèi)存布局和結(jié)構(gòu)體對(duì)齊VC6默認(rèn)的結(jié)構(gòu)體對(duì)齊是8字節(jié)新版VS默認(rèn)也是8字節(jié)但某些編譯選項(xiàng)下可能不同。如果工程里用了#pragma pack或者依賴了特定的內(nèi)存布局比如和硬件通信的數(shù)據(jù)結(jié)構(gòu)遷移后可能出現(xiàn)數(shù)據(jù)錯(cuò)位。檢查方法是在遷移前后分別用sizeof打印關(guān)鍵結(jié)構(gòu)體的大小對(duì)比是否一致。如果不一致檢查Struct Member Alignment設(shè)置在C/C - Code Generation里確保兩邊一致。6.2 浮點(diǎn)運(yùn)算的精度差異VC6的浮點(diǎn)運(yùn)算默認(rèn)使用x87指令集新版VS默認(rèn)使用SSE2指令集。這個(gè)差異會(huì)導(dǎo)致浮點(diǎn)計(jì)算結(jié)果有微小差別。對(duì)于大多數(shù)應(yīng)用來說無所謂但對(duì)于需要精確復(fù)現(xiàn)的數(shù)值計(jì)算比如控制系統(tǒng)、仿真軟件可能是個(gè)問題。如果必須保持和VC6一致的浮點(diǎn)行為可以在工程屬性里把Floating Point Model設(shè)為Strict (/fp:strict)并關(guān)閉Enable Enhanced Instruction Set。但這樣會(huì)犧牲性能而且不是所有情況都能完全復(fù)現(xiàn)。6.3 調(diào)試器的使用差異VC6的調(diào)試器功能比較基礎(chǔ)新版VS的調(diào)試器強(qiáng)大很多。遷移后建議花點(diǎn)時(shí)間熟悉新調(diào)試器的功能比如條件斷點(diǎn)、數(shù)據(jù)斷點(diǎn)、內(nèi)存窗口、并行堆棧等。這些功能在排查遷移后的運(yùn)行時(shí)問題時(shí)非常有用。一個(gè)實(shí)用技巧如果遷移后程序在某個(gè)函數(shù)崩潰但VC6里正??梢杂眯掳鎂S的“異常設(shè)置”窗口把Win32 Exceptions里的0xC0000005: Access violation勾上讓調(diào)試器在第一次機(jī)會(huì)異常時(shí)就中斷這樣能更早定位到問題代碼。7. 常見問題速查與避坑經(jīng)驗(yàn)7.1 編譯錯(cuò)誤速查表錯(cuò)誤代碼典型原因解決辦法C2065for循環(huán)變量作用域加/Zc:forScope-或改代碼C2664字符集不匹配改MBCS或加_T()宏C2440類型轉(zhuǎn)換失敗檢查char*和wchar_t*混用C1010預(yù)編譯頭配置錯(cuò)誤統(tǒng)一設(shè)置Use Precompiled HeaderC4996不安全函數(shù)警告加_CRT_SECURE_NO_WARNINGSLNK2005運(yùn)行時(shí)庫沖突重新編譯第三方庫LNK2019入口點(diǎn)或符號(hào)缺失檢查Entry Point和庫依賴LNK1104庫文件找不到添加Additional Library Directories7.2 我踩過的幾個(gè)典型坑第一個(gè)坑是預(yù)編譯頭文件。有一次遷移一個(gè)MFC工程轉(zhuǎn)換后所有文件都報(bào)C1010我以為是stdafx.h路徑問題查了半天才發(fā)現(xiàn)是轉(zhuǎn)換向?qū)О裺tdafx.cpp的預(yù)編譯頭設(shè)置改成了Not Using。后來我養(yǎng)成了習(xí)慣轉(zhuǎn)換完成后第一件事就是檢查stdafx.cpp的設(shè)置。第二個(gè)坑是第三方庫的運(yùn)行時(shí)庫版本。有個(gè)工程鏈接了一個(gè)用VC6編譯的靜態(tài)庫編譯能過但運(yùn)行時(shí)在new/delete處隨機(jī)崩潰。查了兩天才發(fā)現(xiàn)是運(yùn)行時(shí)庫不匹配。后來我定了個(gè)規(guī)矩遷移前先確認(rèn)所有第三方庫有沒有對(duì)應(yīng)新VS版本的預(yù)編譯庫沒有的話就準(zhǔn)備好源碼重新編譯。第三個(gè)坑是字符集轉(zhuǎn)換后的文件路徑問題。有個(gè)工程從MBCS改成Unicode后讀寫配置文件的功能失效了。原因是代碼里用fopen打開文件路徑是char*Unicode下fopen的參數(shù)類型變了。改成_wfopen并做路徑轉(zhuǎn)換后解決。7.3 遷移后的驗(yàn)證清單遷移完成后不要只滿足于“能編譯通過”。我一般會(huì)做以下幾項(xiàng)驗(yàn)證完整編譯一次Release版本確認(rèn)沒有警告運(yùn)行所有單元測試如果有手動(dòng)測試核心功能模塊檢查內(nèi)存泄漏用VS自帶的內(nèi)存分析工具對(duì)比遷移前后的輸出結(jié)果比如生成的文件、網(wǎng)絡(luò)報(bào)文是否一致。如果工程有自動(dòng)化測試遷移后第一時(shí)間跑一遍。沒有的話至少要把主要業(yè)務(wù)流程手動(dòng)走一遍。我見過太多“編譯通過但運(yùn)行時(shí)行為變了”的案例都是因?yàn)楹雎粤蓑?yàn)證環(huán)節(jié)。8. 一些提高遷移效率的實(shí)用技巧批量修改代碼時(shí)善用Visual Studio的“在文件中替換”功能。比如把所有strcpy替換成strcpy_s可以限定文件類型和目錄范圍避免誤改第三方庫。但要注意strcpy_s的參數(shù)比strcpy多一個(gè)不能簡單替換需要手動(dòng)調(diào)整。如果工程里有大量#include iostream.h這種老式頭文件引用可以批量替換成#include iostream加using namespace std;。但要注意命名空間污染問題最好在文件級(jí)別加using不要放在全局頭文件里。對(duì)于匯編文件新版VS需要單獨(dú)配置MASM。在工程里添加一個(gè).asm文件后右鍵屬性把Item Type設(shè)為Microsoft Macro Assembler。如果VS沒安裝MASM需要通過Visual Studio Installer勾選“C MFC for latest build tools”或者單獨(dú)安裝MASM。最后遷移過程中遇到問題時(shí)善用搜索引擎查錯(cuò)誤代碼。大部分常見錯(cuò)誤都有現(xiàn)成的解決方案特別是微軟的官方文檔和開發(fā)者社區(qū)的討論。但要注意不同VS版本的解決方案可能不一樣查的時(shí)候要帶上版本號(hào)。