Condividi tramite


Passare a C++/WinRT da C++/CX

Questo argomento è il primo di una serie che descrive come è possibile convertire il codice sorgente nel progetto C++/CX nell'equivalente in C++/WinRT.

Se il progetto utilizza anche i tipi Libreria modelli C++ per Windows Runtime, vedi Passare a C++/WinRT da WRL.

Strategie per la conversione

È importante tenere presente che la conversione da C++/CX a C++/WinRT è in genere semplice, con la sola eccezione del passaggio dalle attività della libreria PPL (Parallel Patterns Library) alle coroutine. I modelli sono diversi. Non esiste un mapping uno a uno naturale dalle attività della libreria PPL alle coroutine e non esiste un modo semplice per convertire meccanicamente il codice in modo che sia adatto a tutti i casi. Per informazioni su questo aspetto specifico della conversione e sulle opzioni per l'interoperabilità tra i due modelli, vedere Asincronia e interoperabilità tra C++/WinRT e C++/CX.

I team di sviluppo periodicamente segnalano che, una volta superati gli ostacoli tipici della conversione del codice asincrono, il resto dell'operazione di conversione è sostanzialmente meccanico.

Conversione in un unico passaggio

Se è possibile convertire l'intero progetto in un unico passaggio, questo argomento è sufficiente a coprire le informazioni utili. Non è quindi necessario leggere gli argomenti seguenti sull'interoperabilità. È consigliabile iniziare creando un nuovo progetto in Visual Studio usando uno dei modelli di progetto di C++/WinRT. Vedere Supporto di Visual Studio per C++/WinRT. Spostare quindi i file del codice sorgente nel nuovo progetto e convertire tutto il codice sorgente C++/CX in C++/WinRT.

In alternativa, se si preferisce eseguire l'operazione di conversione nel progetto C++/CX esistente, sarà necessario aggiungere il supporto di C++/WinRT. La procedura da seguire per questa operazione è descritta in Acquisizione di un progetto C++/CX e aggiunta del supporto di C++/WinRT. Al termine della conversione, un progetto C++/CX puro sarà stato trasformato in un progetto C++/WinRT puro.

Nota

Se si dispone di un progetto di componente Windows Runtime, la conversione in un unico passaggio è l'unica opzione. Un progetto di componente Windows Runtime scritto in C++ deve contenere tutto il codice sorgente C++/CX o tutto il codice sorgente C++/WinRT. I due codici non possono coesistere in questo tipo di progetto.

Conversione graduale di un progetto

Ad eccezione di un progetto di componente Windows Runtime, come indicato nella sezione precedente, se la dimensione o la complessità della base di codici rende necessario convertire gradualmente il progetto, è necessario un processo di conversione in cui per un certo periodo i codici C++/CX e C++/WinRT coesistono nello stesso progetto. Oltre a leggere questo argomento, vedere anche Interoperabilità tra C++/WinRT e C++/CX e Asincronia e interoperabilità tra C++/WinRT e C++/CX. Questi argomenti forniscono informazioni ed esempi di codice che illustrano l'interoperabilità tra le due proiezioni di linguaggio.

Per preparare un progetto per un processo di conversione graduale, un'opzione consiste nell'aggiungere il supporto di C++/WinRT al progetto C++/CX. La procedura da seguire per questa operazione è descritta in Acquisizione di un progetto C++/CX e aggiunta del supporto di C++/WinRT. A questo punto, è possibile eseguire la conversione gradualmente.

Un'altra opzione consiste nella creazione di un nuovo progetto in Visual Studio usando uno dei modelli di progetto di C++/WinRT. Vedere Supporto di Visual Studio per C++/WinRT. E quindi aggiungere il supporto di C++/CX al progetto. La procedura da seguire per questa operazione è descritta in Acquisizione di un progetto C++/WinRT e aggiunta del supporto di C++/CX. A questo punto, spostare il codice sorgente nel progetto e convertire una parte del codice sorgente C++/CX in C++/WinRT.

In ogni caso, si genera interoperabilità (entrambe le modalità) tra il codice C++/WinRT e qualsiasi codice C++/CX che non è ancora stato convertito.

Nota

