如何:移轉至 /clr
本文討論使用 /clr
編譯原生程式碼時所發生的問題。 (如需詳細資訊,請參閱 /clr (Common Language Runtime Compilation) 。) /clr
允許原生 C++ 程式碼除了其他原生 C++ 程式碼之外,還叫用和從 .NET 元件叫用。 如需使用 /clr
編譯的優點詳細資訊,請參閱 混合式(原生和 Managed)元件 和 原生和 .NET 互通性 。
使用 編譯程式庫專案的已知問題 /clr
使用 編譯程式庫專案 /clr
時,Visual Studio 包含一些已知問題:
您的程式碼可能會在執行時間使用
CRuntimeClass::FromName
查詢類型。 不過,如果類型位於 MSIL DLL 中(使用/clr
編譯),在 Managed DLL 中執行靜態建構函式之前,對 的呼叫FromName
可能會失敗。 (如果FromName
呼叫是在 Managed DLL 中執行程式碼之後發生,您就不會看到這個問題。若要解決此問題,您可以強制建構 Managed 靜態建構函式:在 Managed DLL 中定義函式、匯出函式,並從原生 MFC 應用程式叫用函式。 例如:// MFC extension DLL Header file: __declspec( dllexport ) void EnsureManagedInitialization () { // managed code that won't be optimized away System::GC::KeepAlive(System::Int32::MaxValue); }
使用 Visual C++ 編譯
在專案中的任何模組上使用 /clr
之前,請先編譯原生專案,並將原生專案與 Visual Studio 連結。
依照順序遵循下列步驟,提供編譯的最簡單路徑 /clr
。 請務必在這些步驟之後編譯並執行您的專案。
從舊版 Visual Studio 升級
如果您要從舊版升級 Visual Studio,您可能會在 Visual Studio 中看到與增強標準 C++ 一致性相關的編譯器錯誤。
使用舊版 Visual Studio 建置的專案也應該先在沒有 的情況下 /clr
編譯。 Visual Studio 現在已提高標準 C++ 一致性,以及一些重大變更。 可能需要最注意的變更是 CRT 中的安全性功能。 使用 CRT 的程式碼可能會產生取代警告。 您可以隱藏這些警告,但建議移轉至新的 CRT 增強型版本的 CRT 函 式,因為它們可提供更佳的安全性,而且可能會顯示程式碼中的安全性問題。
從適用于 C++ 的 Managed 擴充功能更新
在 Visual Studio 2005 和更新版本中,使用 Managed Extensions for C++ 撰寫的程式碼將不會在 下 /clr
編譯。
將 C 程式碼轉換為 C++
雖然 Visual Studio 會編譯 C 檔案,但必須將它們轉換成 C++ 以進行 /clr
編譯。 實際檔案名不需要變更;您可以使用 /Tp
(請參閱 /Tc
、、 /Tp
/TC
、 /TP
(指定來源檔案類型) 。 雖然 C++ 原始程式碼檔案是必要的 /clr
,但不需要重構程式碼以使用物件導向架構。
C 程式碼在編譯為 C++ 檔案時可能需要變更。 C++ 型別安全性規則是嚴格的,因此必須使用轉換明確類型轉換。 例如,malloc 會傳回 void 指標,但可以使用轉換指派給 C 中任何類型的指標:
int* a = malloc(sizeof(int)); // C code
int* b = (int*)malloc(sizeof(int)); // C++ equivalent
函式指標在 C++ 中也絕對具有型別安全,因此下列 C 程式碼需要修改。 在 C++ 中,最好建立 typedef
定義函式指標類型的 ,然後使用該類型來轉換函式指標:
NewFunc1 = GetProcAddress( hLib, "Func1" ); // C code
typedef int(*MYPROC)(int); // C++ equivalent
NewFunc2 = (MYPROC)GetProcAddress( hLib, "Func2" );
C++ 也需要先建立原型或完整定義函式,才能參考或叫用函式。
C 程式碼中使用的識別碼必須重新命名為 C++ 中的關鍵字(例如 virtual
、 new
、 delete
、 bool
、 true
、 false
等)。 這項變更通常可以使用簡單的搜尋和取代作業來完成。
COMObj1->lpVtbl->Method(COMObj, args); // C code
COMObj2->Method(args); // C++ equivalent
重新設定專案設定
在 Visual Studio 中編譯和執行專案之後,您應該為 /clr
建立新的專案組態,而不是修改預設組態。 /clr
與某些編譯器選項不相容。 建立個別的組態可讓您將專案建置為原生或受控。 在 [屬性頁] 對話方塊中選取時 /clr
,會停用與 不相容 /clr
的專案設定。 (如果 /clr
稍後未選取,則不會自動還原停用的選項。
建立新的專案組態
您可以使用 [新增專案組態] 對話方塊 ( Build > Configuration Manager > Active Solution Configuration > New ) 中的 [複製設定 From ] 選項,根據現有的專案設定建立專案組態。 針對 [偵錯] 組態建立組態的複本,一次用於發行組態。 接著,後續的變更只能套用至 /clr
特定組態,讓原始專案組態保持不變。
使用自訂建置規則的專案可能需要額外的注意。
此步驟對於使用 makefiles 的專案有不同的影響。 在此情況下,可以設定個別的組建目標,或從原始複本建立編譯的特定 /clr
版本。
變更專案設定
/clr
您可以遵循 /clr(Common Language Runtime 編譯) 中的 指示,在開發環境中選取。 如先前所述,此步驟會自動停用衝突的專案設定。
注意
從 Visual Studio 2003 升級 Managed 程式庫或 Web 服務專案時,編譯 /Zl
程式選項會新增至 命令列 屬性頁。 這會導致LNK2001錯誤。 從 [命令列 ] 屬性頁移除 /Zl
,以解決錯誤。 如需詳細資訊,請參閱 /Zl
(省略預設程式庫名稱) 和 設定編譯器和建置屬性 。
針對使用 makefiles 建置的專案,新增之後 /clr
,必須手動停用不相容的編譯器選項。 如需與 不相容 /clr
之編譯器選項的資訊,請參閱 /clr
限制 。
先行編譯標頭
在 下 /clr
支援先行編譯標頭。 不過,如果您只使用 /clr
編譯部分 CPP 檔案(將其餘檔案編譯為原生),則需要某些變更。 產生的 /clr
先行編譯標頭與在沒有 /clr
的情況下產生的先行編譯標頭不相容,因為 /clr
會產生 和 需要中繼資料。 使用 編譯的 /clr
模組無法使用不包含中繼資料的先行編譯標頭,而且非 /clr
模組無法使用包含中繼資料的先行編譯標頭檔。
使用 編譯某些模組 /clr
的專案,最簡單的方式是完全停用先行編譯的標頭。 (在 [專案屬性頁] 對話方塊中,開啟 C/C++ 節點,然後選取 [先行編譯標頭 ]。然後將 Create/Use Precompiled Headers 屬性變更 為「不使用先行編譯標頭」。
不過,特別是針對大型專案,先行編譯標頭可提供更佳的編譯速度,因此不建議停用此功能。 在此情況下,最好設定 /clr
和非 /clr
檔案,以使用不同的先行編譯標頭。 您可以在一個步驟中設定它們:使用 方案總管 ,以多重選取要編譯的 /clr
模組。 以滑鼠右鍵按一下群組,然後選取 [ 屬性 ]。 然後,將建立/使用 PCH 透過檔案 和 先行編譯標頭檔 屬性變更 為分別使用不同的標頭檔名稱和 PCH 檔案。
修正錯誤
使用 編譯器代碼 /clr
可能會導致編譯器、連結器或執行時間錯誤。 本節討論最常見的問題。
中繼資料合併
不同的資料類型版本可能會導致連結器失敗,因為為兩種類型產生的中繼資料不相符。 (當您有條件地定義類型的成員,但所有使用類型之 CPP 檔案的條件都不同時,就會發生錯誤。在此情況下,連結器會失敗,只報告符號名稱和定義類型的第二個 OBJ 檔案的名稱。 您可能會發現輪替 OBJ 檔案傳送至連結器的順序會很有用,以探索其他資料類型版本的位置。
載入器鎖定死結
可能會發生「載入器鎖定死結」,但具決定性,而且會在執行時間偵測並報告。 如需詳細的背景、指引和解決方案,請參閱 混合元件的 初始化。
資料匯出
匯出 DLL 資料很容易出錯,而且不建議在程式碼中 /clr
。 這是因為在 DLL 的某些 Managed 部分執行之前,不保證 DLL 的資料區段初始化。 參考具有 #using
指示詞的 中繼資料。
型別可視性
原生類型預設為 private
。 private
在 DLL 外部看不到原生類型。 藉由新增 public
至這些類型來解決此錯誤。
浮點和對齊問題
__controlfp
Common Language Runtime 不支援。 (如需詳細資訊,請參閱 _control87
、 _controlfp
__control87_2
。CLR 也不尊重 align
。
COM 初始化
Common Language Runtime 會在模組初始化時自動初始化 COM(當 COM 自動初始化時,它會以 MTA 完成)。 因此,明確初始化 COM 會產生傳回碼,指出 COM 已經初始化。 當 CLR 已將 COM 初始化為另一個執行緒模型時,嘗試使用一個執行緒模型明確地初始化 COM,可能會導致您的應用程式失敗。
Common Language Runtime 預設會以 MTA 的形式啟動 COM;使用 /CLRTHREADATTRIBUTE
(設定 CLR 執行緒屬性) 來修改 COM 模型。
效能問題
當產生至 MSIL 的原生 C++ 方法間接呼叫時,您可能會看到效能降低(透過虛擬函式呼叫或使用函式指標)。 若要深入瞭解,請參閱 Double Thunking 。
當您從原生移至 MSIL 時,您會發現工作集的大小會增加。 之所以增加,是因為 Common Language Runtime 提供許多功能,以確保程式能夠正確執行。 /clr
如果您的應用程式未正確執行,您可能想要啟用預設 編譯器警告 (層級 1 和 3) C4793 。
程式在關機時當機
在某些情況下,CLR 可以在 Managed 程式碼完成執行之前關閉。 std::set_terminate
使用 和 SIGTERM
可能會導致關機。 如需詳細資訊,請參閱 signal
常數 和 set_terminate
。
使用新的 Visual C++ 功能
在應用程式編譯、連結和執行之後,您就可以在任何使用 編譯的 /clr
模組中使用 .NET 功能。 如需詳細資訊,請參閱 Component Extensions for Runtime Platforms。
如需 Visual C++ 中 .NET 程式設計的詳細資訊,請參閱: