共用方式為


如何:移轉至 /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++ 中的關鍵字(例如 virtualnewdeletebooltruefalse 等)。 這項變更通常可以使用簡單的搜尋和取代作業來完成。

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 指示詞的 中繼資料。

型別可視性

原生類型預設為 privateprivate在 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 程式設計的詳細資訊,請參閱:

另請參閱

混合 (原生和 Managed) 組件