Sia C++/CX che Windows SDK dichiarano tipi nello spazio dei nomi radice Windows . Un tipo Windows proiettato in C++/WinRT ha lo stesso nome qualificato completo del tipo Windows, ma non posizionato nello spazio dei nomi C++ winrt. Questi spazi dei nomi distinti ti consentono la conversione da C++/CX a C++/WinRT al tuo ritmo.

Conversione graduale di un progetto XAML

Importante

Per un progetto che usa XAML, in qualsiasi momento, tutti i tipi di pagina XAML devono essere interamente C++/CX o interamente C++/WinRT. È comunque possibile combinare C++/CX e C++/WinRT all'esterno dei tipi di pagina XAML nello stesso progetto, ad esempio nei modelli e nei modelli di visualizzazione.

Per questo scenario, il flusso di lavoro consigliato consiste nel creare un nuovo progetto C++/WinRT e copiare il codice sorgente e il markup dal progetto C++/CX. Finché tutti i tipi di pagina XAML sono C++/WinRT, è possibile aggiungere nuove pagine XAML con Progetto > Aggiungi nuovo elemento >Visual C++>Pagina vuota (C++/WinRT).

In alternativa, è possibile usare un componente Windows Runtime per escludere il codice dal progetto C++/CX XAML al momento del trasferimento.

  • È possibile creare un nuovo progetto di componente Windows Runtime in C++/CX, spostare quanto più codice C++/CX possibile nel progetto e quindi modificare il progetto XAML in C++/WinRT.
  • In alternativa, è possibile creare un nuovo progetto di componente Windows Runtime in C++/WinRT, lasciare il progetto XAML come C++/CX e iniziare la conversione del codice C++/CX in C++/WinRT spostando il codice risultante dal progetto XAML al progetto di componente.
  • Potresti anche avere un progetto di componente C++/CX accanto a un progetto di componente C++/WinRT all'interno della stessa soluzione, facendo riferimento a entrambi dal tuo progetto applicativo e ed eseguendo la conversione graduale dall'uno all'altro. Anche in questo caso, per altre informazioni dettagliate sull'uso delle proiezioni in due linguaggi nello stesso progetto, vedere Interoperabilità tra C++/WinRT e C++/CX.

Primi passaggi per la portabilità di un progetto C++/CX in C++/WinRT

Indipendentemente dalla strategia di conversione adottata (in un unico passaggio o graduale), il primo passaggio consiste nel preparare il progetto per la conversione. Ecco un riepilogo di ciò che è stato descritto in Strategie per la conversione in merito al tipo di progetto iniziale e alla configurazione.

  • Conversione in un unico passaggio. Creare un nuovo progetto in Visual Studio usando uno dei modelli di progetto di C++/WinRT. Spostare i file dal progetto C++/CX al nuovo progetto e convertire il codice sorgente C++/CX.
  • Conversione graduale di un progetto non XAML. È possibile scegliere di aggiungere il supporto di C++/WinRT al progetto C++/CX ed eseguire la conversione gradualmente. Vedere Acquisizione di un progetto C++/CX e aggiunta del supporto di C++/WinRT. In alternativa, è possibile scegliere di creare un nuovo progetto C++/WinRT e di aggiungere il supporto di C++/CX, spostare i file ed eseguire la conversione gradualmente. Vedere Acquisizione di un progetto C++/WinRT e aggiunta del supporto di C++/CX.
  • Conversione graduale di un progetto XAML. Creare un nuovo progetto C++/WinRT, spostarvi i file ed eseguire la conversione gradualmente. In qualsiasi momento, i tipi di pagina XAML devono essere interamente C++/WinRT o interamente C++/CX.

Il resto di questo argomento si applica indipendentemente dalla strategia di conversione scelta. Contiene un catalogo dei dettagli tecnici relativi alla conversione del codice sorgente da C++/CX a C++/WinRT. Se si esegue la conversione gradualmente, è probabile che si voglia vedere anche Interoperabilità tra C++/WinRT e C++/CX e Asincrona e interoperabilità tra C++/WinRT e C++/CX.

Convenzioni di denominazione dei file

File di markup XAML

