共用方式為


潛在升級問題概觀 (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 所使用的工具組為二進位相容。 如需詳細資訊,請參閱 Visual Studio 版本 之間的 C++ 二進位相容性。

當您將應用程式從 Visual Studio 2013 或之前升級至較新版本時,通常建議且必須升級應用程式連結的所有程式庫和 DLL。 您必須具有原始程式碼的存取權,或者程式庫廠商必須提供以相同主要版本的編譯器編譯的新二進位檔案。 如果符合其中一個條件,則您可以略過處理二進位檔相容性詳細資料的這一節。 如果兩者都不是這種情況,則程式庫可能無法在升級的應用程式中運作。 本節中的資訊將協助您瞭解是否可以繼續升級。

工具組

.obj.lib 檔案格式已妥善定義且很少變更。 有時會針對這些檔案格式進行新增,但這些新增通常不會影響較新工具組使用較舊工具組所產生的物件檔案和程式庫。 主要例外狀況是,如果您使用 /GL 編譯 (神秘le 程式優化)。 如果您使用 編譯 /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。 如需詳細資訊,請參閱 Visual Studio 版本 之間的 C++ 二進位相容性。

如果您的目的檔包含具有 C++ 連結的外部符號,則該目的檔可能未正確地連結到以不同主要工具組版本所產生的目的檔。 有許多可能的結果:連結可能會完全失敗(例如,如果名稱裝飾已變更)。 連結可能會成功,但應用程式可能會在執行時間失敗(例如,如果類型配置已變更)。 或者您的應用程式可能會繼續運作,而且不會有任何問題。 另請注意,雖然 C++ ABI 不穩定,但 COM 所需的 C ABI 和 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 版標準 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 執行時間標頭所建置的靜態程式庫,建議您依此循序執行下列動作:

  1. 使用新版 Visual Studio 和通用 CRT 標頭重建靜態程式庫,以支援連結到通用 CRT。 這個方法完全受到支援,也是最佳選項。

  2. 如果您無法(或不想)重建靜態程式庫,您可以嘗試與 legacy_stdio_definitions.lib 連結。 如果它符合靜態程式庫的連結時間相依性,您會想要在二進位檔中使用時徹底測試靜態程式庫。 請確定它不會受到對通用 CRT 所做的任何 行為變更造成負面影響。

  3. 您的靜態程式庫相依性可能不符合 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_twchar_t 為原生類型)

(在 Microsoft Visual C++ 6.0 和更早版本中, wchar_t 未實作為內建類型。在 中 wchar.h 宣告為 的 typedef。 unsigned shortC++ 標準需要 wchar_t 是內建類型。 使用 typedef 版本可能會造成可攜性問題。 如果您從舊版 Visual Studio 升級,並看到編譯器錯誤 C2664,因為程式碼嘗試隱含地轉換成 wchar_tunsigned 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_tint64_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 - 2015Visual Studio 中的 C++ 一致性改善,以查看這些文章是否包含任何其他資訊。 如果錯誤LNK2019,請確定函式尚未移除。 否則,如果您確定函式仍然存在,而且呼叫程式碼正確,請檢查您的專案是否使用 /NODEFAULTLIB 。 如果是,您必須更新程式庫清單,以使用新的通用 (UCRT) 程式庫。 如需詳細資訊,請參閱上方的<程式庫和相依性>一節。

如果錯誤涉及 printfscanf ,請確定您不私下定義任一函式,而不包含 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 必須先連結,才能提供 newdelete 運算子。 若要修正錯誤,請使用 /NODEFAULTLIB 參數忽略這些預設程式庫: MSVCRTD.libmfcs140d.lib 。 然後新增這些與其他相依性相同的程式庫。

32 與 64 位元

如果您的原始程式碼是針對 32 位系統所編譯,您可以選擇建立 64 位版本,而不是新的 32 位應用程式。。 一般而言,您應該先以 32 位元模式編譯程式,然後嘗試 64 位元。 針對 64 位元進行編譯十分簡單,但在部分情況下,它可以顯示 32 位元組建所隱藏的 Bug。

此外,您應該注意與指標大小、時間和大小值以及 和 函式中 printfscanf 的大小特定格式規範相關的可能編譯時間和執行時間問題。 如需詳細資訊,請參閱 設定 Visual C++ for 64 位、x64 目標和 一般 Visual C++ 64 位移轉問題 。 如需更多移轉秘訣,請參閱 64 位 Windows 的程式設計指南。

Unicode 與 MBCS/ASCII

在標準化 Unicode 之前,許多程式會使用多位元組字元集 (MBCS) 來代表未包含在 ASCII 字元集中的字元。 在較舊的 MFC 專案中,MBCS 是預設設定。 當您升級這類程式時,您會看到建議改用 Unicode 的警告。 如果您決定轉換成 Unicode 並不值得開發成本,您可以選擇停用或忽略警告。 若要在解決方案中的所有專案停用此項目,請開啟 [檢視]>[屬性管理員],並選取您要停用此警告的所有專案,然後以滑鼠右鍵按一下選取的項目,再選擇 [屬性]。 在 [屬性頁] 對話方塊中,選取 [組態屬性]>[C/C++]>[進階]。 在 [停用特定警告] 屬性中,開啟下拉式箭頭並選擇 [編輯]。 在文字方塊中輸入 4996 (請勿包含 'C' 前置詞。選擇 [ 確定 ] 儲存屬性,然後選擇 [ 確定 ] 以儲存變更。

如需詳細資訊,請參閱從 MBCS 移植到 Unicode。 如需 MBCS 與 Unicode 的一般資訊,請參閱 Visual C++ 和國際化 中的文字和 字串。

另請參閱

從舊版 Visual C++ 升級專案
Visual Studio 中的 C++ 一致性改善