潛在升級問題概觀 (Visual C++)
多年來,Microsoft C++ 編譯器經過許多變更,包括 C++ 語言本身、C++ 標準程式庫、C 執行階段 (CRT) 以及 MFC 和 ATL 這類其他程式庫。 因此,當您從舊版 Visual Studio 升級應用程式時,您可能會在先前編譯乾淨的程式代碼中看到編譯程式和連結器錯誤和警告。 原始程式碼基底越舊,這類錯誤的可能性就越大。 此概觀摘要說明您可能會看到的最常用問題類別,並提供更詳細的信息連結。
注意
過去,我們建議跨數個 Visual Studio 版本的升級一次以累加方式執行一個版本。 我們不再建議這種方法。 我們發現,無論程式代碼基底有多舊,升級至最新版的Visual Studio幾乎一律比較簡單。
有關升級程序的問題或意見都可以傳送至 vcupgrade@microsoft.com。
程式庫和工具組相依性
注意
本節適用於以 Visual Studio 2013 及更早版本建置的應用程式和程式庫。 Visual Studio 2015、Visual Studio 2017 與 Visual Studio 2019 所使用的工具組為二進位相容。 如需詳細資訊,請參閱 C++ Visual Studio 版本之間的二進位相容性。
當您將應用程式從 Visual Studio 2013 或之前升級至較新版本時,通常建議且必須升級應用程式連結的所有連結庫和 DLL。 您必須具有原始程式碼的存取權,或者連結庫廠商必須提供以相同主要版本的編譯程式編譯的新二進位檔。 如果符合其中一個條件,則您可以略過處理二進位檔相容性詳細資料的這一節。 如果兩者都不是這種情況,則連結庫可能無法在升級的應用程式中運作。 本節中的資訊將協助您瞭解是否可以繼續升級。
工具組
和 .obj
.lib
檔案格式已妥善定義且很少變更。 有時會針對這些檔案格式進行新增,但這些新增通常不會影響較新工具組使用較舊工具組所產生的物件檔案和程式庫。 主要例外狀況是,如果您使用 /GL
編譯 (整個程序優化) 。 如果您使用 編譯 /GL
,您只能使用用來產生它的工具組來連結產生的物件檔。 因此,如果您使用 和 /GL
使用 Visual Studio 2017 (v141) 編譯程式產生物件檔案,則必須使用 Visual Studio 2017 (v141) 連結器連結它。 這是因為對象檔中的內部數據結構在主要版本的工具組之間並不穩定。 較新的工具組無法瞭解較舊的數據格式。
C++ 沒有穩定的應用程式二進位介面 (ABI)。 但 Visual Studio 會為版本所有次要版本維持穩定的 C++ ABI。 Visual Studio 2015 (v140)、Visual Studio 2017(v141)、Visual Studio 2019 (v142) 和 Visual Studio 2022 (v143) 工具組只會在其次要版本中有所不同。 它們都有相同的主要版本號碼,也就是 14。 如需詳細資訊,請參閱 C++ Visual Studio 版本之間的二進位相容性。
如果您的目的檔包含具有 C++ 連結的外部符號,則該目的檔可能未正確地連結到以不同主要工具組版本所產生的目的檔。 有許多可能的結果:連結可能會完全失敗(例如,如果名稱裝飾已變更)。 連結可能會成功,但應用程式可能會在運行時間失敗(例如,如果類型配置已變更)。 或者您的應用程式可能會繼續運作,而且不會有任何問題。 另請注意,雖然C++ ABI 不穩定,但 C ABI 和 COM 所需的C++ ABI 子集是穩定的。
若您連結到匯入程式庫,在執行階段即會使用會保留 ABI 相容性的 Visual Studio 可轉散發程式庫之任一新版。 例如,如果您使用 Visual Studio 2015 Update 3 工具組編譯及連結應用程式,您可以使用任何稍後的可轉散發套件。 這是因為 2015、2017、2019 和 2022 連結庫保留回溯二進位相容性。 反向不是真的:您無法針對舊版的工具組使用可轉散發套件,而不是用來建置程式代碼的任何元件。
程式庫
如果您 #include
特定版本的標頭檔,您必須將產生的物件檔連結至相同版本的連結庫。 因此,例如,如果您的來源檔案包含 Visual Studio 2015 Update 3 <immintrin.h>
,您必須連結至 Visual Studio 2015 Update 3 vcruntime
連結庫。 同樣地,如果您的來源檔案包含 Visual Studio 2017 15.5 <iostream>
版,您必須連結至 Visual Studio 2017 15.5 版 Standard C++ 連結庫。 msvcprt
不支援混合和比對。
針對C++標準連結庫,自Visual Studio 2010以來,在標準標頭中使用 混合和比對已明確不允許 #pragma detect_mismatch
。 如果您嘗試連結不相容的物件檔案,或如果您連結錯誤的標準連結庫,則鏈接會失敗。
從未支援較舊的CRT版本混合與比對,但它通常只是運作,因為API介面不會隨著時間而變更太多。 通用 CRT 已中斷回溯相容性,因此未來我們可以維持回溯相容性。 我們未來沒有計劃引進新的版本通用CRT二進位檔。 相反地,已就地更新現有的通用 CRT。
為了提供以舊版 Microsoft C 運行時間標頭編譯的物件檔案(和連結庫)部分連結相容性,我們提供連結庫 legacy_stdio_definitions.lib
,搭配 Visual Studio 2015 和更新版本。 此程式庫提供已從通用 CRT 移除之大部分函式和資料匯出的相容性符號。 所提供的 legacy_stdio_definitions.lib
相容性符號集足以滿足大部分相依性,包括 Windows SDK 中包含的連結庫中的所有相依性。 不過,某些符號已從沒有相容性符號的通用CRT中移除。 這些符號包括函式 (例如__iob_func
, ) 與某些資料匯出 (例如, __imp___iob
、 __imp___pctype
__imp___mb_cur_max
如果您有使用舊版 C 運行時間標頭所建置的靜態庫,建議您依此順序執行下列動作:
使用新版 Visual Studio 和通用 CRT 標頭重建靜態程式庫,以支援連結到通用 CRT。 這個方法完全受到支援,也是最佳選項。
如果您無法(或不想)重建靜態庫,您可以嘗試與
legacy_stdio_definitions.lib
連結。 如果它符合靜態庫的鏈接時間相依性,您會想要在二進位檔中使用時徹底測試靜態庫。 請確定它不會受到對通用CRT所做的任何行為變更造成負面影響。您的靜態庫相依性可能不符合
legacy_stdio_definitions.lib
,或連結庫因行為變更而無法與通用CRT搭配運作。 在此情況下,建議您將靜態庫封裝成連結至與 Microsoft C 運行時間所需版本連結的 DLL。 例如,如果使用Visual Studio 2013建置靜態庫,請使用Visual Studio 2013 工具組和C++連結庫建置此 DLL。 透過將程式庫建置到 DLL,可以封裝為其與特定 Microsoft C 執行階段版本之相依性的實作詳細資料。 請小心 DLL 介面不會洩漏它所使用的 C 運行時間詳細數據,例如,如果它傳回FILE*
跨 DLL 界限,或malloc
呼叫端必須配置的free
指標。
在單一程式中使用多個 CCT 本身並無問題。 (事實上,大部分進程都會載入多個CRT DLL。例如,Windows 作業系統元件相依於 msvcrt.dll
,而CLR則取決於自己的私人CRT。當您從不同的 CCT 發生錯誤狀態時,就會發生問題。 例如,您不應該使用 msvcr110.dll!malloc
配置記憶體,而且不應該嘗試使用 來解除分配該記憶體 msvcr120.dll!free
,而且您不應該嘗試使用 來開啟 FILE msvcr110!fopen
,並嘗試使用 msvcr120!fread
從該 FILE 讀取。 只要您沒有來自不同 CCT 的亂亂狀態,就可以安全地在單一進程中載入多個 CCT。
如需詳細資訊,請參閱將程式碼升級至通用 CRT。
項目設定所造成的錯誤
若要開始升級程序,請在最新版 Visual Studio 中開啟較舊的專案/解決方案/工作區。 Visual Studio 將會根據舊專案設定來建立新的專案。 檢查較舊的專案是否有連結庫路徑,或包含硬式編碼為非標準位置的路徑。 當專案使用預設設定時,編譯程式可能會看不到這些路徑中的檔案。 如需詳細資訊,請參閱連結器 OutputFile 設定。
一般而言,現在是組織專案程式代碼以簡化項目維護,並協助您儘快建置升級的程序代碼。 如果您的原始程式碼已經組織良好,而且舊版專案會在Visual Studio 2010或更新版本下編譯,您可以手動編輯新的項目檔,以支援舊編譯程式與新編譯程式上的編譯。 下列範例示範如何針對 Visual Studio 2015 和 Visual Studio 2017 進行編譯:
<PlatformToolset Condition="'$(VisualStudioVersion)'=='14.0'">v140</PlatformToolset>
<PlatformToolset Condition="'$(VisualStudioVersion)'=='15.0'">v141</PlatformToolset>
LNK2019:無法解析的外部
針對無法解析的符號,您可能需要修復專案設定。
如果來源檔案位於非預設位置,您是否已將路徑新增至專案的 include 目錄?
如果外部是在檔案中
.lib
定義,您是否已在專案屬性中指定 lib 路徑,而且檔案的正確版本.lib
位於該處嗎?您是否嘗試連結至
.lib
以不同版本的 Visual Studio 編譯的檔案? 如果是的話,請參閱有關程式庫和工具組相依性的上一節。呼叫位置上的引數類型實際符合函式的現有多載嗎? 請確認基礎類型是您預期的結果,無論是針對函式簽章中的任何 typedefs,還是呼叫函式的程序代碼。
若要針對未解決的符號錯誤進行疑難解答,您可以使用 dumpbin.exe
來檢查二進位檔中定義的符號。 請嘗試下列的命令列,檢視程式庫中定義的符號:
dumpbin.exe /LINKERMEMBER somelibrary.lib
/Zc:wchar_t
(wchar_t
為原生類型)
(在 Microsoft Visual C++ 6.0 和更早版本中, wchar_t
並未實作為內建類型。在 中 wchar.h
宣告為 的 typedef。 unsigned short
C++標準需要 wchar_t
是內建類型。 使用 typedef 版本可能會造成可攜性問題。 如果您從舊版 Visual Studio 升級,並看到編譯程式錯誤 C2664,因為程式碼嘗試隱含地轉換成 wchar_t
unsigned short
,建議您變更程式代碼以修正錯誤,而不是設定 /Zc:wchar_t-
。 如需詳細資訊,請參閱 /Zc:wchar_t
(wchar_t 為原生類型)。
使用連結器選項 /NODEFAULTLIB
、 /ENTRY
、 和 升級 /NOENTRY
鏈接 /NODEFAULTLIB
器選項(或 忽略所有預設連結庫 連結器屬性)會告知連結器不要在默認連結庫中自動連結,例如CRT。 這表示每個程式庫都必須個別列為輸入。 此程式庫清單提供於 [專案屬性] 對話方塊之 [連結器] 區段的 [其他相依性] 屬性中。
因為部分預設程式庫的名稱已變更,所以升級時,使用此選項的專案會出現問題。 因為每個程式庫都必須列在 [其他相依性] 屬性中或連結器命令列上,所以您必須更新程式庫清單以使用所有目前名稱。
下表顯示從 Visual Studio 2015 開始其內容經變更的程式庫。 若要升級,您需要將第二個資料行中新程式庫名稱新增到第一個資料行中的程式庫。 其中一些連結庫是匯入連結庫,但這不重要。
如果您是使用︰ | 您需要使用這些程式庫: |
---|---|
libcmt.lib |
libcmt.lib 、 、 libucrt.lib libvcruntime.lib |
libcmtd.lib |
libcmtd.lib 、 、 libucrtd.lib libvcruntimed.lib |
msvcrt.lib |
msvcrt.lib 、 、 ucrt.lib vcruntime.lib |
msvcrtd.lib |
msvcrtd.lib 、 、 ucrtd.lib vcruntimed.lib |
如果您使用也會略過預設程式庫的 /ENTRY
選項或 /NOENTRY
選項,則也會出現相同問題。
改善語言一致性所造成的錯誤
多年來,Microsot C++ 編譯器持續改善其與 C++ 標準的一致性。 在舊版中編譯的程式代碼可能無法在較新版本的Visual Studio中編譯。 這是因為編譯程序正確地標幟先前忽略或明確允許的錯誤。
例如,在 MSVC 的歷程記錄中,/Zc:forScope
為早期推出的參數。 它允許不一致的迴圈變數行為。 該參數現在已被取代,可能會在未來的版本中移除。 強烈建議您在升級程式代碼時不要使用該參數。 如需詳細資訊,請參閱 /Zc:forScope-
已被取代。
您在升級時可能會看到的其中一個常見編譯器錯誤範例,是將非 const 引數傳遞至 const 參數。 舊版編譯程式不一定會將它標示為錯誤。 如需詳細資訊,請參閱編譯器的更嚴格轉換。
如需特定一致性改進的詳細資訊,請參閱 Visual C++ 2003 - 2015 的變更歷程記錄和 Visual Studio 中的 C++ 一致性改進。
涉及 <stdint.h>
整數型別的錯誤
標頭 <stdint.h>
會定義 typedefs 和巨集,與內建整數類型不同,保證在所有平臺上都有指定的長度。 例如 uint32_t
和 int64_t
就是範例。 標頭 <stdint.h>
已在Visual Studio 2010中新增。 在 2010 年之前撰寫的程式代碼可能已為這些類型提供私人定義。 而且,這些定義不一定與 <stdint.h>
定義一致。
如果錯誤是 C2371,而且 stdint
涉及類型,可能表示類型是在程式代碼或第三方連結庫檔案的標頭中定義。 升級時,您應該排除類型的任何自定義定義 <stdint.h>
,但請先比較自定義定義與目前的標準定義,以確保您不會帶來新的問題。
您可以按下 F12 (移至定義),來查看有疑義的類型在何處定義。
編譯 /showIncludes
程式選項在這裡很有用。 在專案的 [屬性頁] 對話框中,選取 [組態屬性>C/C++][>進階] 頁面,並將 [顯示包含] 設定為 [是]。 然後重建您的專案。 您會在輸出視窗中看到檔案清單 #include
。 每個標頭都會縮排在包含它的標頭下方。
涉及 CRT 函式的錯誤
多年來,已對 C 執行階段進行許多變更。 已新增許多安全版本的函式,並已移除一些函式。 此外,如本文稍早所述,Microsoft CRT 的實作已在 Visual Studio 2015 中重構為新的二進位檔和相關聯的 .lib
檔案。
如果錯誤涉及 CRT 函式,請搜尋 Visual C++ 變更歷程記錄 2003 - 2015 或 Visual Studio 中的 C++ 一致性改善,以查看這些文章是否包含任何其他資訊。 如果錯誤LNK2019,請確定函式尚未移除。 否則,如果您確定函式仍然存在,而且呼叫程式代碼正確,請檢查您的專案是否使用 /NODEFAULTLIB
。 如果是,您必須更新連結庫清單,以使用新的通用 (UCRT) 連結庫。 如需詳細資訊,請參閱上方的<程式庫和相依性>一節。
如果錯誤涉及 printf
或 scanf
,請確定您不私下定義任一函式,而不包含 stdio.h
。 如果是,請移除私人定義或連結至 legacy_stdio_definitions.lib
。 您可於 [組態屬性]>[連結器]>[輸入] 下的 [屬性頁] 對話方塊中,於 [其他相依性] 屬性中設定此程式庫。 如果您與 Windows SDK 8.1 或更早版本連結,請新增 legacy_stdio_definitions.lib
。
如果錯誤涉及格式字串引數,則原因可能是編譯器在強制執行標準方面較為嚴格。 如需詳細資訊,請參閱變更歷程記錄。 因為這裡的任何錯誤都可能代表安全性風險,所以請密切注意它們。
C++ 標準變更所造成的錯誤
C++標準本身已以不一定會回溯相容的方式演進。 C++11 引進移動語意、新關鍵詞,以及其他語言和標準連結庫功能。 這些變更可能會導致編譯程序錯誤,甚至是不同的運行時間行為。
例如,舊的C++程式可能包含 iostream.h
標頭。 在 C++ 歷程記錄中,此標頭已在早期淘汰,並最終從 Visual Studio 完全移除。 在此情況下,您必須使用 <iostream>
並重寫程序代碼。 如需詳細資訊,請參閱 更新舊 iostream
程序代碼。
C4838:縮小轉換警告
C++標準現在指定從不帶正負號到帶正負號整數值的轉換會縮小轉換。 編譯程式未在Visual Studio 2015之前引發此警告。 檢查每個專案,以確定縮小不會影響程式代碼的正確性。
使用安全 CRT 函式的警告
多年來,已引進安全版本的 C 執行階段函式。 雖然舊的不安全版本仍然可用,但是建議將程式碼變更成使用安全版本。 編譯器會發出使用不安全版本的警告。 您可以選擇停用或忽略這些警告。 若要停用解決方案中所有專案的警告,請開啟 [檢視]>[屬性管理員],並選取您要停用此警告的所有專案,然後以滑鼠右鍵按一下選取的項目,再選擇 [屬性]。 在 [組態屬性]>[C/C++]>[進階] 下的 [屬性頁] 對話方塊中,選取 [停用特定警告]。 選擇下拉式箭號,然後選擇 [ 編輯]。 在文字方塊中輸入 4996 (請勿包含 'C' 前置詞。如需詳細資訊,請參閱 移植以使用安全 CRT。
Windows API 或過時 SDK 變更所造成的錯誤
多年來,已新增 Windows API 和資料類型,有時也會進行變更或移除。 此外,不屬於核心操作系統的其他 SDK 也來來去。 較舊的程式可能包含對已不存在之 API 的呼叫。 它們也可能包含不再支援之其他Microsoft SDK 中的 API 呼叫。 您可能會看到舊版 Microsoft SDK 遺失 Windows API 或 API 的相關錯誤。 API 有可能由較新的、更安全的函式移除或取代。
Windows API 檔會列出支援的作業系統下限或上限。 如需特定 Windows API 的相關信息,請在傳統型 Windows 應用程式的 API 索引中查閱。
Windows 版本
升級直接或間接使用 Windows API 的程式時,您必須決定支援的最小 Windows 版本。 在大部分情況下,Windows 7 是不錯的選擇。 如需詳細資訊,請參閱標頭檔問題。 WINVER
巨集會定義您的程式應執行的最舊 Windows 版本。 如果您的 MFC 程式設定 WINVER
為 0x0501 (Windows XP),您會收到警告,因為 MFC 不再支援 XP,即使編譯程式工具組本身有 XP 模式也一樣。 Windows XP 的編譯程式工具組支援已於 Visual Studio 2017 中結束。
如需詳細資訊,請參閱 更新目標視窗版本 和 更多過期的頭檔。
ATL/MFC
ATL 和 MFC 是相當穩定的 API,但偶而會進行變更。 如需詳細資訊,請參閱 Visual C++變更歷程記錄 2003 - 2015、Visual Studio 中 Visual C++ 的新功能,以及 Visual Studio 中的C++一致性改善。
已在 MSVCRTD.lib 中定義的 LNK 2005 _DllMain@12
MFC 應用程式中可能會發生此錯誤。 這指出 CRT 程式庫與 MFC 程式庫之間的順序問題。 MFC 必須先連結,才能提供 new
和 delete
運算符。 若要修正錯誤,請使用 /NODEFAULTLIB
參數忽略這些預設連結庫: MSVCRTD.lib
和 mfcs140d.lib
。 然後新增這些與其他相依性相同的連結庫。
32 與 64 位元
如果您的原始程式代碼是針對 32 位系統所編譯,您可以選擇建立 64 位版本,而不是新的 32 位應用程式。。 一般而言,您應該先以 32 位元模式編譯程式,然後嘗試 64 位元。 針對 64 位元進行編譯十分簡單,但在部分情況下,它可以顯示 32 位元組建所隱藏的 Bug。
此外,您應該注意與指標大小、時間和大小值以及 和函式中printf
scanf
的大小特定格式規範相關的可能編譯時間和運行時間問題。 如需詳細資訊,請參閱設定 64 位、x64 目標和一般 Visual C++ 64 位移轉問題的 Visual C++。 如需更多移轉秘訣,請參閱 64 位 Windows 的程式設計指南。
Unicode 與 MBCS/ASCII
在標準化 Unicode 之前,許多程式會使用多位元組位元集 (MBCS) 來代表未包含在 ASCII 字元集中的字元。 在較舊的 MFC 專案中,MBCS 是預設設定。 當您升級這類程式時,您會看到建議改用 Unicode 的警告。 如果您決定轉換成 Unicode 並不值得開發成本,您可以選擇停用或忽略警告。 若要在解決方案中的所有專案停用此項目,請開啟 [檢視]>[屬性管理員],並選取您要停用此警告的所有專案,然後以滑鼠右鍵按一下選取的項目,再選擇 [屬性]。 在 [屬性頁] 對話方塊中,選取 [組態屬性]>[C/C++]>[進階]。 在 [停用特定警告] 屬性中,開啟下拉式箭頭並選擇 [編輯]。 在文字方塊中輸入 4996 (請勿包含 'C' 前置詞。選擇 [ 確定 ] 儲存屬性,然後選擇 [ 確定 ] 以儲存變更。
如需詳細資訊,請參閱從 MBCS 移植到 Unicode。 如需 MBCS 與 Unicode 的一般資訊,請參閱 Visual C++ 和國際化中的文字和字串。