Origine file C++/CX C++/WinRT
File XAML per sviluppatori MyPage.xaml
MyPage.xaml.h
MyPage.xaml.cpp
MyPage.xaml
MyPage.h
MyPage.cpp
MyPage.idl (vedi di seguito)
File XAML generati MyPage.xaml.g.h
MyPage.xaml.g.hpp
MyPage.xaml.g.h
MyPage.xaml.g.hpp
MyPage.g.h

Nota che C++/WinRT rimuove .xaml dai nomi dei file con estensione *.h e *.cpp.

C++/WinRT include un altro file per sviluppatori, il file Midl (.idl). C++/CX genera internamente questo file in modo automatico, aggiungendolo a ogni membro pubblico e protetto. In C++/WinRT il file viene aggiunto e modificato direttamente dallo sviluppatore. Per altri dettagli, esempi di codice e una procedura dettagliata sulla creazione di un file con estensione idl, vedi Controlli XAML, binding a una proprietà C++/WinRT.

Vedi anche Factoring delle classi di runtime nei file Midl (.idl)

Classi di runtime

C++/CX non impone restrizioni per i nomi dei file di intestazione. È prassi comune inserire più definizioni di classe di runtime in un unico file di intestazione, soprattutto per le classi di piccole dimensioni. C++/WinRT richiede tuttavia che ogni classe di runtime abbia uno specifico file di intestazione a cui è assegnato il nome della classe.

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

Meno comune (ma ancora valido) in C++/CX è l'uso di file di intestazione con un nome diverso per i controlli personalizzati XAML. Dovrai rinominare questi file di intestazione in modo che corrispondano al nome della classe.

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

Requisiti per i file di intestazione

C++/CX non richiede file di intestazione speciali, perché genera internamente i file di intestazione dai file .winmd in modo automatico. In C++/CX è prassi comune usare direttive using per gli spazi dei nomi utilizzati in base al nome.

using namespace Windows::Media::Playback;

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

La direttiva using namespace Windows::Media::Playback consente di scrivere MediaPlaybackItem senza un prefisso dello spazio dei nomi. Abbiamo toccato anche lo spazio dei nomi Windows.Media.Core perché item->VideoTracks->GetAt(0) restituisce un oggetto Windows.Media.Core.VideoTrack. Non abbiamo però dovuto digitare il nome VideoTrack e quindi la direttiva using Windows.Media.Core non è stata necessaria.

C++/WinRT richiede tuttavia l'inclusione di un file di intestazione corrispondente a ogni spazio dei nomi utilizzato, anche se tale spazio non viene nominato.

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

D'altra parte, anche se l'evento MediaPlaybackItem.AudioTracksChanged è di tipo TypedEventHandler<MediaPlaybackItem, Windows.Foundation.Collections.IVectorChangedEventArgs>, non dobbiamo includere winrt/Windows.Foundation.Collections.h perché tale evento non è stato usato.

Per C++/WinRT è inoltre necessario includere i file di intestazione per gli spazi dei nomi utilizzati dal markup XAML.

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

L'uso della classe Rectangle indica che è necessario aggiungere questa inclusione.

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

Se dimentichi un file di intestazione, la compilazione viene eseguita correttamente, ma vengono restituiti errori del linker a causa delle classi consume_ mancanti.

Passaggio di parametri

Durante la scrittura del codice sorgente C++/CX, i tipi C++/CX vengono passati come parametri di funzioni con riferimenti hat (^).

void LogPresenceRecord(PresenceRecord^ record);

In C++/WinRT, per le funzioni sincrone, è consigliabile utilizzare i parametri const& per impostazione predefinita. Ciò consente di evitare le copie e sovraccarico con interlock. Tuttavia, le coroutine devono utilizzare pass-by-value per assicurare l'acquisizione per valore ed evitare problemi di durata (per ulteriori informazioni, vedi Concorrenza e operazioni asincrone con C++/WinRT).

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

Un oggetto C++/WinRT è fondamentalmente un valore che contiene un puntatore all'interfaccia per il backup dell'oggetto Windows Runtime. Quando copi un oggetto C++/WinRT, il compilatore copia il puntatore di interfaccia incapsulato, incrementando il conteggio dei riferimenti. Un eventuale distruzione della copia comporta la riduzione del conteggio di riferimento. Pertanto, utilizza il sovraccarico di una copia solo quando necessario.

