Pindah ke C++/WinRT dari C++/CX

Topik ini adalah yang pertama dalam seri yang menjelaskan bagaimana Anda dapat memindahkan kode sumber dalam proyek C++/CX Anda ke yang setara di C++/WinRT.

Jika proyek Anda juga menggunakan jenis Windows Runtime C++ Template Library (WRL), maka lihat Pindah ke C++/WinRT dari WRL.

Strategi untuk porting

Perlu diketahui bahwa porting dari C++/CX ke C++/WinRT umumnya mudah, dengan satu pengecualian berpindah dari tugas Parallel Patterns Library (PPL) ke koroutin. Modelnya berbeda. Tidak ada pemetaan alami satu-ke-satu dari tugas PPL ke koroutine, dan tidak ada cara sederhana untuk secara mekanis memindahkan kode yang berfungsi untuk semua kasus. Untuk bantuan terkait aspek porting khusus ini, dan opsi Anda untuk mengoperasikan antara kedua model, lihat Asinkron, dan interop antara C++/WinRT dan C++/CX.

Tim pengembangan secara rutin melaporkan bahwa setelah mereka lebih dari rintangan porting kode asinkron mereka, sisa pekerjaan porting sebagian besar mekanis.

Porting dalam satu pass

Jika Anda berada dalam posisi untuk dapat memindahkan seluruh proyek Anda dalam satu pass, maka Anda hanya memerlukan topik ini untuk info yang Anda butuhkan (dan Anda tidak memerlukan topik interop yang mengikuti yang satu ini). Sebaiknya Anda mulai dengan membuat proyek baru di Visual Studio menggunakan salah satu templat proyek C++/WinRT (lihat dukungan Visual Studio untuk C++/WinRT). Kemudian pindahkan file kode sumber Anda ke proyek baru tersebut, dan port semua kode sumber C++/CX ke C++/WinRT saat Anda melakukannya.

Atau, jika Anda lebih suka melakukan pekerjaan porting di proyek C++/CX yang ada, maka Anda harus menambahkan dukungan C++/WinRT ke dalamnya. Langkah-langkah yang Anda ikuti untuk melakukannya dijelaskan dalam Mengambil proyek C++/CX dan menambahkan dukungan C++/WinRT. Pada saat Anda selesai melakukan porting, Anda akan mengubah apa yang merupakan proyek C++/CX murni menjadi proyek C++/WinRT murni.

Catatan

Jika Anda memiliki proyek komponen Windows Runtime, maka porting dalam satu pass adalah satu-satunya opsi Anda. Proyek komponen Windows Runtime yang ditulis dalam C++ harus berisi semua kode sumber C++/CX, atau semua kode sumber C++/WinRT. Mereka tidak bisa berdampingan dalam jenis proyek ini.

Porting proyek secara bertahap

Dengan pengecualian proyek komponen Windows Runtime, seperti yang disebutkan di bagian sebelumnya, jika ukuran atau kompleksitas basis kode Anda membuatnya perlu untuk memindahkan proyek Anda secara bertahap, maka Anda akan memerlukan proses porting di mana untuk waktu C++/CX dan kode C++/WinRT ada berdampingan dalam proyek yang sama. Selain membaca topik ini, lihat juga Interop antara C++/WinRT dan C++/CX dan Asinkron, dan interop antara C++/WinRT dan C++/CX. Topik-topik tersebut memberikan contoh info dan kode yang menunjukkan cara mengoperasikan antara dua proyeksi bahasa.

Untuk menyiapkan proyek untuk proses porting bertahap, salah satu opsinya adalah menambahkan dukungan C++/WinRT ke proyek C++/CX Anda. Langkah-langkah yang Anda ikuti untuk melakukannya dijelaskan dalam Mengambil proyek C++/CX dan menambahkan dukungan C++/WinRT. Anda kemudian dapat port secara bertahap dari sana.

Opsi lain adalah membuat proyek baru di Visual Studio menggunakan salah satu templat proyek C++/WinRT (lihat dukungan Visual Studio untuk C++/WinRT). Lalu tambahkan dukungan C++/CX ke proyek tersebut. Langkah-langkah yang Anda ikuti untuk melakukannya dijelaskan dalam Mengambil proyek C++/WinRT dan menambahkan dukungan C++/CX. Anda kemudian dapat mulai memindahkan kode sumber Anda ke dalamnya, dan memindahkan beberapa kode sumber C++/CX ke C++/WinRT saat Anda melakukannya.

Dalam kedua kasus, Anda akan mengoperasikan (kedua cara) antara kode C++/WinRT dan kode C++/CX apa pun yang belum Anda porting.

Catatan

C ++/CX dan Windows SDK mendeklarasikan jenis di namespace layanan akar Windows. Jenis Windows yang diproyeksikan ke dalam C++/WinRT memiliki nama yang sepenuhnya memenuhi syarat yang sama dengan jenis Windows, tetapi ditempatkan di namespace C++ winrt . Namespace layanan yang berbeda ini memungkinkan Anda melakukan port dari C++/CX ke C++/WinRT dengan kecepatan Anda sendiri.

