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 pengalihan

Perlu diketahui bahwa memindahkan kode dari C++/CX ke C++/WinRT pada umumnya cukup mudah, dengan satu pengecualian, yaitu saat beralih dari task Parallel Patterns Library (PPL) ke korutin. Modelnya berbeda. Tidak ada pemetaan alami satu banding satu dari tugas PPL ke coroutine, dan tidak ada cara sederhana untuk memporting kode secara mekanis yang bisa diterapkan 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 sering melaporkan bahwa setelah berhasil melewati kendala dalam mem-porting kode asinkron mereka, sisa pekerjaan porting sebagian besar bersifat mekanis.

Memindahkan dalam satu langkah

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.

Note

Jika Anda memiliki proyek komponen Windows Runtime, maka memigrasikannya dalam satu tahap adalah satu-satunya pilihan. 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.

Memigrasikan 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 memindahkannya secara bertahap dari situ.

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 dapat berinteroperasi secara dua arah antara kode C++/WinRT Anda dan kode C++/CX apa pun yang belum Anda migrasikan.

Note

Baik C++/CX maupun SDK Windows mendeklarasikan tipe dalam namespace 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.

Memindahkan proyek XAML secara bertahap

Important

Untuk proyek yang menggunakan XAML, pada waktu tertentu semua jenis halaman XAML Anda harus sepenuhnya C++/CX atau sepenuhnya C++/WinRT. Anda masih dapat menggabungkan C++/CX dan C++/WinRT di luar tipe halaman XAML dalam proyek yang sama (dalam model dan viewmodel Anda, serta di bagian 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>Tambahkan Item Baru...>Visual C++>Halaman Kosong (C++/WinRT).

Atau, Anda dapat menggunakan komponen Windows Runtime (WRC) untuk memisahkan kode dari proyek XAML C++/CX saat Anda mem-portingnya.

  • 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 migrasi yang Anda pilih (migrasi sekaligus atau secara bertahap), langkah pertama adalah menyiapkan proyek Anda untuk dimigrasikan. Berikut adalah ringkasan dari apa yang telah kami jelaskan dalam Strategi porting terkait jenis proyek yang akan Anda gunakan sebagai titik awal, dan cara menyiapkannya.

  • Pemortaan dalam satu langkah. 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.
  • Memigrasikan 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.
  • Memigrasikan proyek XAML secara bertahap. Buat proyek C++/WinRT baru, pindahkan file, dan port secara bertahap. Pada waktu tertentu, tipe halaman XAML Anda harus semuanya C++/WinRT atau semuanya 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 berkas 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 nama file *.h 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 Memindahkan kelas runtime ke 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 { ... }
A.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" ...>
A.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. Lazim dalam C++/CX untuk menggunakan direktif using bagi namespace yang Anda gunakan dengan menyebut namanya.

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 membahas namespace Windows.Media.Core, 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 berjenisTypedEventHandler<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 bagi namespace yang digunakan oleh markup XAML.

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

Menggunakan kelas Rectangle berarti Anda harus menambahkan direktif include ini.

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

Jika Anda lupa menyertakan file header, semuanya akan berhasil dikompilasi dengan baik, tetapi Anda akan mendapatkan kesalahan linker karena kelas consume_ tidak ditemukan.

Pengoperasian parameter

Saat menulis kode sumber C++/CX, Anda meneruskan tipe C++/CX ke parameter fungsi sebagai referensi hat (^).

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 memuat penunjuk antarmuka ke objek Windows Runtime yang mendasarinya. Saat Anda menyalin objek C++/WinRT, pengkompilasi menyalin penunjuk antarmuka yang dienkapsulasi, meningkatkan jumlah referensinya. Penghancuran akhir salinan melibatkan penurunan jumlah referensi. Jadi, tanggung overhead penyalinan hanya bila diperlukan.

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 mengonversi ke kode C++/WinRT yang setara, Anda bisa melakukannya sebagian besar dengan menghapus tanda topi (^), dan mengganti operator panah (->) dengan 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 bawaan untuk referensi hat C++/CX menginisialisasikannya ke nilai nul. Berikut adalah contoh kode C++/CX yang menunjukkan pembuatan variabel/field dengan tipe yang benar, tetapi belum diinisialisasi. Dengan kata lain, pada awalnya itu tidak merujuk ke TextBlock; kami bermaksud menetapkan referensinya 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 instance dari sebuah kelas

Anda bekerja dengan objek C++/CX menggunakan handle ke objek tersebut, yang umumnya dikenal sebagai referensi hat (^). 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 hat C++/CX menginisialisasinya ke 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.

Scenario 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);
Larik 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 bentuk singkat untuk membuat vektor berukuran tetap yang berisi referensi kosong (lihat tabel di atas), tidak ada bentuk singkat serupa untuk membuat array yang berisi referensi kosong. Anda harus mengulangi nullptr untuk setiap elemen dalam array. Jika jumlahnya terlalu sedikit, elemen sisanya akan dikonstruksi 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 dalam peta, maka buat entri baru dalam peta yang berisi kunci (dipindahkan, jika dapat dipindahkan) dan nilai yang dikonstruksi secara default, lalu kembalikan referensi ke nilai tersebut (yang kemudian dapat Anda timpa nilainya).

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

Mengubah kelas runtime dasar menjadi kelas turunan

Sering kali kita memiliki referensi ke kelas dasar yang diketahui merujuk ke objek bertipe turunan. Di C++/CX, Anda menggunakan dynamic_cast untuk mengonversi referensi ke kelas dasar menjadi referensi ke kelas turunan. dynamic_cast Itu benar-benar hanya panggilan tersembunyi ke QueryInterface. Berikut adalah contoh umum—Anda sedang menangani peristiwa perubahan properti dependensi, dan Anda ingin melakukan cast kembali dari DependencyObject ke tipe sebenarnya yang memiliki properti dependensi tersebut.