Riferimenti a variabili e campi

Durante la scrittura del codice sorgente C++/CX, utilizzerai le variabili hat (^) per fare riferimento a oggetti di Windows Runtime e l'operatore freccia (->) per dereferenziare una variabile hat.

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

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

Quando esegui la conversione nel codice C++/WinRT equivalente, puoi ottenere molto rimuovendo gli hat e cambiando l'operatore freccia (->) nell'operatore punto (.). I tipi proiettati C++/WinRT sono valori e non puntatori.

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

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

Il costruttore predefinito per un riferimento hat C++/CX lo inizializza su Null. Ecco un esempio di codice C++/CX in cui creiamo una variabile/campo del tipo corretto, ma che non è inizializzato. In altre parole, inizialmente non si fa riferimento a un TextBlock e l'intenzione è assegnare un riferimento in seguito.

TextBlock^ textBlock;

class MyClass
{
    TextBlock^ textBlock;
};

Per l'equivalente in C++/WinRT, vedi Inizializzazione ritardata.

Proprietà

Le estensioni del linguaggio C++/CX includono il concetto di proprietà. Durante la scrittura del codice sorgente C++/CX, puoi accedere a una proprietà come se fosse un campo. Il linguaggio C++ standard non include il concetto di proprietà, pertanto in C++/WinRT, chiami le funzioni get e set.

Negli esempi che seguono, XboxUserId, UserState, PresenceDeviceRecords e Size sono tutte proprietà.

Recupero di un valore da una proprietà

Ecco come ottenere un valore di proprietà in C++/CX.

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

Il codice sorgente C++/WinRT equivalente chiama una funzione con lo stesso nome della proprietà, ma senza parametri.

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

Nota che la funzione PresenceDeviceRecords restituisce un oggetto Windows Runtime che a sua volta include una funzione Size. Dal momento che anche l'oggetto restituito è un tipo proiettato C++/WinRT, dereferenziamo mediante l'operatore punto per chiamare Size .

Impostazione di una proprietà su un nuovo valore

L'impostazione di una proprietà su un nuovo valore segue un motivo simile. Innanzitutto, esegui l'operazione in C++/CX.

record->UserState = newValue;

Per eseguire l'operazione equivalente in C++/WinRT, devi chiamare una funzione con lo stesso nome della proprietà e passare un argomento.

record.UserState(newValue);

Creazione di un'istanza di una classe

Utilizzi un oggetto C++/CX tramite un handle, comunemente noto come riferimento hat (^). Crei un nuovo oggetto tramite la parola chiave ref new, che a sua volta chiama RoActivateInstance per attivare una nuova istanza della classe di runtime.

using namespace Windows::Storage::Streams;

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

Un oggetto C++/WinRT è un valore e puoi quindi puoi allocarlo nello stack o come un campo di un oggetto. Non utilizzare mairef new (né new) per allocare un oggetto C++/WinRT. RoActivateInstance viene ancora chiamato in background.

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

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

Se una risorsa è costosa da inizializzare, è pratica comune ritardarne l'inizializzazione fino a quando non è effettivamente necessaria. Come già indicato, il costruttore predefinito per un riferimento hat C++/CX lo inizializza su 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;
};

Lo stesso codice convertito in C++/WinRT. Osserva l'uso del costruttore std::nullptr_t. Per altre informazioni su questo costruttore, vedi Inizializzazione ritardata.

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

Effetti del costruttore predefinito sulle raccolte

I tipi di raccolta C++ usano il costruttore predefinito e questo può comportare la costruzione non intenzionale di oggetti.