Porting proyek XAML secara bertahap

Penting

Untuk proyek yang menggunakan XAML, pada waktu tertentu semua jenis halaman XAML Anda harus sepenuhnya C++/CX atau sepenuhnya C++/WinRT. Anda masih dapat mencampur C++/CX dan C++/WinRT di luar jenis halaman XAML dalam proyek yang sama (dalam model dan viewmodel Anda, dan di tempat lain).

Untuk skenario ini, alur kerja yang kami sarankan adalah membuat proyek C++/WinRT baru dan menyalin kode sumber dan markup dari proyek C++/CX. Selama semua jenis halaman XAML Anda adalah C++/WinRT, maka Anda dapat menambahkan halaman XAML baru dengan Project>Add New Item...>Halaman Kosong C++>Visual (C++/WinRT).

Atau, Anda dapat menggunakan komponen Windows Runtime (WRC) untuk memperhitungkan kode dari proyek XAML C++/CX saat Anda memindahkannya.

  • Anda dapat membuat proyek C++/CX WRC baru, memindahkan sebanyak mungkin kode C++/CX ke dalam proyek tersebut, lalu mengubah proyek XAML menjadi C++/WinRT.
  • Atau Anda dapat membuat proyek C++/WinRT WRC baru, meninggalkan proyek XAML sebagai C++/CX, dan mulai porting C++/CX ke C++/WinRT dan memindahkan kode yang dihasilkan dari proyek XAML dan ke dalam proyek komponen.
  • Anda juga dapat memiliki proyek komponen C++/CX bersama proyek komponen C++/WinRT dalam solusi yang sama, mereferensikannya dari proyek aplikasi Anda, dan secara bertahap port dari satu ke yang lain. Sekali lagi, lihat Interop antara C++/WinRT dan C++/CX untuk detail selengkapnya tentang menggunakan dua proyeksi bahasa dalam proyeksi yang sama.

Langkah pertama dalam memindahkan proyek C++/CX ke C++/WinRT

Apa pun strategi porting Anda (porting dalam satu pass, atau porting secara bertahap), langkah pertama Anda adalah menyiapkan proyek Anda untuk porting. Berikut adalah rekap dari apa yang kami jelaskan dalam Strategi untuk porting dalam hal jenis proyek yang akan Anda mulai, dan cara menyiapkannya.

  • Porting dalam satu pass. Buat proyek baru di Visual Studio menggunakan salah satu templat proyek C++/WinRT. Pindahkan file dari proyek C++/CX Anda ke proyek baru tersebut, dan port kode sumber C++/CX.
  • Porting proyek non-XAML secara bertahap. Anda dapat memilih untuk menambahkan dukungan C++/WinRT ke proyek C++/CX Anda (lihat Mengambil proyek C++/CX dan menambahkan dukungan C++/WinRT), dan port secara bertahap. Atau Anda dapat memilih untuk membuat proyek C++/WinRT baru dan menambahkan dukungan C++/CX ke proyek tersebut (lihat Mengambil proyek C++/WinRT dan menambahkan dukungan C++/CX), memindahkan file, dan port secara bertahap.
  • Porting proyek XAML secara bertahap. Buat proyek C++/WinRT baru, pindahkan file, dan port secara bertahap. Pada waktu tertentu jenis halaman XAML Anda harus semua C++/WinRT atau semua C++/CX.

Sisa topik ini berlaku tidak peduli strategi porting mana yang Anda pilih. Ini berisi katalog detail teknis yang terlibat dalam kode sumber porting dari C++/CX ke C++/WinRT. Jika Anda melakukan porting secara bertahap, kemungkinan Anda juga ingin melihat Interop antara C++/WinRT dan C++/CX dan Asinkron, dan interop antara C++/WinRT dan C++/CX.

Konvensi penamaan file

File markup XAML

Asal file C++/CX C++/WinRT
File XAML pengembang MyPage.xaml
MyPage.xaml.h
MyPage.xaml.cpp
MyPage.xaml
MyPage.h
MyPage.cpp
MyPage.idl (lihat di bawah)
File XAML yang dihasilkan MyPage.xaml.g.h
MyPage.xaml.g.hpp
MyPage.xaml.g.h
MyPage.xaml.g.hpp
MyPage.g.h

Perhatikan bahwa C++/WinRT menghapus .xaml dari *.h nama file dan *.cpp .

C++/WinRT menambahkan file pengembang tambahan, file Midl (.idl). C++/CX secara otomatis membuat file ini secara internal, menambahkan setiap anggota publik dan terlindungi. Di C++/WinRT, Anda menambahkan dan menulis file sendiri. Untuk detail selengkapnya, contoh kode, dan panduan penulisan IDL, lihat kontrol XAML; ikat ke properti C++/WinRT.

Lihat Juga Memperhitungkan kelas runtime ke dalam file Midl (.idl)

Kelas runtime