void BgLabelControl::OnLabelChanged(Microsoft::UI::Xaml::DependencyObject^ d, Microsoft::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(Microsoft::UI::Xaml::DependencyObject const& d, Microsoft::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

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

unsealed runtimeclass BasePage : Microsoft.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 menemui kesalahan seperti "Penggunaan tipe ini sebagai ekspresi tidak sah".

// 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 delegate Anda sebagai fungsi bebas atau sebagai pointer ke fungsi anggota. 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 kuasa 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 membungkus skalar menjadi 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;
Operation C++/CX C++/WinRT
Boxing o = 1;
o = "string";
o = box_value(1);
o = box_value(L"string");
Pembukaan Kotak 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.

Scenario C++/CX C++/WinRT
Lakukan unboxing pada bilangan bulat yang diketahui i = (int)o; i = unbox_value<int>(o);
Jika o null Platform::NullReferenceException Kecelakaan
Jika o bukan int yang dibungkus Platform::InvalidCastException Kecelakaan
Unbox int, gunakan fallback jika null; crash jika nilainya selain itu 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 hal merupakan jenis nilai, dan dalam hal lain merupakan jenis referensi. C++/CX dan C++/WinRT memperlakukan string secara berbeda.

Jenis ABI HSTRING merupakan penunjuk ke string dengan jumlah referensi. Tetapi tidak berasal dari IInspectable, jadi itu bukan objek secara teknis. Selain itu, HSTRING null mewakili string kosong. Boxing untuk hal-hal yang tidak diturunkan dari IInspectable dilakukan dengan membungkusnya 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 "".

Behavior C++/CX C++/WinRT
Deklarasi Object^ o;
String^ s;
IInspectable o;
hstring s;
Kategori jenis string Jenis referensi Jenis nilai
null HSTRING diproyeksikan sebagai (String^)nullptr hstring{}
Apakah null dan "" identik? Yes Yes
Keabsahan 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.

Operation C++/CX C++/WinRT
Beri kotak pada teks 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 nilainya bukan string.
s = unbox_value<hstring>(o);
Objek null mengalami crash.
Crash jika nilainya 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 bukan string akan menjadi nilai cadangan.
String kosong dipertahankan.

Operasi konkurensi dan asinkron

Pustaka Pola Paralel (PPL) (concurrency::task, misalnya) diperbarui untuk mendukung referensi hat 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 mengakses anggota privat dan elemen bernama dalam 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, pengikatan ke Boolean menampilkan true atau false di C++/CX, tetapi di C++/WinRT yang ditampilkan adalah Windows.Foundation.IReference`1<Boolean>.

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 (Visual Studio properti proyek C/C++>General>Consume Windows Runtime Extension>Yes (/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

Porting Platform::Agile^ ke 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.

Dalam C++/CX.

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

Di C++/WinRT (WinUI 3 menggunakan Microsoft::UI::Xaml::Window alih-alih CoreWindow).

winrt::agile_ref<Microsoft::UI::Xaml::Window> 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.

Migrasikan Platform::Exception^ ke winrt::hresult_error

Jenis Platform::Exception^ diproduksi dalam C++/CX saat API Windows Runtime 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.

Dalam 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 KESALAHAN_DIBATALKAN
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 padanannya di C++/WinRT.

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

Porting Platform::Object^ ke 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 };

Porting Platform::String^ ke winrt::hstring

Platform::String^ setara dengan tipe ABI HSTRING milik Windows Runtime. Untuk C++/WinRT, yang setara adalah winrt::hstring. Tetapi dengan C++/WinRT, Anda dapat memanggil API Windows Runtime 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::Data untuk mengambil string sebagai larik bergaya C bertipe const wchar_t* (misalnya, untuk meneruskannya ke std::wcout).

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

Untuk melakukan hal yang sama di C++/WinRT, Anda dapat menggunakan fungsi hstring::c_str untuk mendapatkan versi string bergaya C yang diakhiri dengan null, sama seperti pada 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.

Bahasa Konversi int menjadi string Ubah enum menjadi string
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);

Jika Anda mengonversi enum menjadi string, Anda perlu menyediakan 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));
        }
    }
}

Representasi string ini sering dimanfaatkan 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 menjalankan winrt::to_hstring pada properti yang diikat. Dalam kasus contoh kedua ( StatusEnum), Anda harus menyediakan kelebihan beban Winrt::to_hstring Anda sendiri, jika tidak, Anda akan mendapatkan kesalahan kompilator.

Perakitan string

C++/CX dan C++/WinRT mengandalkan std::wstringstream standar untuk membangun string.

Operation C++/CX C++/WinRT
Tambahkan string, pertahankan null stream.print(s->Data(), s->Length); stream << std::wstring_view{ s };
Tambahkan string, berhenti 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.

Operation 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" };
Konversikan dari std::wstring, dengan mempertahankan nilai 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
Konversikan ke std::wstring, dengan mempertahankan karakter 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

Note

Banyak topik C++/WinRT sedang dalam proses migrasi dari dokumentasi UWP ke bagian ini. Hingga migrasi selesai, tautan dalam daftar di bawah ini dapat mengarah ke bagian dokumen UWP. Proyeksi bahasa C++/WinRT sama untuk aplikasi UWP dan WinUI 3, sehingga konten berlaku di kedua konteks. Pola khusus UWP apa pun (seperti siklus hidup aplikasi atau API namespace Windows.UI) disebutkan secara eksplisit dalam artikel-artikel tersebut.