Scenario C++/CX C++/WinRT (errato) C++/WinRT (corretto)
Variabile locale, inizialmente vuota TextBox^ textBox; TextBox textBox; // Creates a TextBox! TextBox textBox{ nullptr };
Variabile membro, inizialmente vuota class C {
  TextBox^ textBox;
};
class C {
  TextBox textBox; // Creates a TextBox!
};
class C {
  TextBox textbox{ nullptr };
};
Variabile globale, inizialmente vuota TextBox^ g_textBox; TextBox g_textBox; // Creates a TextBox! TextBox g_textBox{ nullptr };
Vettore dei riferimenti vuoti std::vector<TextBox^> boxes(10); // Creates 10 TextBox objects!
std::vector<TextBox> boxes(10);
std::vector<TextBox> boxes(10, nullptr);
Impostazione di un valore in una mappa 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);
Matrice di riferimenti vuoti TextBox^ boxes[2]; // Creates 2 TextBox objects!
TextBox boxes[2];
TextBox boxes[2] = { nullptr, nullptr };
Associazione std::pair<TextBox^, String^> p; // Creates a TextBox!
std::pair<TextBox, String> p;
std::pair<TextBox, String> p{ nullptr, nullptr };

Altre informazioni sulle raccolte di riferimenti vuoti

Ogni volta che hai un tipo Platform::Array^ (vedi Eseguire il porting di Platform::Array) in C++/CX, puoi eseguirne il porting a std::vector in C++/WinRT (in realtà, a ogni contenitore contiguo) anziché mantenerlo come matrice. La scelta di std::vector presenta alcuni vantaggi.

Ad esempio, sebbene esista una sintassi abbreviata per la creazione di un vettore di dimensioni fisse di riferimenti vuoti (vedi la tabella precedente), non esiste una tale abbreviazione per la creazione di una matrice di riferimenti vuoti. Devi ripetere nullptr per ogni elemento di una matrice. Se gli elementi sono troppo pochi, verranno creati elementi in più per impostazione predefinita.

Per un vettore, puoi inserire riferimenti vuoti in fase di inizializzazione, come nella tabella precedente, oppure puoi inserire tali riferimenti dopo l'inizializzazione con codice come quello seguente.

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

Altre informazioni sull'esempio std::map

L'operatore di indice inferiore [] per std::map si comporta nel modo seguente.

  • Se la chiave viene trovata nella mappa, viene restituito un riferimento al valore esistente, che puoi sovrascrivere.
  • Se la chiave non viene trovata nella mappa, viene creata una nuova voce nella mappa costituita dalla chiave (spostata, se mobile) e da un valore creato per impostazione predefinita e viene restituito un riferimento al valore, che puoi quindi sovrascrivere.

In altre parole, l'operatore [] crea sempre una voce nella mappa. Questo avviene in modo diverso rispetto a C#, Java e JavaScript.

Conversione da una classe di runtime di base a una derivata

È comune avere un riferimento alla base che sai che si riferisce a un oggetto di tipo derivato. In C++/CX utilizzi dynamic_cast per trasmettere il riferimento alla base in un riferimento al derivato. dynamic_cast è in realtà solo una chiamata nascosta a QueryInterface. Ecco un esempio tipico: gestisci un evento modificato della proprietà di dipendenza e vuoi trasmettere da DependencyObject al tipo effettivo a cui appartiene la proprietà di dipendenza.

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

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

Il codice C++/WinRT equivalente sostituisce dynamic_cast con una chiamata alla funzione IUnknown::try_as, che incapsula QueryInterface. Hai anche la possibilità di chiamare IUnknown::as, invece, che genera un'eccezione se non viene restituita l'interfaccia richiesta (l'interfaccia predefinita del tipo richiesto). Ecco un esempio di codice 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 ...
    }
}

Classi derivate

Per essere derivata da una classe di runtime, la classe di base deve essere componibile. In C++/CX non è necessario eseguire operazioni particolari per rendere componibili le classi, diversamente da C++/WinRT. Per indicare che una classe può essere usata come classe di base devi usare la parola chiave unsealed.

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

Nella classe dell'intestazione di implementazione devi includere il file di intestazione della classe di base prima dell'intestazione generata automaticamente per la classe derivata. In caso contrario, verranno restituiti errori come "Tipo non valido come espressione".

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

Gestione degli eventi con un delegato

Ecco un esempio tipico di gestione di un evento in C++/CX, usando una funzione lambda come un delegato in questo caso.

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

Questo è l'equivalente in 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.
});

Invece di una funzione lambda, puoi scegliere di implementare il delegato come una funzione libera o come un puntatore alla funzione membro. Per altre info, vedi Gestire eventi mediante i delegati in C++/WinRT.