C++/CX tidak memberlakukan pembatasan pada nama file header Anda; adalah umum untuk menempatkan beberapa definisi kelas runtime ke dalam satu file header, terutama untuk kelas kecil. Tetapi C++/WinRT mengharuskan setiap kelas runtime memiliki file header sendiri yang dinamai sesuai dengan nama kelas.

C++/CX C++/WinRT
Common.h
ref class A { ... }
ref class B { ... }
Common.idl
runtimeclass A { ... }
runtimeclass B { ... }
H
namespace implements {
  struct A { ... };
}
B.h
namespace implements {
  struct B { ... };
}

Kurang umum (tetapi masih legal) di C++/CX adalah menggunakan file header bernama berbeda untuk kontrol kustom XAML. Anda harus mengganti nama file header ini agar sesuai dengan nama kelas.

C++/CX C++/WinRT
A.xaml
<Page x:Class="LongNameForA" ...>
A.xaml
<Page x:Class="LongNameForA" ...>
H
partial ref class LongNameForA { ... }
LongNameForA.h
namespace implements {
  struct LongNameForA { ... };
}

Persyaratan file header

C++/CX tidak mengharuskan Anda untuk menyertakan file header khusus apa pun, karena secara internal membuat file header dari .winmd file. Umum di C++/CX menggunakan using direktif untuk namespace yang Anda gunakan berdasarkan nama.

using namespace Windows::Media::Playback;

String^ NameOfFirstVideoTrack(MediaPlaybackItem^ item)
{
    return item->VideoTracks->GetAt(0)->Name;
}

Direktif using namespace Windows::Media::Playback memungkinkan kita menulis MediaPlaybackItem tanpa awalan namespace. Kami juga menyentuh Windows.Media.Core namespace layanan, karena item->VideoTracks->GetAt(0) mengembalikan Windows.Media.Core.VideoTrack. Tetapi kami tidak perlu mengetikkan nama VideoTrack di mana saja, jadi kami tidak memerlukan arahan using Windows.Media.Core .

Tetapi C++/WinRT mengharuskan Anda untuk menyertakan file header yang sesuai dengan setiap namespace yang Anda gunakan, bahkan jika Anda tidak menamainya.

#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();
}

Di sisi lain, meskipun peristiwa MediaPlaybackItem.AudioTracksChanged berjenis TypedEventHandler<MediaPlaybackItem, Windows.Foundation.Collections.IVectorChangedEventArgs>, kami tidak perlu menyertakan winrt/Windows.Foundation.Collections.h karena kami tidak menggunakan peristiwa tersebut.

C++/WinRT juga mengharuskan Anda untuk menyertakan file header untuk namespace layanan yang digunakan oleh markup XAML.

<!-- MainPage.xaml -->
<Rectangle Height="400"/>

Menggunakan kelas Persegi panjang berarti Anda harus menambahkan menyertakan ini.

// MainPage.h
#include <winrt/Windows.UI.Xaml.Shapes.h>

Jika Anda lupa file header, maka semuanya akan dikompilasi dengan baik tetapi Anda akan mendapatkan kesalahan linker karena consume_ kelas hilang.

Pengoperasian parameter

Saat menulis kode sumber C++/CX, Anda meneruskan jenis C++/CX sebagai parameter fungsi sebagai referensi topi (^).

void LogPresenceRecord(PresenceRecord^ record);

Di C++/WinRT, untuk fungsi sinkron, Anda harus menggunakan const& parameter secara default. Itu akan menghindari salinan dan overhead yang saling mengunci. Tetapi coroutine Anda harus menggunakan pass-by-value untuk memastikan bahwa mereka menangkap berdasarkan nilai dan menghindari masalah seumur hidup (untuk detail selengkapnya, lihat Operasi konkurensi dan asinkron dengan C++/WinRT).

void LogPresenceRecord(PresenceRecord const& record);
IASyncAction LogPresenceRecordAsync(PresenceRecord const record);

Objek C++/WinRT pada dasarnya adalah nilai yang menyimpan penunjuk antarmuka ke objek Windows Runtime yang mendukung. Saat Anda menyalin objek C++/WinRT, pengkompilasi menyalin penunjuk antarmuka yang dienkapsulasi, meningkatkan jumlah referensinya. Penghancuran akhir salinan melibatkan penurunan jumlah referensi. Jadi, hanya dikenakan overhead salinan bila perlu.

Referensi variabel dan bidang

Saat menulis kode sumber C++/CX, Anda menggunakan variabel topi (^) untuk mereferensikan objek Windows Runtime, dan operator panah (->) untuk mendereferensikan variabel topi.

IVectorView<User^>^ userList = User::Users;

