本主題是一系列中的第一個主題,描述如何在 C++/CX 專案中將原始程式碼移植到其在 C++/WinRT中的對等專案。
如果您的專案也使用 Windows 執行階段 C++ 模板程式庫(WRL) 類型,請參閱 從 WRL 移至 C++/WinRT。
移植的策略
值得注意的是,從 C++/CX 移植到 C++/WinRT 通常很簡單,唯有從 平行運算程式庫(PPL) 的工作轉換至協程是個例外。 模型不同。 PPL 工作與協程之間沒有自然的一對一對應,也沒有簡單的方法可以機械地轉換適用於所有情況的程式碼。 如需移植此特定層面的說明,以及兩個模型之間互通的選項,請參閱 Asynchrony,以及 C++/WinRT 與 C++/CX之間的互操作性。
開發小組經常反映,一旦他們克服移植異步程式碼的困難,移植工作的其餘部分主要是機械式的。
在一次傳遞中移植
如果您能夠一次性移植整個專案,那麼您只需要本主題提供的資訊(而不需要後續的 interop 主題)。 建議您先使用其中一個 C++/WinRT 專案範本在 Visual Studio 中建立新專案(請參閱 Visual Studio 對 C++/WinRT的支援)。 然後將您的原始碼檔案移至該新專案,並在此過程中將所有 C++/CX 原始程式碼移植到 C++/WinRT。
或者,如果您想要在現有的C++/CX 專案中執行移植工作,則必須新增C++/WinRT 支援。 在「取用C++/CX專案並新增C++/WinRT支援」中,描述了您應遵循的步驟。 完成移植時,您將把純粹的C++/CX 專案變成純C++/WinRT 專案。
備註
如果您有 Windows 執行階段元件專案,則一次執行移植是唯一的選項。 以 C++ 撰寫的 Windows 執行時間元件項目必須包含所有C++/CX 原始程式碼,或所有C++/WinRT 原始程式碼。 它們無法並存於此項目類型中。
逐漸移植專案
除了如前一節所述的 Windows 執行時間元件專案之外,如果您的程式碼基底的大小或複雜度需要逐步移植專案,則您需要一個移植流程,其中 C++/CX 和 C++/WinRT 程式代碼會在一段時間內並存在同一個專案中。 除了閱讀本主題之外,另請參閱 C++/WinRT 與 C++/CX 與
若要讓專案準備好進行漸進式移植程式,其中一個選項是將C++/WinRT 支援新增至您的C++/CX 專案。 在「取用C++/CX專案並新增C++/WinRT支援」中,描述了您應遵循的步驟。 然後,您可以從該處逐漸移植。
另一個選項是在 Visual Studio 中使用其中一個 C++/WinRT 專案範本來建立新專案(請參閱 Visual Studio 對 C++/WinRT的支援)。 然後將C++/CX 支援新增至該專案。 遵循在 中描述的步驟,以 C++/WinRT 專案為基礎並新增 C++/CX 支援。 然後,您可以開始將原始程式碼移至該原始程式碼,並移植 C++/CX 原始程式碼的一些,以C++/WinRT 做為此動作。
不論是哪一種情況,您都會雙向互操作您的 C++/WinRT 程式碼與尚未移植的 C++/CX 程式碼。
備註
逐漸移植 XAML 專案
這很重要
對於使用 XAML 的專案,在任何指定時間,所有 XAML 頁面類型都必須完全C++/CX 或完全C++/WinRT。 您仍然可以在相同專案內的 XAML 頁面類型 外部混合
在此案例中,我們建議的工作流程是建立新的 C++/WinRT 專案,並從 C++/CX 專案複製原始程式碼和標記。 只要所有 XAML 頁面類型都是 C++/WinRT,您就可以使用 Project>[新增專案...]>Visual C++>空白頁面(C++/WinRT)來新增 XAML 頁面。
或者,您可以在移植 XAML C++/CX 專案時,使用 Windows 執行階段元件 (WRC) 將程式碼重構出專案。
- 您可以建立新的C++/CX WRC 專案、盡可能多地移動C++/CX 程式代碼到該專案,然後將 XAML 專案變更為 C++/WinRT。
- 或者,您可以建立新的 C++/WinRT WRC 專案、將 XAML 專案保留為 C++/CX,並開始將C++/CX 移植到 C++/WinRT,並將產生的程式代碼移出 XAML 專案,並移至元件專案中。
- 您也可以在同一個解決方案內有一個C++/CX元件專案,以及C++/WinRT 元件專案、從您的應用程式項目參考這兩個專案,然後逐漸從其中一個移植到另一個專案。 同樣地,如需在相同專案中使用兩種語言投影的詳細資訊,請參閱 C++/WinRT 與 C++/CX 之間的
Interop。
將C++/CX 專案移植到 C++/WinRT 的第一個步驟
無論您的移植策略為何(一次移植,或逐漸移植),您的第一個步驟是準備專案以進行移植。 以下是我們在 中所描述的將 移植的策略概要,針對您將要啟動的專案類型,以及如何設置它。
- 一次過完成移植。 使用其中一個 C++/WinRT 專案範本,在 Visual Studio 中建立新的專案。 將檔案從您的C++/CX專案移至該新專案,並移植C++/CX 原始程式碼。
- 漸進地移植非 XAML 專案。 您可以選擇將C++/WinRT 支援新增至您的C++/CX 專案(請參閱 取得C++/CX 專案並新增 C++/WinRT 支援),並逐漸移植。 或者,您可以選擇建立新的C++/WinRT 專案,並將C++/CX 支援新增至該專案(請參閱 取得C++/WinRT 專案並新增C++/CX 支援)、逐步移動檔案和移植。
- 逐步移植 XAML 項目。 建立新的 C++/WinRT 專案,移動檔案並逐步進行軟體移植。 在任何指定時間,您的 XAML 頁面類型都必須 所有C++/WinRT 或 所有C++/CX。
不論您選擇的移植策略為何,本主題的其餘部分都適用。 其中包含將原始程式碼從 C++/CX 移植到 C++/WinRT 相關技術詳細資料的目錄。 如果您逐漸移植,則您可能也會想要在 C++/WinRT 與 C++/CX 和
檔案命名慣例
XAML 標記檔案
| 檔案來源 | C++/CX | C++/WinRT |
|---|---|---|
| 開發人員 XAML 檔案 | MyPage.xaml MyPage.xaml.h MyPage.xaml.cpp |
MyPage.xaml MyPage.h MyPage.cpp MyPage.idl (請參閱下方) |
| 產生的 XAML 檔案 | MyPage.xaml.g.h MyPage.xaml.g.hpp |
MyPage.xaml.g.h MyPage.xaml.g.hpp MyPage.g.h |
請注意,C++/WinRT 會從 .xaml 和 *.h 檔名中移除 *.cpp。
C++/WinRT 會新增額外的開發人員檔案,Midl 檔案 (.idl)。 C++/CX 會在內部自動產生此檔案,並將每個公用和受保護的成員加入其中。 在 C++/WinRT 中,您可以自行新增和撰寫檔案。 如需更多詳細數據、程式代碼範例和撰寫IDL的逐步解說,請參閱 XAML 控制件;繫結至 C++/WinRT 屬性。
另請參閱將運行時間類別 分解成 Midl 檔案 (.idl)
執行時類別
C++/CX 不會對頭文件的名稱施加限制;通常會將多個運行時間類別定義放入單一頭檔,特別是針對小型類別。 但C++/WinRT 要求每個運行時間類別都有自己的頭檔,其名稱是以類別名稱命名。
| C++/CX | C++/WinRT |
|---|---|
Common.href class A { ... }ref class B { ... } |
Common.idlruntimeclass A { ... }runtimeclass B { ... } |
A.hnamespace implements {struct A { ... };} |
|
B.hnamespace implements {struct B { ... };} |
在 C++/CX 中,較不常見(但仍合法)的做法是為 XAML 自定義控制項使用不同名稱的標頭檔案。 您必須重新命名這些標頭檔,以符合類別名稱。
| C++/CX | C++/WinRT |
|---|---|
A.xaml<Page x:Class="LongNameForA" ...> |
A.xaml<Page x:Class="LongNameForA" ...> |
A.hpartial ref class LongNameForA { ... } |
LongNameForA.hnamespace implements {struct LongNameForA { ... };} |
標頭檔需求
C++/CX 不需要您包含任何特殊頭文件,因為它會在內部自動產生來自 .winmd 檔案的頭檔。 C++/CX 通常會針對您依名稱取用的命名空間使用 using 指示詞。
using namespace Windows::Media::Playback;
String^ NameOfFirstVideoTrack(MediaPlaybackItem^ item)
{
return item->VideoTracks->GetAt(0)->Name;
}
using namespace Windows::Media::Playback 指令讓我們能寫 MediaPlaybackItem 而不需要命名空間前綴。 我們也觸及 Windows.Media.Core 命名空間,因為 item->VideoTracks->GetAt(0) 傳回 Windows.Media.Core.VideoTrack。 但我們不需要在任何地方輸入 VideoTrack 名稱,因此我們不需要 using Windows.Media.Core 指示詞。
但是,C++/WinRT 需要您為您使用的每個命名空間包含對應的標頭檔,即使您沒有命名它也是如此。
#include <winrt/Windows.Media.Playback.h>
#include <winrt/Windows.Media.Core.h> // !!This is important!!
using namespace winrt;
using namespace Windows::Media::Playback;
winrt::hstring NameOfFirstVideoTrack(MediaPlaybackItem const& item)
{
return item.VideoTracks().GetAt(0).Name();
}
另一方面,即使 MediaPlaybackItem.AudioTracksChanged 事件的類型是 TypedEventHandler<MediaPlaybackItem、Windows.Foundation.Collections.IVectorChangedEventArgs>,我們仍不需要包含 winrt/Windows.Foundation.Collections.h,因為我們未使用該事件。
C++/WinRT 也要求您包含 XAML 標記所取用的命名空間的頭檔。
<!-- MainPage.xaml -->
<Rectangle Height="400"/>
使用 Rectangle 類別表示您必須新增此 include。
// MainPage.h
#include <winrt/Windows.UI.Xaml.Shapes.h>
如果您忘記頭檔,則所有專案都會編譯正常,但您會收到連結器錯誤,因為遺漏 consume_ 類別。
參數傳遞
撰寫 C++/CX 原始程式碼時,您會將 C++/CX 類型作為函式參數,以 hat(^)參考的形式進行傳遞。
void LogPresenceRecord(PresenceRecord^ record);
在 C++/WinRT 中,針對同步函式,您應該預設使用 const& 參數。 這可避免複製和相互鎖定的額外負荷。 但是您的協同程式應該使用傳遞值來確保它們依值擷取並避免存留期問題(如需詳細資訊,請參閱使用 C++/WinRT
void LogPresenceRecord(PresenceRecord const& record);
IASyncAction LogPresenceRecordAsync(PresenceRecord const record);
C++/WinRT 物件基本上是持有 Windows 運行時間物件支援介面指標的值。 當您複製C++/WinRT 物件時,編譯程式會複製封裝的介面指標,並遞增其參考計數。 複製的最終銷毀涉及到遞減參考計數。 因此,只有在需要時才會產生複本的額外負荷。
變數與字段參考
撰寫C++/CX 原始程式碼時,您可以使用 hat (^) 變數來參考 Windows 執行時間物件,並使用箭號 (->) 運算子來取值 hat 變數。
IVectorView<User^>^ userList = User::Users;
if (userList != nullptr)
{
for (UINT32 iUser = 0; iUser < userList->Size; ++iUser)
...
移植到對等的 C++/WinRT 程式代碼時,您可以移除帽子,並將箭頭運算符 (->) 變更為點運算符 (.)。 C++/WinRT 投影類型是值,而不是指標。
IVectorView<User> userList = User::Users();
if (userList != nullptr)
{
for (UINT32 iUser = 0; iUser < userList.Size(); ++iUser)
...
C++/CX hat 參考的預設構造函數會將它初始化為 null。 這裡有一個 C++/CX 程式碼範例,其中我們建立了一個正確類型但未初始化的變數或欄位。 換句話說,它一開始不會參考 TextBlock;我們稍後打算指派參考。
TextBlock^ textBlock;
class MyClass
{
TextBlock^ textBlock;
};
如需 C++/WinRT 中的等效實作,請參閱 延遲初始化。
性能
C++/CX 語言延伸模組包含屬性的概念。 撰寫C++/CX 原始程式碼時,您可以存取屬性,就像是字段一樣。 標準C++沒有屬性的概念,因此,在 C++/WinRT 中,您可以呼叫 get 和 set 函式。
在下列範例中,XboxUserId、UserState、PresenceDeviceRecords,Size 都是屬性。
從屬性擷取值
以下是您在 C++/CX 中取得屬性值的方式。
void Sample::LogPresenceRecord(PresenceRecord^ record)
{
auto id = record->XboxUserId;
auto state = record->UserState;
auto size = record->PresenceDeviceRecords->Size;
}
對等C++/WinRT 原始程式碼會呼叫與 屬性同名但不含參數的函式。
void Sample::LogPresenceRecord(PresenceRecord const& record)
{
auto id = record.XboxUserId();
auto state = record.UserState();
auto size = record.PresenceDeviceRecords().Size();
}
請注意,PresenceDeviceRecords 函式會傳回本身具有 Size 函式的 Windows 運行時間物件。 由於傳回的物件也是 C++/WinRT 投影類型,因此我們使用點運算符來存取並呼叫 Size。
將屬性設定為新值
將屬性設定為新的值會遵循類似的模式。 首先,在 C++/CX 中。
record->UserState = newValue;
若要在 C++/WinRT 中執行對等作業,請呼叫與 屬性同名的函式,並傳遞自變數。
record.UserState(newValue);
建立類別的實例
您可以透過句柄來處理C++/CX 物件,這通常稱為 hat (^) 參考。 您可以透過 ref new 關鍵詞建立新的 物件,進而呼叫 RoActivateInstance 來啟動運行時間類別的新實例。
using namespace Windows::Storage::Streams;
class Sample
{
private:
Buffer^ m_gamerPicBuffer = ref new Buffer(MAX_IMAGE_SIZE);
};
C++/WinRT 物件是一個值,因此您可以將它放置在堆疊上,或作為物件的欄位。 您 永遠不會 使用 ref new(或 new)來分配 C++/WinRT 物件。 在幕後,RoActivateInstance 仍在呼叫中。
using namespace winrt::Windows::Storage::Streams;
struct Sample
{
private:
Buffer m_gamerPicBuffer{ MAX_IMAGE_SIZE };
};
如果資源初始化的成本很高,通常會延遲初始化,直到實際需要為止。 如先前所述,C++/CX hat 參考的預設建構函式會將它初始化為 null。
using namespace Windows::Storage::Streams;
class Sample
{
public:
void DelayedInit()
{
// Allocate the actual buffer.
m_gamerPicBuffer = ref new Buffer(MAX_IMAGE_SIZE);
}
private:
Buffer^ m_gamerPicBuffer;
};
相同的程式碼被移植到 C++/WinRT。 請注意,使用 std::nullptr_t 建構函式。 如需該建構函式的詳細資訊,請參閱 延遲初始化。
using namespace winrt::Windows::Storage::Streams;
struct Sample
{
void DelayedInit()
{
// Allocate the actual buffer.
m_gamerPicBuffer = Buffer(MAX_IMAGE_SIZE);
}
private:
Buffer m_gamerPicBuffer{ nullptr };
};
默認建構函式如何影響集合
C++集合類型會使用預設建構函式,這可能會導致非預期的物件建構。
| 情境 | C++/CX | C++/WinRT (不正確) | C++/WinRT (正確) |
|---|---|---|---|
| 局部變數,一開始為空白 | TextBox^ textBox; |
TextBox textBox; // Creates a TextBox! |
TextBox textBox{ nullptr }; |
| 成員變數,一開始為空白 | class C {TextBox^ textBox;}; |
class C {TextBox textBox; // Creates a TextBox!}; |
class C {TextBox textbox{ nullptr };}; |
| 全域變數,一開始為空白 | TextBox^ g_textBox; |
TextBox g_textBox; // Creates a TextBox! |
TextBox g_textBox{ nullptr }; |
| 空參考的向量 | std::vector<TextBox^> boxes(10); |
// Creates 10 TextBox objects!std::vector<TextBox> boxes(10); |
std::vector<TextBox> boxes(10, nullptr); |
| 在地圖中設定值 | std::map<int, TextBox^> boxes;boxes[2] = value; |
std::map<int, TextBox> boxes;// Creates a TextBox at 2,// then overwrites it!boxes[2] = value; |
std::map<int, TextBox> boxes;boxes.insert_or_assign(2, value); |
| 空參考的陣列 | TextBox^ boxes[2]; |
// Creates 2 TextBox objects!TextBox boxes[2]; |
TextBox boxes[2] = { nullptr, nullptr }; |
| 雙 | std::pair<TextBox^, String^> p; |
// Creates a TextBox!std::pair<TextBox, String> p; |
std::pair<TextBox, String> p{ nullptr, nullptr }; |
進一步了解空參考集合
每當您在 C++/CX 中有 Platform::Array^(請參閱 Port Platform::Array^),您可以選擇將它移植到 C++/WinRT 中的 std::vector(事實上,任何連續容器),而不是將它保留為數組。 選擇 std::vector有優點。
例如,雖然有方法可以用速記來建立固定大小的空參考向量(請參閱上表),但沒有這樣的速記方法可以用來建立空參考的 陣列。 您必須針對陣列中的每個元素重複 nullptr。 如果數量不夠,那麼多餘的部分將被預設建構。
針對向量,您可以在初始化時以空的參考填滿它(如上表所示),或者您可以使用如下的程式代碼在初始化後填入空白參考。
std::vector<TextBox> boxes(10); // 10 default-constructed TextBoxes.
boxes.resize(10, nullptr); // 10 empty references.
深入瞭解 std::map 範例
[]
注標運算符的行為如下所示。
- 如果在映射中找到鍵值,請傳回現有值的引用(您可以覆寫)。
- 如果在地圖中找不到鍵,請在地圖中建立一個新的項目,其包含該鍵(如果可移動則移動),和預設建構的值,並傳回該值的參考,您可以透過該參考覆寫該值。
換句話說,[] 操作符一律會在映射中建立條目。 這與 C#、Java 和 JavaScript 不同。
從基底運行時間類別轉換成衍生的運行時間類別
通常您會擁有一個基礎類型的參考,並且知道它指向的是衍生類型的物件。 在 C++/CX 中,您可以使用 dynamic_cast 將 的基礎參考 轉換為衍生參考。
dynamic_cast 實際上只是一個對 QueryInterface的隱藏呼叫。 以下是一個典型的範例:您正在處理相依性屬性已變更事件,而且您想要從 DependencyObject 轉換回擁有相依性屬性的實際類型。
void BgLabelControl::OnLabelChanged(Windows::UI::Xaml::DependencyObject^ d, Windows::UI::Xaml::DependencyPropertyChangedEventArgs^ e)
{
BgLabelControl^ theControl{ dynamic_cast<BgLabelControl^>(d) };
if (theControl != nullptr)
{
// succeeded ...
}
}
對等C++/WinRT 程式代碼會以呼叫 dynamic_cast 函式來取代 ,此函式會封裝 QueryInterface。 您也可以選擇呼叫 IUnknown::as,如果查詢所需的介面(即您請求類型的預設介面)未被傳回,則會拋出例外狀況。 以下是C++/WinRT 程式代碼範例。
void BgLabelControl::OnLabelChanged(Windows::UI::Xaml::DependencyObject const& d, Windows::UI::Xaml::DependencyPropertyChangedEventArgs const& e)
{
if (BgLabelControlApp::BgLabelControl theControl{ d.try_as<BgLabelControlApp::BgLabelControl>() })
{
// succeeded ...
}
try
{
BgLabelControlApp::BgLabelControl theControl{ d.as<BgLabelControlApp::BgLabelControl>() };
// succeeded ...
}
catch (winrt::hresult_no_interface const&)
{
// failed ...
}
}
衍生類別
若要衍生自運行時類別,基類必須 具組合性。 C++/CX 不需要您採取任何特殊步驟來讓您的類別成為可組合的,但 C++/WinRT 則需要這樣做。 您可以使用 未密封關鍵詞,指出您想要讓類別作為基類使用。
unsealed runtimeclass BasePage : Windows.UI.Xaml.Controls.Page
{
...
}
runtimeclass DerivedPage : BasePage
{
...
}
在您實作的標頭類別中,您必須先包含基類標頭檔,再包含衍生類別的自動產生標頭。 否則,您會收到錯誤,例如「非法使用此類型做為表達式」。
// DerivedPage.h
#include "BasePage.h" // This comes first.
#include "DerivedPage.g.h" // Otherwise this header file will produce an error.
namespace winrt::MyNamespace::implementation
{
struct DerivedPage : DerivedPageT<DerivedPage>
{
...
}
}
使用委派進行事件處理
以下是處理 C++/CX 中事件的典型範例,在此案例中使用 Lambda 函式作為委派。
auto token = myButton->Click += ref new RoutedEventHandler([=](Platform::Object^ sender, RoutedEventArgs^ args)
{
// Handle the event.
// Note: locals are captured by value, not reference, since this handler is delayed.
});
這是 C++/WinRT 中的對等專案。
auto token = myButton().Click([=](IInspectable const& sender, RoutedEventArgs const& args)
{
// Handle the event.
// Note: locals are captured by value, not reference, since this handler is delayed.
});
與其使用 lambda 函式,您可以選擇將委派實作為自由函式,或作為指向成員函式的指標。 如需詳細資訊,請參閱 在 C++/WinRT中使用委派來處理事件。
如果您要從內部使用事件和委派函式的 C++/CX 程式碼基底進行移植(而非跨二進位檔),則 winrt::delegate 將協助您在 C++/WinRT 中複寫這種模式。 請參閱 專案中的參數化委派、簡單信號和回調。
撤銷委派
在 C++/CX 中,您可以使用 -= 運算符來撤銷先前的事件註冊。
myButton->Click -= token;
這是 C++/WinRT 中的對等專案。
myButton().Click(token);
如需詳細資訊和選項,請參閱 撤銷已註冊的委派。
值類型裝箱(Boxing)和拆箱(unboxing)
C++/CX 會自動將純量方塊放入 物件中。 C++/WinRT 需要您明確呼叫 winrt::box_value 函式。 這兩種語言都需要您明確地解包。 請參閱 使用 C++/WinRT 進行 Boxing 和 unboxing。
在下列數據表中,我們將使用這些定義。
| C++/CX | C++/WinRT |
|---|---|
int i; |
int i; |
String^ s; |
winrt::hstring s; |
Object^ o; |
IInspectable o; |
| 行動 | C++/CX | C++/WinRT |
|---|---|---|
| 拳擊 | o = 1;o = "string"; |
o = box_value(1);o = box_value(L"string"); |
| 拆箱 | i = (int)o;s = (String^)o; |
i = unbox_value<int>(o);s = unbox_value<winrt::hstring>(o); |
如果您嘗試將 Null 指標拆箱為值類型,C++/CX 和 C# 會拋出例外。 C++/WinRT 會將此視為程序設計錯誤,而且損毀。 在 C++/WinRT 中,如果您想要處理物件不是您所認為類型的案例,請使用 winrt::unbox_value_or 函式。
| 情境 | C++/CX | C++/WinRT |
|---|---|---|
| 對已知的整數解封裝 | i = (int)o; |
i = unbox_value<int>(o); |
| 如果 o 為 null | Platform::NullReferenceException |
崩潰 |
| 如果 o 不是封裝整數 | Platform::InvalidCastException |
崩潰 |
| 解封 int,如果為 null,則使用後援;如果是其他值則當機。 | i = o ? (int)o : fallback; |
i = o ? unbox_value<int>(o) : fallback; |
| 如果可能,請取消收件匣;針對任何其他專案使用後援 | auto box = dynamic_cast<IBox<int>^>(o);i = box ? box->Value : fallback; |
i = unbox_value_or<int>(o, fallback); |
封裝和拆裝字串
字串在某些方面是實值型別,而其他方式則是參考型別。 C++/CX 和 C++/WinRT 會以不同的方式處理字串。
ABI 類型 HSTRING 是一個指向具有參考計數的字串的指標。 但是它並非從 IInspectable派生,因此在技術上不算是 物件。 此外,null HSTRING 代表空字串。 未從 IInspectable 衍生的項目的封裝是透過將它們包裝在 IReference<T>中完成的,而 Windows 執行階段提供了 PropertyValue 物件形式的標準實作(自定義類型則會回報為 PropertyType::OtherType)。
C++/CX 代表 Windows 運行時間字串做為參考類型;而C++/WinRT 則會將字串投影為實值型別。 這表示封裝的空字串可能會有不同的表示方式,取決於您獲得它的方法。
此外,C++/CX 可讓您取值 null String^,在此情況下它的行為就像字串 ""。
| 行為 | C++/CX | C++/WinRT |
|---|---|---|
| 聲明 | Object^ o;String^ s; |
IInspectable o;hstring s; |
| 字串類型類別 | 參考類型 | 值類型 |
| null HSTRING 專案顯示為 | (String^)nullptr |
hstring{} |
null 和 "" 是否相同? |
是的 | 是的 |
| Null 的有效性 | s = nullptr;s->Length == 0 (有效) |
s = hstring{};s.size() == 0 (有效) |
| 如果您將 null 字串指派給物件 | o = (String^)nullptr;o == nullptr |
o = box_value(hstring{});o != nullptr |
如果您將 "" 分配給物件 |
o = "";o == nullptr |
o = box_value(hstring{L""});o != nullptr |
基本封箱和拆箱。
| 行動 | C++/CX | C++/WinRT |
|---|---|---|
| 將字串框成方塊 | o = s;空字串會變成 nullptr。 |
o = box_value(s);空字串會變成非 Null 物件。 |
| 將已知字串解包 | s = (String^)o;空對象會變成空字串。 如果不是字串,則會拋出 InvalidCastException。 |
s = unbox_value<hstring>(o);Null 對象當機。 若非字串,會當機。 |
| 解開可能的字串 | s = dynamic_cast<String^>(o);空值物件或非字串的值會變成空字串。 |
s = unbox_value_or<hstring>(o, fallback);Null 或非字串將成為回退選項。 保留空字符串。 |
並行和非同步作業
平行模式程式庫(PPL,例如concurrency::task) 已更新,以支援 C++/CX hat 參考。
針對 C++/WinRT,您應該改用協程和 co_await。 如需詳細資訊和程式碼範例,請參閱 使用 C++/WinRT 進行並行和異步操作。
從 XAML 標記取用物件
在 C++/CX 專案中,您可以透過 XAML 標記來存取私人成員和具名元素。 但在 C++/WinRT 中,所有使用 XAML {x:Bind} 標記延伸 的實體都必須在 IDL 中公開。
此外,繫結至布林值會在 C++/CX 中顯示 true 或 false,但在 C++/WinRT 中會顯示 Windows.Foundation.IReference`1<布林值>。
如需詳細資訊和程式代碼範例,請參閱 從標記取用物件。
將C++/CX 平臺 類型對應至 C++/WinRT 類型
C++/CX 在 Platform 命名空間中提供數種數據類型。 這些類型不是標準C++,因此您只能在啟用 Windows 執行階段語言擴充時使用它們(Visual Studio 專案屬性 C/C++>一般>使用 Windows 執行階段擴充功能>是 (/ZW))。 下表可協助您將 Platform 類型轉換為 C++/WinRT 中的等效類型。 完成後,因為 C++/WinRT 是標準C++,因此您可以關閉 [/ZW] 選項。
| C++/CX | C++/WinRT |
|---|---|
| Platform:敏捷^ | winrt::agile_ref |
| Platform::Array^ | 請參閱 埠 平台::Array^ |
| Platform::Exception^ | winrt::hresult_error |
| Platform::InvalidArgumentException^ | winrt::hresult_invalid_argument |
| Platform::Object^ | winrt::Windows::Foundation::IInspectable |
| Platform::String^ | winrt::hstring |
將 Platform::Agile^ 移植到 winrt::agile_ref
C++/CX 中的 Platform::Agile^ 類型代表可從任何線程存取的 Windows 運行時間類別。 C++/WinRT 等效是 winrt::agile_ref。
在 C++/CX 中。
Platform::Agile<Windows::UI::Core::CoreWindow> m_window;
在 C++/WinRT 中。
winrt::agile_ref<Windows::UI::Core::CoreWindow> m_window;
埠 Platform::Array^
如果 C++/CX 要求您使用陣列,C++/WinRT 可讓您使用任何連續容器。 請參閱 預設建構函式如何影響集合,原因為何 std::vector 是不錯的選擇。
因此,每當您在 C++/CX 中有 Platform::Array^,您的移植選項包括使用初始化列表、std::array或 std::vector。 如需詳細資訊和程式代碼範例,請參閱 標準初始化表達式清單 和 標準陣列和向量。
將 Platform::Exception^ 移植到 winrt::hresult_error
當 Windows 執行階段 API 傳回非 S_OK HRESULT 時,Platform::Exception^ 類型會在 C++/CX 中產生。 C++/WinRT 對等專案 winrt::hresult_error。
若要移植到 C++/WinRT,請變更使用 Platform::Exception^ 的所有程式碼,以使用 winrt::hresult_error。
在 C++/CX 中。
catch (Platform::Exception^ ex)
在 C++/WinRT 中。
catch (winrt::hresult_error const& ex)
C++/WinRT 提供這些例外狀況類別。
| 例外狀況類型 | 基類 | HRESULT |
|---|---|---|
| winrt::hresult_error | call hresult_error::to_abi | |
| winrt::hresult_access_denied | winrt::hresult_error | E_ACCESSDENIED |
| winrt::hresult_canceled | winrt::hresult_error | 錯誤已取消 |
| winrt::hresult_changed_state | winrt::hresult_error | E_STATE_CHANGED狀態改變 |
| winrt::hresult_class_not_available | winrt::hresult_error | CLASS_E_CLASSNOTAVAILABLE(類別不可用) |
| winrt::hresult_illegal_delegate_assignment | winrt::hresult_error | 非法委派指派錯誤 |
| winrt::hresult_illegal_method_call | winrt::hresult_error | 非法方法呼叫错误 |
| winrt::hresult_illegal_state_change | winrt::hresult_error | E_ILLEGAL_STATE_CHANGE |
| winrt::hresult_invalid_argument | winrt::hresult_error | E_INVALIDARG錯誤(無效參數) |
| winrt::hresult_no_interface | winrt::hresult_error | E_NOINTERFACE |
| winrt::hresult_not_implemented | winrt::hresult_error | E_NOTIMPL |
| winrt::hresult_out_of_bounds | winrt::hresult_error | E_BOUNDS |
| winrt::hresult_wrong_thread | winrt::hresult_error | RPC_E_WRONG_THREAD |
請注意,每個類別(透過 hresult_error 基類)都會提供 to_abi 函式,此函式會傳回錯誤的 HRESULT,以及傳回該 HRESULT 的字串表示法 訊息 函式。
以下是在 C++/CX 中擲回例外狀況的範例。
throw ref new Platform::InvalidArgumentException(L"A valid User is required");
和在 C++/WinRT 中的對應項目。
throw winrt::hresult_invalid_argument{ L"A valid User is required" };
將 Port Platform::Object^ 改為 winrt::Windows::Foundation::IInspectable
如同所有C++/WinRT 類型,winrt::Windows::Foundation::IInspectable 是實值類型。 以下說明如何將該類型的變數初始化為 Null。
winrt::Windows::Foundation::IInspectable var{ nullptr };
將 Platform::String^ 移植到 winrt::hstring
Platform:String^ 相當於 Windows 執行階段 HSTRING ABI 類型。 對於 C++/WinRT,等價的是 winrt::hstring。 但是,使用 C++/WinRT,您可以使用 C++ 標準函式庫的寬字元串類型來呼叫 Windows 執行階段 API,例如 std::wstring和寬字元串常值。 如需詳細資訊和程式代碼範例,請參閱 C++/WinRT中的
透過 C++/CX,您可以存取 Platform::String::D ata 屬性,將字串擷取為 C 樣式 const wchar_t* 陣組(例如,將它傳遞至 std::wcout)。
auto var{ titleRecord->TitleName->Data() };
若要使用 C++/WinRT 執行相同的動作,您可以使用 hstring::c_str 函式來取得 null 終止的 C 樣式字串版本,就像您可以從 std::wstring一樣。
auto var{ titleRecord.TitleName().c_str() };
在實作採用或傳回字串的 API 時,您通常會變更任何使用 platform::String^ C++
以下是採用字串的C++/CX API 範例。
void LogWrapLine(Platform::String^ str);
針對 C++/WinRT,您可以在 MIDL 3.0 中宣告該 API,如下所示。
// LogType.idl
void LogWrapLine(String str);
C++/WinRT 工具鏈接著會為您產生如下所示的原始程式碼。
void LogWrapLine(winrt::hstring const& str);
ToString()
C++/CX 類型提供 Object::ToString 方法。
int i{ 2 };
auto s{ i.ToString() }; // s is a Platform::String^ with value L"2".
C++/WinRT 不會直接提供這項功能,但您可以轉向替代方案。
int i{ 2 };
auto s{ std::to_wstring(i) }; // s is a std::wstring with value L"2".
C++/WinRT 也支援有限類型的 winrt::to_hstring。 您需要為想要字串化的任何其他類型添加多載。
| 語言 | 將整數轉換成字串(Stringify int) | 將列舉轉換為字串 |
|---|---|---|
| C++/CX | String^ result = "hello, " + intValue.ToString(); |
String^ result = "status: " + status.ToString(); |
| C++/WinRT | hstring result = L"hello, " + to_hstring(intValue); |
// must define overload (see below)hstring result = L"status: " + to_hstring(status); |
在將列舉轉為字串的情況下,您必須提供 winrt::to_hstring的實作。
namespace winrt
{
hstring to_hstring(StatusEnum status)
{
switch (status)
{
case StatusEnum::Success: return L"Success";
case StatusEnum::AccessDenied: return L"AccessDenied";
case StatusEnum::DisabledByPolicy: return L"DisabledByPolicy";
default: return to_hstring(static_cast<int>(status));
}
}
}
這些字串化通常由資料綁定隱含取用。
<TextBlock>
You have <Run Text="{Binding FlowerCount}"/> flowers.
</TextBlock>
<TextBlock>
Most recent status is <Run Text="{x:Bind LatestOperation.Status}"/>.
</TextBlock>
這些系結會執行系結屬性 winrt::to_hstring。 在第二個範例中(StatusEnum),您必須提供您自己版本的 winrt::to_hstring的多載,否則您會收到編譯器錯誤。
字串構建
C++/CX 和 C++/WinRT 使用標准的 std::wstringstream 來構建字串。
| 行動 | C++/CX | C++/WinRT |
|---|---|---|
| 附加字串,保留空值 | stream.print(s->Data(), s->Length); |
stream << std::wstring_view{ s }; |
| 附加字串,直到遇到第一個空值時停止 | stream << s->Data(); |
stream << s.c_str(); |
| 擷取結果 | ws = stream.str(); |
ws = stream.str(); |
其他範例
在下列範例中,
| 行動 | C++/CX | C++/WinRT |
|---|---|---|
| 從常值建構字串 | String^ s = "hello";String^ s = L"hello"; |
// winrt::hstring s{ "hello" }; // Doesn't compilewinrt::hstring s{ L"hello" }; |
| 從 std::wstring轉換,保留 null 字元 | String^ s = ref new String(ws.c_str(),(uint32_t)ws.size()); |
winrt::hstring s{ ws };s = winrt::hstring(ws);// s = ws; // Doesn't compile |
| 從 std::wstring轉換,並在遇到第一個空值時停止。 | String^ s = ref new String(ws.c_str()); |
winrt::hstring s{ ws.c_str() };s = winrt::hstring(ws.c_str());// s = ws.c_str(); // Doesn't compile |
| 轉換為 std::wstring,並保留空值 | std::wstring ws{ s->Data(), s->Length };ws = std::wstring(s>Data(), s->Length); |
std::wstring ws{ s };ws = s; |
| 轉換為 std::wstring,在第一個 null 時停止 | std::wstring ws{ s->Data() };ws = s->Data(); |
std::wstring ws{ s.c_str() };ws = s.c_str(); |
| 將字面值傳遞至方法 | Method("hello");Method(L"hello"); |
// Method("hello"); // Doesn't compileMethod(L"hello"); |
| 將 std::wstring 傳遞至方法 | Method(ref new String(ws.c_str(),(uint32_t)ws.size()); // Stops on first null |
Method(ws);// param::winrt::hstring accepts std::wstring_view |