Per la conversione da un codebase C++/CX in cui eventi e delegati vengono usati internamente (non tra file binari), winrt::delegate ti consentirà di replicare tale motivo in C++/WinRT. Vedi anche Delegati con parametri, segnali semplici e callback all'interno di un progetto.

Revoca di un delegato

In C++/CX utilizzi l'operatore -= per revocare la registrazione di un evento precedente.

myButton->Click -= token;

Questo è l'equivalente in C++/WinRT.

myButton().Click(token);

Per altre info e opzioni, vedi Revocare un delegato registrato.

Boxing e unboxing

C++/CX esegue automaticamente la conversione boxing di valori scalari in oggetti. C++/WinRT richiede che la funzione winrt::box_value venga chiamata esplicitamente. Per entrambi i linguaggi è necessario eseguire l'unboxing esplicito. Vedi Boxing e unboxing con C++/WinRT.

Nelle tabelle seguenti useremo queste definizioni.

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");
Unboxing i = (int)o;
s = (String^)o;
i = unbox_value<int>(o);
s = unbox_value<winrt::hstring>(o);

C++/CX e C# generano eccezioni se provi a eseguire l'unboxing di un puntatore Null in un tipo di valore. C++/WinRT considera questo caso come un errore di programmazione e si arresta in modo anomalo. In C++/WinRT usa la funzione winrt::unbox_value_or se vuoi gestire il caso in cui l'oggetto non è del tipo previsto.

Scenario C++/CX C++/WinRT
Unboxing di un numero intero noto i = (int)o; i = unbox_value<int>(o);
Se o è Null Platform::NullReferenceException Arresto anomalo
Se o non è un valore int sottoposto a boxing Platform::InvalidCastException Arresto anomalo
Unboxing di int, fallback se Null e arresto anomalo negli altri casi i = o ? (int)o : fallback; i = o ? unbox_value<int>(o) : fallback;
Unboxing di int, se possibile, e fallback per gli altri casi auto box = dynamic_cast<IBox<int>^>(o);
i = box ? box->Value : fallback;
i = unbox_value_or<int>(o, fallback);

Boxing e unboxing di una stringa

Una stringa è per certi versi un tipo di valore e per altri versi un tipo di riferimento. C++/CX e C++/WinRT trattano le stringhe in modo diverso.

Il tipo ABI HSTRING è un puntatore a una stringa con conteggio dei riferimenti. Non deriva tuttavia da IInspectable e pertanto non è tecnicamente un oggetto. Inoltre, un'istanza Null di HSTRING rappresenta la stringa vuota. La conversione boxing di elementi non derivati da IInspectable viene eseguita tramite il wrapping all'interno di un'interfaccia IReference<T> e Windows Runtime offre un'implementazione standard sotto forma di oggetto PropertyValue. I tipi personalizzati vengono segnalati come PropertyType::OtherType.

C++/CX rappresenta una stringa di Windows Runtime come tipo di riferimento, mentre C++/WinRT proietta una stringa come tipo di valore. Ciò significa che una stringa Null sottoposta a boxing può avere rappresentazioni diverse a seconda del modo in cui viene raggiunta.

C++/CX consente inoltre di dereferenziare un'istanza Null di String^ e in tal caso si comporta come la stringa "".

Comportamento C++/CX C++/WinRT
Dichiarazioni Object^ o;
String^ s;
IInspectable o;
hstring s;
Categoria del tipo di stringa Tipo di riferimento Tipo di valore
HSTRING Null proiettata come (String^)nullptr hstring{}
Null e "" sono identici?
Validità di Null s = nullptr;
s->Length == 0 (valido)
s = hstring{};
s.size() == 0 (valido)
Se si assegna una stringa null all'oggetto o = (String^)nullptr;
o == nullptr
o = box_value(hstring{});
o != nullptr
Se si assegna "" all'oggetto o = "";
o == nullptr
o = box_value(hstring{L""});
o != nullptr

Boxing e unboxing di base.