if (userList != nullptr)
{
    for (UINT32 iUser = 0; iUser < userList->Size; ++iUser)
    ...

Saat mentransfer ke kode C++/WinRT yang setara, Anda bisa mendapatkan jalan panjang dengan menghapus topi, dan mengubah operator panah (->) ke operator titik (.). Jenis proyeksi C++/WinRT adalah nilai, dan bukan penunjuk.

IVectorView<User> userList = User::Users();

if (userList != nullptr)
{
    for (UINT32 iUser = 0; iUser < userList.Size(); ++iUser)
    ...

Konstruktor default untuk referensi topi C++/CX menginisialisasinya menjadi null. Berikut adalah contoh kode C++/CX di mana kita membuat variabel/bidang dari jenis yang benar, tetapi yang tidak diinisialisasi. Dengan kata lain, awalnya tidak merujuk ke TextBlock; kami ingin menetapkan referensi nanti.

TextBlock^ textBlock;

class MyClass
{
    TextBlock^ textBlock;
};

Untuk yang setara dalam C++/WinRT, lihat Inisialisasi tertunda.

Properti

Ekstensi bahasa C++/CX mencakup konsep properti. Saat menulis kode sumber C++/CX, Anda dapat mengakses properti seolah-olah itu adalah bidang. C++ standar tidak memiliki konsep properti sehingga, di C++/WinRT, Anda memanggil dapatkan dan atur fungsi.

Dalam contoh berikut, XboxUserId, UserState, PresenceDeviceRecords, dan Size adalah semua properti.

Mengambil nilai dari properti

Berikut adalah cara Anda mendapatkan nilai properti di C++/CX.

void Sample::LogPresenceRecord(PresenceRecord^ record)
{
    auto id = record->XboxUserId;
    auto state = record->UserState;
    auto size = record->PresenceDeviceRecords->Size;
}

Kode sumber C++/WinRT yang setara memanggil fungsi dengan nama yang sama dengan properti , tetapi tanpa parameter.

void Sample::LogPresenceRecord(PresenceRecord const& record)
{
    auto id = record.XboxUserId();
    auto state = record.UserState();
    auto size = record.PresenceDeviceRecords().Size();
}

Perhatikan bahwa fungsi PresenceDeviceRecords mengembalikan objek Windows Runtime yang memiliki fungsi Ukuran . Karena objek yang dikembalikan juga merupakan jenis proyeksi C++/WinRT, kami mendereferensikan menggunakan operator titik untuk memanggil Ukuran.

Mengatur properti ke nilai baru

Mengatur properti ke nilai baru mengikuti pola serupa. Pertama, di C++/CX.

record->UserState = newValue;

Untuk melakukan yang setara di C++/WinRT, Anda memanggil fungsi dengan nama yang sama dengan properti , dan meneruskan argumen.

record.UserState(newValue);

Membuat instans dari kelas

Anda bekerja dengan objek C++/CX melalui handel ke objek tersebut, umumnya dikenal sebagai referensi topi (^). Anda membuat objek baru melalui kata kunci, yang pada gilirannya ref new memanggil RoActivateInstance untuk mengaktifkan instans baru kelas runtime.

using namespace Windows::Storage::Streams;

class Sample
{
private:
    Buffer^ m_gamerPicBuffer = ref new Buffer(MAX_IMAGE_SIZE);
};

Objek C++/WinRT adalah nilai; sehingga Anda dapat mengalokasikannya pada tumpukan, atau sebagai bidang objek. Anda tidak pernah menggunakan ref new (atau new) untuk mengalokasikan objek C++/WinRT. Di balik layar, RoActivateInstance masih dipanggil.

using namespace winrt::Windows::Storage::Streams;

struct Sample
{
private:
    Buffer m_gamerPicBuffer{ MAX_IMAGE_SIZE };
};

Jika sumber daya mahal untuk diinisialisasi, maka umum untuk menunda inisialisasinya sampai benar-benar diperlukan. Seperti yang telah disebutkan, konstruktor default untuk referensi topi C++/CX menginisialisasinya menjadi 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;
};

Kode yang sama di-port ke C++/WinRT. Perhatikan penggunaan konstruktor std::nullptr_t . Untuk informasi selengkapnya tentang konstruktor tersebut, lihat Inisialisasi tertunda.

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 };
};

Bagaimana konstruktor default memengaruhi koleksi

Jenis koleksi C++ menggunakan konstruktor default, yang dapat mengakibatkan konstruksi objek yang tidak diinginkan.

Skenario C++/CX C++/WinRT (salah) C++/WinRT (benar)
Variabel lokal, awalnya kosong TextBox^ textBox; TextBox textBox; // Creates a TextBox! TextBox textBox{ nullptr };
Variabel anggota, awalnya kosong class C {
  TextBox^ textBox;
};
class C {
  TextBox textBox; // Creates a TextBox!
};
class C {
  TextBox textbox{ nullptr };
};
Variabel global, awalnya kosong TextBox^ g_textBox; TextBox g_textBox; // Creates a TextBox! TextBox g_textBox{ nullptr };
Vektor referensi kosong std::vector<TextBox^> boxes(10); // Creates 10 TextBox objects!
std::vector<TextBox> boxes(10);
std::vector<TextBox> boxes(10, nullptr);
Mengatur nilai dalam peta 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);
Array referensi kosong TextBox^ boxes[2]; // Creates 2 TextBox objects!
TextBox boxes[2];
TextBox boxes[2] = { nullptr, nullptr };
Pasangkan std::pair<TextBox^, String^> p; // Creates a TextBox!
std::pair<TextBox, String> p;
std::pair<TextBox, String> p{ nullptr, nullptr };

Selengkapnya tentang kumpulan referensi kosong

Setiap kali Anda memiliki Platform::Array^ (lihat Port Platform::Array^) di C++/CX, Anda memiliki pilihan untuk memindahkannya ke std::vector di C++/WinRT (sebenarnya, kontainer apa pun yang berdekatan) daripada membiarkannya sebagai array. Ada keuntungan untuk memilih std::vector.

Misalnya, meskipun ada singkatan untuk membuat vektor referensi kosong berukuran tetap (lihat tabel di atas), tidak ada kekurangan seperti itu untuk membuat array referensi kosong. Anda harus mengulangi nullptr untuk setiap elemen dalam array. Jika Anda memiliki terlalu sedikit, maka ekstra akan dibangun secara default.

Untuk vektor, Anda dapat mengisinya dengan referensi kosong pada inisialisasi (seperti pada tabel di atas), atau Anda dapat mengisinya dengan referensi kosong pascainisialisasi dengan kode seperti ini.

std::vector<TextBox> boxes(10); // 10 default-constructed TextBoxes.
boxes.resize(10, nullptr); // 10 empty references.

Selengkapnya tentang contoh std::map

Operator [] subskrip untuk std::map berulah seperti ini.

  • Jika kunci ditemukan di peta, kembalikan referensi ke nilai yang ada (yang dapat Anda timpa).
  • Jika kunci tidak ditemukan di peta, buat entri baru di peta yang terdiri dari kunci (dipindahkan, jika dapat dipindahkan) dan nilai yang dibangun secara default, dan kembalikan referensi ke nilai (yang kemudian dapat Anda timpa).

Dengan kata lain, [] operator selalu membuat entri di peta. Ini berbeda dari C#, Java, dan JavaScript.

Mengonversi dari kelas runtime dasar ke yang diturunkan

Adalah umum untuk memiliki referensi-ke-dasar yang Anda ketahui mengacu pada objek dari jenis turunan. Di C++/CX, Anda menggunakan dynamic_cast untuk mentransmisikan referensi ke basis ke referensi ke turunan. dynamic_cast Itu benar-benar hanya panggilan tersembunyi ke QueryInterface. Berikut adalah contoh umum—Anda menangani peristiwa yang diubah properti dependensi, dan Anda ingin mentransmisikan kembali dari DependencyObject ke jenis aktual yang memiliki properti dependensi.

void BgLabelControl::OnLabelChanged(Windows::UI::Xaml::DependencyObject^ d, Windows::UI::Xaml::DependencyPropertyChangedEventArgs^ e)
{
    BgLabelControl^ theControl{ dynamic_cast<BgLabelControl^>(d) };

    if (theControl != nullptr)
    {
        // succeeded ...
    }
}

Kode C++/WinRT yang setara menggantikan dynamic_cast dengan panggilan ke fungsi IUnknown::try_as , yang merangkum QueryInterface. Anda juga memiliki opsi untuk memanggil IUnknown::as, sebagai gantinya, yang melemparkan pengecualian jika kueri untuk antarmuka yang diperlukan (antarmuka default jenis yang Anda minta) tidak dikembalikan. Berikut adalah contoh kode 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 ...
    }
}

Kelas turunan

Untuk memperoleh dari kelas runtime, kelas dasar harus dapat disusupi. C++/CX tidak mengharuskan Anda mengambil langkah khusus apa pun untuk membuat kelas Anda dapat disusupi, tetapi C++/WinRT tidak. Anda menggunakan kata kunci yang tidak disegel untuk menunjukkan bahwa Anda ingin kelas Anda dapat digunakan sebagai kelas dasar.

unsealed runtimeclass BasePage : Windows.UI.Xaml.Controls.Page
{
    ...
}
runtimeclass DerivedPage : BasePage
{
    ...
}

Di kelas header implementasi, Anda harus menyertakan file header kelas dasar sebelum menyertakan header yang dibuat secara otomatis untuk kelas turunan. Jika tidak, Anda akan mendapatkan kesalahan seperti "Penggunaan ilegal dari jenis ini sebagai ekspresi".

// 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>
    {
        ...
    }
}

Penanganan peristiwa dengan delegasi

Berikut adalah contoh umum penanganan peristiwa di C++/CX, menggunakan fungsi lambda sebagai delegasi dalam kasus ini.

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.
});

Ini setara dengan 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.
});

Alih-alih fungsi lambda, Anda dapat memilih untuk mengimplementasikan delegasi Anda sebagai fungsi bebas, atau sebagai pointer-to-member-function. Untuk informasi selengkapnya, lihat Menangani peristiwa dengan menggunakan delegasi di C++/WinRT.