Operation C++/CX C++/WinRT
Boxing di una stringa o = s;
Una stringa vuota diventa nullptr.
o = box_value(s);
Una stringa vuota diventa un oggetto non null.
Unboxing di una stringa nota s = (String^)o;
Un oggetto null diventa una stringa vuota.
InvalidCastException se non è una stringa.
s = unbox_value<hstring>(o);
Arresto anomalo dell'oggetto null.
Arresto anomalo se non è una stringa.
Unboxing di una possibile stringa s = dynamic_cast<String^>(o);
L'oggetto null o non stringa diventa una stringa vuota.
s = unbox_value_or<hstring>(o, fallback);
Il valore null o non stringa diventa fallback.
La stringa vuota viene mantenuta.

Concorrenza e operazioni asincrone

La libreria PPL (Parallel Patterns Library), ad esempio concurrency::task, è stata aggiornata per supportare riferimenti hat di C++/CX.

Per C++/WinRT, devi usare in alternativa le coroutine e co_await. Per altre informazioni ed esempi di codice, vedi Concorrenza e operazioni asincrone con C++/WinRT.

Utilizzo di oggetti dal markup XAML

In un progetto C++/CX puoi utilizzare membri privati ed elementi denominati dal markup XAML. In C++/WinRT, tuttavia, tutte le entità utilizzate con l'estensione di markup {x:Bind} XAML devono essere esposte pubblicamente in IDL.