Jika Anda melakukan porting dari basis kode C++/CX tempat peristiwa dan delegasi digunakan secara internal (bukan di seluruh biner), maka winrt::d elegate akan membantu Anda mereplikasi pola tersebut di C++/WinRT. Lihat juga Delegasi berparameter, sinyal sederhana, dan panggilan balik dalam proyek.

Mencabut delegasi

Di C++/CX, Anda menggunakan -= operator untuk mencabut pendaftaran peristiwa sebelumnya.

myButton->Click -= token;

Ini setara dengan C++/WinRT.

myButton().Click(token);

Untuk informasi dan opsi selengkapnya, lihat Mencabut delegasi terdaftar.

Boxing dan Unboxing

C++/CX secara otomatis kotak skalar ke dalam objek. C++/WinRT mengharuskan Anda untuk memanggil fungsi winrt::box_value secara eksplisit. Kedua bahasa mengharuskan Anda untuk membuka kotak secara eksplisit. Lihat Tinju dan buka kotak dengan C++/WinRT.

Dalam tabel berikut, kita akan menggunakan definisi ini.

C++/CX C++/WinRT
int i; int i;
String^ s; winrt::hstring s;
Object^ o; IInspectable o;
Operasi C++/CX C++/WinRT
Boxing o = 1;
o = "string";
o = box_value(1);
o = box_value(L"string");
Unboxing i = (int)o;
s = (String^)o;
i = unbox_value<int>(o);
s = unbox_value<winrt::hstring>(o);

C++/CX dan C# menimbulkan pengecualian jika Anda mencoba membuka kotak penunjuk null ke jenis nilai. C++/WinRT menganggap ini sebagai kesalahan pemrograman, dan mengalami crash. Di C++/WinRT, gunakan fungsi winrt::unbox_value_or jika Anda ingin menangani kasus di mana objek bukan dari jenis yang Anda pikirkan.

Skenario C++/CX C++/WinRT
Batalkan kotak bilangan bulat yang diketahui i = (int)o; i = unbox_value<int>(o);
Jika o null Platform::NullReferenceException Kecelakaan
Jika o bukan int berkotak Platform::InvalidCastException Kecelakaan
Batalkan kotak masuk, gunakan fallback jika null; crash jika ada yang lain i = o ? (int)o : fallback; i = o ? unbox_value<int>(o) : fallback;
Batalkan kotak int jika memungkinkan; gunakan fallback untuk hal lain auto box = dynamic_cast<IBox<int>^>(o);
i = box ? box->Value : fallback;
i = unbox_value_or<int>(o, fallback);

Tinju dan membuka kotak string

String dalam beberapa cara adalah jenis nilai, dan dengan cara lain jenis referensi. C++/CX dan C++/WinRT memperlakukan string secara berbeda.

HSTRING jenis ABI adalah penunjuk ke string yang dihitung referensi. Tetapi tidak berasal dari IInspectable, jadi itu bukan objek secara teknis. Selain itu, HSTRING null mewakili string kosong. Tinju hal-hal yang tidak berasal dari IInspectable dilakukan dengan membungkusnya di dalam IReference<T>, dan Windows Runtime menyediakan implementasi standar dalam bentuk objek PropertyValue (jenis kustom dilaporkan sebagai PropertyType::OtherType).

C++/CX mewakili string Windows Runtime sebagai jenis referensi; sementara C++/WinRT memproyeksikan string sebagai jenis nilai. Ini berarti bahwa string null kotak dapat memiliki representasi yang berbeda tergantung bagaimana Anda sampai di sana.

Selain itu, C++/CX memungkinkan Anda untuk mendereferensikan String null ^, dalam hal ini berperilaku seperti string "".

Perilaku C++/CX C++/WinRT
Deklarasi Object^ o;
String^ s;
IInspectable o;
hstring s;
Kategori jenis string Jenis referensi Jenis nilai
proyek HSTRING null sebagai (String^)nullptr hstring{}
Apakah null dan "" identik? Ya Ya
Validitas null s = nullptr;
s->Length == 0 (valid)
s = hstring{};
s.size() == 0 (valid)
Jika Anda menetapkan string null ke objek o = (String^)nullptr;
o == nullptr
o = box_value(hstring{});
o != nullptr
Jika Anda menetapkan "" ke objek o = "";
o == nullptr
o = box_value(hstring{L""});
o != nullptr

Tinju dasar dan pembukaan kotak.

Operasi C++/CX C++/WinRT
Kotak untai (karakter) o = s;
String kosong menjadi nullptr.
o = box_value(s);
String kosong menjadi objek non-null.
Batalkan kotak string yang diketahui s = (String^)o;
Objek null menjadi string kosong.
InvalidCastException jika bukan string.
s = unbox_value<hstring>(o);
Objek null mengalami crash.
Crash jika bukan string.
Batalkan kotak kemungkinan string s = dynamic_cast<String^>(o);
Objek null atau non-string menjadi string kosong.
s = unbox_value_or<hstring>(o, fallback);
Null atau non-string menjadi fallback.
String kosong dipertahankan.

Operasi konkurensi dan asinkron