Inoltre, il binding a un valore booleano viene visualizzato come true o false in C++/CX, ma appare come Windows.Foundation.IReference`1<Boolean> in C++/WinRT.

Per altre informazioni ed esempi di codice, vedi Utilizzo di oggetti dal markup XAML.

Mappatura dei tipi C++/CX Platform in tipi C++/WinRT

C++/CX fornisce diversi tipi di dati nello spazio dei nomi Platform . Questi tipi non sono C++ standard, pertanto puoi utilizzarli solo quando abiliti le estensioni del linguaggio Windows Runtime (proprietà del progetto Visual Studio C/C++>Generale>Utilizza estensioni di Windows Runtime>Sì (/ZW)). La tabella seguente facilita la conversione dai tipi Platform nei rispettivi equivalenti in C++/WinRT. Dopo aver eseguito questa operazione, dal momento che C++/WinRT è C++ standard, puoi disattivare l'opzione /ZW.

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

Convertire Platform::Agile^ in winrt::agile_ref

Il tipo Platform::Agile^ in C++/CX rappresenta una classe Windows Runtime accessibile da qualsiasi thread. L'equivalente C++/WinRT è winrt::agile_ref.

In C++/CX.

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

In C++/WinRT.

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

Convertire Platform::Array^

Nei casi in cui C++/CX richiede l'uso di una matrice, C++/WinRT ti consente di usare qualsiasi contenitore contiguo. Vedi Effetti del costruttore predefinito sulle raccolte per comprendere perché std::vector è la scelta ottimale.

Pertanto, ogni volta che hai un tipo Platform::Array^ in C++/CX, le opzioni di porting includono l'uso di un elenco di inizializzatori, std::array, o std::vector. Per ulteriori informazioni ed esempi di codice, vedi Elenchi di inizializzatori standard e Vettori e matrici standard.

Convertire Platform::Exception^ in winrt::hresult_error

Il tipo Platform::Exception^ viene generato in C++/CX quando un'API di Windows Runtime restituisce un valore non S_OK HRESULT. L'equivalente C++/WinRT è winrt::hresult_error.

Per la conversione in C++/WinRT, modifica tutto il codice che utilizza Platform::Exception^ in modo da utilizzare winrt::hresult_error.

In C++/CX.

catch (Platform::Exception^ ex)

In C++/WinRT.

catch (winrt::hresult_error const& ex)

C++/WinRT fornisce queste classi di eccezione.

Tipo di eccezione Classe base HRESULT
winrt::hresult_error chiamare 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

Nota che ogni classe (tramite la classe base hresult_error) fornisce una funzione to_abi che restituisce l'HRESULT dell'errore, e una funzione message che restituisce la rappresentazione di stringa di tale HRESULT.

Ecco un esempio di generazione di un'eccezione in C++/CX.

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

E l'equivalente in C++/WinRT.

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

Convertire Platform::Object\t^ in winrt::Windows::Foundation::IInspectable

Come tutti i tipi C++/WinRT, winrt::Windows::Foundation::IInspectable è un tipo di valore. Ecco come inizializzare una variabile di quel tipo su Null.

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

Convertire Platform::String^ in winrt::hstring

Platform::String^ è equivalente al tipo Windows Runtime HSTRING ABI. Per C++/WinRT, l'equivalente è winrt::hstring. Tuttavia, con C++/WinRT, puoi chiamare le API di Windows Runtime tramite tipi di stringa estesa della libreria standard C++ come std:: wstring e/o valori letterali stringa estesa. Per ulteriori dettagli ed esempi di codice, vedi Gestione delle stringhe in C++/WinRT.

Con C++/CX, puoi accedere alla proprietà Platform::String::Data per recuperare la stringa come una matrice const wchar_t* stile C (ad esempio, per passarla su std::wcout).

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

Per eseguire la stessa operazione con C++/WinRT, puoi utilizzare la funzione hstring::c_str per ottenere una versione di stringa in stile C che termina con Null, proprio come per qualsiasi std:: wstring .

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

Quando si tratta di implementare API che accettano o restituiscono stringhe, in genere puoi modificare qualsiasi codice C++/CX che utilizza Platform::String^ per utilizzare invece winrt::hstring.

Ecco un esempio di API C++/CX che accetta una stringa.

void LogWrapLine(Platform::String^ str);

Per C++/WinRT, puoi dichiarare l'API in MIDL 3.0 come segue.

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

La toolchain C++/WinRT genera quindi codice sorgente simile al seguente.

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

ToString()

I tipi C++/CX forniscono il metodo Object::ToString.

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

C++/ WinRT non fornisce direttamente questa funzionalità, ma puoi ricorrere ad alternative.

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

C++/WinRT supporta inoltre winrt::to_hstring per un numero limitato di tipi. Dovrai aggiungere gli overload per tutti i tipi aggiuntivi che vuoi convertire in stringhe.

Lingua Conversione in stringa di int Conversione in stringa di enum
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);

In caso di conversione in stringa di un enum, dovrai fornire l'implementazione di 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));
        }
    }
}

Queste conversioni in stringa vengono spesso utilizzate in modo implicito dal data binding.

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

I binding eseguiranno la funzione winrt::to_hstring sulla proprietà associata. Nel caso del secondo esempio (StatusEnum), devi fornire un overload personalizzato di winrt::to_hstring, altrimenti verrà restituito un errore del compilatore.

Compilazione di stringhe

C++/CX e C++/WinRT rimandano allo standard std::wstringstream per la compilazione di stringhe.

Operation C++/CX C++/WinRT
Aggiunta di una stringa con conservazione di Null stream.print(s->Data(), s->Length); stream << std::wstring_view{ s };
Aggiunta di una stringa con arresto al primo valore Null stream << s->Data(); stream << s.c_str();
Estrazione del risultato ws = stream.str(); ws = stream.str();

Altri esempi

Negli esempi seguenti ws è una variabile di tipo std::wstring. Inoltre, C++/CX è in grado di creare un elemento Platform::String da una stringa a 8 bit, mentre C++/WinRT non può fare altrettanto.

Operation C++/CX C++/WinRT
Creazione di una stringa da un valore letterale String^ s = "hello";
String^ s = L"hello";
// winrt::hstring s{ "hello" }; // Doesn't compile
winrt::hstring s{ L"hello" };
Conversione da std::wstring con conservazione di 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
Conversione da std:: wstring con arresto al primo valore Null 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
Conversione in std::wstring con conservazione di Null std::wstring ws{ s->Data(), s->Length };
ws = std::wstring(s>Data(), s->Length);
std::wstring ws{ s };
ws = s;
Conversione in std:: wstring con arresto al primo valore Null std::wstring ws{ s->Data() };
ws = s->Data();
std::wstring ws{ s.c_str() };
ws = s.c_str();
Passaggio di un valore letterale a un metodo Method("hello");
Method(L"hello");
// Method("hello"); // Doesn't compile
Method(L"hello");
Passaggio di std:: wstring a un metodo 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 importanti