Pustaka Pola Paralel (PPL) (konkurensi::task, misalnya) diperbarui untuk mendukung referensi topi C++/CX.

Untuk C++/WinRT, Anda harus menggunakan coroutines dan co_await sebagai gantinya. Untuk informasi selengkapnya, dan contoh kode, lihat Operasi konkurensi dan asinkron dengan C++/WinRT.

Mengkonsumsi objek dari markup XAML

Dalam proyek C++/CX, Anda dapat menggunakan anggota privat dan elemen bernama dari markup XAML. Tetapi di C++/WinRT, semua entitas yang digunakan dengan menggunakan ekstensi markup XAML {x:Bind} harus diekspos secara publik di IDL.

Selain itu, mengikat boolean ditampilkan true atau di C++/CX, tetapi menampilkan Windows.Foundation.IReference'1<Boolean> di C++/WinRTfalse.

Untuk informasi selengkapnya, dan contoh kode, lihat Menggunakan objek dari markup.

Memetakan jenis Platform C++/CX ke jenis C++/WinRT

C++/CX menyediakan beberapa jenis data di namespace platform . Jenis ini bukan C++standar, sehingga Anda hanya dapat menggunakannya saat mengaktifkan ekstensi bahasa Windows Runtime (properti proyek Visual Studio C/C++>General>Gunakan Ekstensi>Runtime Windows Ya (/ZW)). Tabel di bawah ini membantu Anda mem-port dari jenis Platform ke yang setara di C++/WinRT. Setelah Anda melakukannya, karena C++/WinRT adalah C++standar, Anda dapat menonaktifkan /ZW opsi .

C++/CX C++/WinRT
Platform::Agile^ winrt::agile_ref
Platform::Array^ Lihat Port Platform::Array^
Platform::Exception^ winrt::hresult_error
Platform::InvalidArgumentException^ winrt::hresult_invalid_argument
Platform::Object^ winrt::Windows::Foundation::IInspectable
Platform::String^ winrt::hstring

Port Platform::Agile^ to winrt::agile_ref

Jenis Platform::Agile^ di C++/CX mewakili kelas Windows Runtime yang dapat diakses dari utas apa pun. Setara C++/WinRT adalah winrt::agile_ref.

Di C++/CX.

Platform::Agile<Windows::UI::Core::CoreWindow> m_window;

Di C++/WinRT.

winrt::agile_ref<Windows::UI::Core::CoreWindow> m_window;

Port Platform::Array^

Dalam kasus di mana C++/CX mengharuskan Anda menggunakan array, C++/WinRT memungkinkan Anda menggunakan kontainer yang berdekatan. Lihat Bagaimana konstruktor default memengaruhi koleksi karena alasan mengapa std::vector adalah pilihan yang baik.

Jadi, setiap kali Anda memiliki Platform::Array^ di C++/CX, opsi porting Anda termasuk menggunakan daftar inisialisasi, std::array, atau std::vector. Untuk informasi selengkapnya, dan contoh kode, lihat Daftar inisialisasi standar dan array dan vektor Standar.

Port Platform::Exception^ to winrt::hresult_error

Jenis Platform::Exception^ diproduksi di C++/CX saat API Runtime Windows mengembalikan HRESULT non S_OK. Setara C++/WinRT adalah winrt::hresult_error.

Untuk port ke C++/WinRT, ubah semua kode yang menggunakan Platform::Exception^ untuk menggunakan winrt::hresult_error.

Di C++/CX.

catch (Platform::Exception^ ex)

Di C++/WinRT.

catch (winrt::hresult_error const& ex)

C++/WinRT menyediakan kelas pengecualian ini.

Jenis pengecualian Kelas dasar HRESULT
winrt::hresult_error panggil hresult_error::to_abi
winrt::hresult_access_denied winrt::hresult_error E_ACCESSDENIED
winrt::hresult_canceled winrt::hresult_error ERROR_CANCELLED
winrt::hresult_changed_state winrt::hresult_error E_CHANGED_STATE
winrt::hresult_class_not_available winrt::hresult_error CLASS_E_CLASSNOTAVAILABLE
winrt::hresult_illegal_delegate_assignment winrt::hresult_error E_ILLEGAL_DELEGATE_ASSIGNMENT
winrt::hresult_illegal_method_call winrt::hresult_error E_ILLEGAL_METHOD_CALL
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

Perhatikan bahwa setiap kelas (melalui kelas dasar hresult_error) menyediakan fungsi to_abi, yang mengembalikan HRESULT kesalahan, dan fungsi pesan, yang mengembalikan representasi string HRESULT tersebut.

Berikut adalah contoh melemparkan pengecualian di C++/CX.

throw ref new Platform::InvalidArgumentException(L"A valid User is required");

Dan setara dalam C++/WinRT.

throw winrt::hresult_invalid_argument{ L"A valid User is required" };

Port Platform::Object^ to winrt::Windows::Foundation::IInspectable

Seperti semua jenis C++/WinRT, winrt::Windows::Foundation::IInspectable adalah jenis nilai. Berikut adalah cara Anda menginisialisasi variabel dari jenis tersebut menjadi null.

winrt::Windows::Foundation::IInspectable var{ nullptr };

Port Platform::String^ ke winrt::hstring

Platform::String^ setara dengan jenis Windows Runtime HSTRING ABI. Untuk C++/WinRT, yang setara adalah winrt::hstring. Tetapi dengan C++/WinRT, Anda dapat memanggil WINDOWS Runtime API menggunakan jenis string lebar Pustaka Standar C++ seperti std::wstring, dan/atau literal string lebar. Untuk detail selengkapnya, dan contoh kode, lihat Penanganan string di C++/WinRT.

Dengan C++/CX, Anda dapat mengakses properti Platform::String::D ata untuk mengambil string sebagai array C-style const wchar_t* (misalnya, untuk meneruskannya ke std::wcout).

auto var{ titleRecord->TitleName->Data() };

Untuk melakukan hal yang sama dengan C++/WinRT, Anda dapat menggunakan fungsi hstring::c_str untuk mendapatkan versi string gaya C yang dihentikan null, seperti yang Anda bisa dari std::wstring.

auto var{ titleRecord.TitleName().c_str() };

Dalam hal menerapkan API yang mengambil atau mengembalikan string, Anda biasanya mengubah kode C++/CX apa pun yang menggunakan Platform::String^ untuk menggunakan winrt::hstring sebagai gantinya.

Berikut adalah contoh API C++/CX yang mengambil string.

void LogWrapLine(Platform::String^ str);

Untuk C++/WinRT, Anda dapat mendeklarasikan API tersebut di MIDL 3.0 seperti ini.

// LogType.idl
void LogWrapLine(String str);

Toolchain C++/WinRT kemudian akan menghasilkan kode sumber untuk Anda yang terlihat seperti ini.

void LogWrapLine(winrt::hstring const& str);

ToString()

Jenis C++/CX menyediakan metode Object::ToString .

int i{ 2 };
auto s{ i.ToString() }; // s is a Platform::String^ with value L"2".

C++/WinRT tidak secara langsung menyediakan fasilitas ini, tetapi Anda dapat beralih ke alternatif.

int i{ 2 };
auto s{ std::to_wstring(i) }; // s is a std::wstring with value L"2".

C++/WinRT juga mendukung winrt::to_hstring untuk sejumlah jenis yang terbatas. Anda harus menambahkan kelebihan beban untuk jenis tambahan apa pun yang ingin Anda stringifikasi.

Language Stringify int Enum stringify
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);

Dalam kasus stringifying enum, Anda harus memberikan implementasi 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));
        }
    }
}

Stringifikasi ini sering digunakan secara implisit oleh pengikatan data.

<TextBlock>
You have <Run Text="{Binding FlowerCount}"/> flowers.
</TextBlock>
<TextBlock>
Most recent status is <Run Text="{x:Bind LatestOperation.Status}"/>.
</TextBlock>

Pengikatan ini akan melakukan winrt::to_hstring properti terikat. Dalam kasus contoh kedua ( StatusEnum), Anda harus menyediakan kelebihan beban Winrt::to_hstring Anda sendiri, jika tidak, Anda akan mendapatkan kesalahan pengkompilasi.

Pembangun string

C++/CX dan C++/WinRT tangguhkan ke std standar ::wstringstream untuk pembuatan string.

Operasi C++/CX C++/WinRT
Tambahkan string, pertahankan null stream.print(s->Data(), s->Length); stream << std::wstring_view{ s };
Tambahkan string, hentikan pada null pertama stream << s->Data(); stream << s.c_str();
Ekstrak hasil ws = stream.str(); ws = stream.str();

Contoh lainnya

Dalam contoh di bawah ini, ws adalah variabel jenis std::wstring. Selain itu, sementara C++/CX dapat membuat Platform::String dari string 8-bit, C++/WinRT tidak melakukannya.

Operasi C++/CX C++/WinRT
Membuat string dari harfiah String^ s = "hello";
String^ s = L"hello";
// winrt::hstring s{ "hello" }; // Doesn't compile
winrt::hstring s{ L"hello" };
Konversi dari std::wstring, mempertahankan 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
Konversi dari std::wstring, hentikan pada null pertama 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
Konversi ke std::wstring, mempertahankan null std::wstring ws{ s->Data(), s->Length };
ws = std::wstring(s>Data(), s->Length);
std::wstring ws{ s };
ws = s;
Konversi ke std::wstring, berhenti pada null pertama std::wstring ws{ s->Data() };
ws = s->Data();
std::wstring ws{ s.c_str() };
ws = s.c_str();
Teruskan harfiah ke metode Method("hello");
Method(L"hello");
// Method("hello"); // Doesn't compile
Method(L"hello");
Teruskan std::wstring ke metode Method(ref new String(ws.c_str(),
  (uint32_t)ws.size()); // Stops on first null
Method(ws);
// param::winrt::hstring accepts std::wstring_view

API penting