Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Questo argomento è il primo di una serie che descrive come convertire il codice sorgente nel progetto C++/CX nell'equivalente in C++/WinRT.
Se il progetto usa anche i tipi di Windows Runtime C++ Template Library (WRL), vedi Passare a C++/WinRT da WRL.
Strategie per la conversione
Vale la pena sapere che la conversione da C++/CX a C++/WinRT è in genere semplice, con l'unica eccezione del passaggio da attività PPL (Parallel Patterns Library) alle coroutine. I modelli sono diversi. Non esiste una corrispondenza uno a uno naturale tra le attività PPL e le coroutine, e non c’è un modo semplice per portare meccanicamente il codice che funzioni in tutti i casi. Per informazioni su questo aspetto specifico della conversione e sulle opzioni per l'interoperabilità tra i due modelli, vedi Asincronia e interoperabilità tra C++/WinRT e C++/CX.
I team di sviluppo segnalano regolarmente che, una volta superato l'ostacolo della conversione del codice asincrono, il resto del lavoro di conversione è in gran parte meccanico.
Conversione in un unico passaggio
Se si è in grado di convertire l'intero progetto in un unico passaggio, sarà necessario solo questo argomento per le informazioni necessarie (e non saranno necessari gli argomenti di interoperabilità che seguono questo argomento). È consigliabile iniziare creando un nuovo progetto in Visual Studio usando uno dei modelli di progetto C++/WinRT (vedere Visual Studio supporto per C++/WinRT). Quindi sposta i file del codice sorgente nel nuovo progetto e, nel farlo, porta tutto il codice sorgente C++/CX a C++/WinRT.
In alternativa, se preferisci eseguire il lavoro di conversione nel progetto C++/CX esistente, dovrai aggiungere il supporto C++/WinRT. I passaggi da seguire per eseguire questa operazione sono descritti in Acquisizione di un progetto C++/CX e aggiunta del supporto C++/WinRT. Al termine della conversione, avrai trasformato ciò che era un progetto C++/CX puro in un progetto C++/WinRT puro.
Note
Se si dispone di un progetto componente Windows Runtime, la conversione in un unico passaggio è l'unica opzione. Un progetto componente Windows Runtime scritto in C++ deve contenere tutto il codice sorgente C++/CX o tutto il codice sorgente C++/WinRT. Non possono coesistere in questo tipo di progetto.
Migrazione graduale di un progetto
Ad eccezione dei progetti di componenti Windows Runtime, come indicato nella sezione precedente, se le dimensioni o la complessità della codebase rendono necessario convertire gradualmente il progetto, sarà necessario un processo di conversione in cui per un momento il codice C++/CX e C++/WinRT esiste affiancato nello stesso progetto. Oltre a leggere questo argomento, consulta anche Interoperabilità tra C++/WinRT e C++/CX e Asincronia e l'interoperabilità tra C++/WinRT e C++/CX. Questi argomenti forniscono informazioni ed esempi di codice che illustrano come interagire tra le due proiezioni del linguaggio.
Per preparare un progetto per un processo di conversione graduale, un'opzione consiste nell'aggiungere il supporto C++/WinRT al progetto C++/CX. I passaggi da seguire per eseguire questa operazione sono descritti in Acquisizione di un progetto C++/CX e aggiunta del supporto C++/WinRT. È quindi possibile eseguire gradualmente la migrazione a partire da quel punto.
Un'altra opzione consiste nel creare un nuovo progetto in Visual Studio usando uno dei modelli di progetto C++/WinRT (vedere Visual Studio supporto per C++/WinRT). Aggiungere quindi il supporto C++/CX a tale progetto. I passaggi da seguire per eseguire questa operazione sono descritti in Acquisizione di un progetto C++/WinRT e aggiunta del supporto C++/CX. È quindi possibile iniziare a spostare il codice sorgente al suo interno e, nel frattempo, portare parte del codice sorgente C++/CX in C++/WinRT.
In entrambi i casi, potrai garantire l'interoperabilità, in entrambi i sensi, tra il tuo codice C++/WinRT e qualsiasi codice C++/CX che non hai ancora migrato.
Note
Sia C++/CX che Windows SDK dichiarano i tipi nello spazio dei nomi radice Windows. Un tipo Windows proiettato in C++/WinRT ha lo stesso nome completo del tipo Windows, ma viene inserito nello spazio dei nomi winrt C++. Questi spazi dei nomi distinti ti consentono di convertire 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. Puoi comunque combinare C++/CX e C++/WinRT all'esterno dei tipi di pagina XAML all'interno dello stesso progetto (nei modelli e nei modelli di visualizzazione e altrove).
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, puoi aggiungere nuove pagine XAML con Project>Aggiungi nuovo elemento...>Visual C++>Pagina vuota (C++/WinRT).
In alternativa, puoi usare un componente Windows Runtime (WRC) per separare il codice dal progetto XAML in C++/CX durante il porting.
- È possibile creare un nuovo progetto C++/CX WRC, spostare tutto il codice C++/CX possibile in tale progetto e quindi modificare il progetto XAML in C++/WinRT.
- In alternativa, è possibile creare un nuovo progetto WRC C++/WinRT, lasciare il progetto XAML C++/CX e iniziare a convertire C++/CX in C++/WinRT e spostare il codice risultante dal progetto XAML e nel progetto componente.
- È anche possibile avere un progetto di componenti C++/CX insieme a un progetto di componenti C++/WinRT all'interno della stessa soluzione, fare riferimento a entrambi dal progetto dell'applicazione e quindi eseguire gradualmente il porting dall'uno all'altro. Anche in questo caso, vedi Interoperabilità tra C++/WinRT e C++/CX per altri dettagli sull'uso delle due proiezioni del linguaggio nello stesso progetto.
Passaggi preliminari per la conversione di un progetto C++/CX in C++/WinRT
Indipendentemente dalla strategia di conversione (conversione graduale o conversione graduale), il primo passaggio consiste nel preparare il progetto per la conversione. Ecco un riepilogo di quanto descritto in Strategie per la conversione in termini di tipo di progetto da iniziare e come configurarlo.
- Conversione in un unico passaggio. Creare un nuovo progetto in Visual Studio usando uno dei modelli di progetto C++/WinRT. Spostare i file dal progetto C++/CX in tale nuovo progetto e convertire il codice sorgente C++/CX.
- Conversione graduale di un progetto non XAML. È possibile scegliere di aggiungere il supporto per C++/WinRT al progetto C++/CX (vedere Prendere un progetto C++/CX e aggiungere il supporto per C++/WinRT) ed eseguire la migrazione gradualmente. In alternativa, è possibile scegliere di creare un nuovo progetto C++/WinRT e aggiungere il supporto C++/CX a tale progetto (vedere Acquisizione di un progetto C++/WinRT e aggiunta del supporto C++/CX), spostamento graduale dei file e conversione.
- Conversione graduale di un progetto XAML. Creare un nuovo progetto C++/WinRT, trasferire i file ed eseguire gradualmente la migrazione. In qualsiasi momento i tipi di pagina XAML devono essere tutti C++/WinRT o tutti C++/CX.
Il resto di questo argomento si applica indipendentemente dalla strategia di conversione scelta. Contiene un catalogo di dettagli tecnici relativi alla conversione del codice sorgente da C++/CX a C++/WinRT. Se si effettua una migrazione graduale, probabilmente si vorrà anche consultare Interoperabilità tra C++/WinRT e C++/CX e Asincronia e interoperabilità tra C++/WinRT e C++/CX.
Convenzione di denominazione dei file
File di markup XAML
| Origine del 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 (vedere di seguito) |
| File XAML generati | MyPage.xaml.g.h MyPage.xaml.g.hpp |
MyPage.xaml.g.h MyPage.xaml.g.hpp MyPage.g.h |
Si noti che C++/WinRT rimuove .xaml dai nomi file *.h e *.cpp.
C++/WinRT aggiunge un file di sviluppo aggiuntivo, il file Midl (con estensione idl). C++/CX genera automaticamente questo file internamente, aggiungendolo a ogni membro pubblico e protetto. In C++/WinRT aggiungi e crea il file manualmente. Per altri dettagli, esempi di codice e una procedura dettagliata per la creazione di IDL, vedi Controlli XAML; binding a una proprietà C++/WinRT.
Vedere anche Estrarre le classi di runtime nei file MIDL (.idl)
Classi di runtime
C++/CX non impone restrizioni sui nomi dei file di intestazione; è comune inserire più definizioni di classe di runtime in un singolo file di intestazione, soprattutto per le classi di piccole dimensioni. Tuttavia, C++/WinRT richiede che ogni classe di runtime abbia un proprio file di intestazione denominato dopo il nome della classe.
| C++/CX | C++/WinRT |
|---|---|
Common.href class A { ... }ref class B { ... } |
Common.idlruntimeclass A { ... }runtimeclass B { ... } |
A.hnamespace implements {struct A { ... };} |
|
B.hnamespace implements {struct B { ... };} |
Meno comune (ma comunque legale) in C++/CX consiste nell'usare file di intestazione con nome diverso per i controlli personalizzati XAML. Sarà necessario rinominare il file di intestazione in modo che corrisponda al nome della classe.
| C++/CX | C++/WinRT |
|---|---|
A.xaml<Page x:Class="LongNameForA" ...> |
A.xaml<Page x:Class="LongNameForA" ...> |
A.hpartial ref class LongNameForA { ... } |
LongNameForA.hnamespace implements {struct LongNameForA { ... };} |
Requisiti dei file header
C++/CX non richiede l'inclusione di file di intestazione speciali, perché genera internamente i file di intestazione dai .winmd file. In C++/CX è comune usare le direttive using per i namespace a cui si fa riferimento per nome.
using namespace Windows::Media::Playback;
String^ NameOfFirstVideoTrack(MediaPlaybackItem^ item)
{
return item->VideoTracks->GetAt(0)->Name;
}
La using namespace Windows::Media::Playback direttiva consente di scrivere MediaPlaybackItem senza un prefisso dello spazio dei nomi. Abbiamo anche modificato il Windows.Media.Core namespace, perché item->VideoTracks->GetAt(0) restituisce un Windows.Media.Core.VideoTrack. Ma non abbiamo dovuto digitare il nome VideoTrack da nessuna parte, quindi non abbiamo bisogno di una using Windows.Media.Core direttiva.
Ma C++/WinRT richiede di includere un file di intestazione corrispondente a ogni spazio dei nomi utilizzato, anche se non lo chiami.
#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 è necessario includere winrt/Windows.Foundation.Collections.h perché l'evento non è stato usato.
C++/WinRT richiede anche di includere i file di intestazione per gli spazi dei nomi utilizzati dal markup XAML.
<!-- MainPage.xaml -->
<Rectangle Height="400"/>
L'uso della classe Rectangle significa che è necessario aggiungere questa inclusione.
// MainPage.h
#include <winrt/Microsoft.UI.Xaml.Shapes.h>
Se si dimentica un file di intestazione, tutto verrà compilato correttamente, ma si otterranno errori del linker perché le consume_ classi sono mancanti.
Passaggio di parametri
Quando si scrive codice sorgente C++/CX, si passano i tipi C++/CX come parametri di funzione come riferimenti hat (^).
void LogPresenceRecord(PresenceRecord^ record);
In C++/WinRT, per le funzioni sincrone, si dovrebbero usare per impostazione predefinita i parametri const&. Ciò eviterà copie e costi aggiuntivi dovuti all’interblocco. Tuttavia, le coroutine devono usare il passaggio per valore, in modo da acquisire per valore ed evitare problemi di ciclo di vita (per maggiori dettagli, vedere 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 a un'interfaccia dell'oggetto Windows Runtime sottostante. Quando si copia un oggetto C++/WinRT, il compilatore copia il puntatore dell'interfaccia incapsulato, incrementando il conteggio dei riferimenti. La distruzione finale della copia comporta la decrementazione del conteggio dei riferimenti. Pertanto, comporta solo il sovraccarico di una copia quando necessario.
Riferimenti a variabili e campi
Quando si scrive codice sorgente C++/CX, si usano variabili hat (^) per fare riferimento a oggetti 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 si esegue la conversione al codice C++/WinRT equivalente, è possibile ottenere un lungo percorso rimuovendo i cappelli e modificando l'operatore freccia (->) con l'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 viene creata una variabile/campo del tipo corretto, ma non inizializzata. In altre parole, inizialmente non fa riferimento a un TextBlock; si intende assegnare un riferimento in un secondo momento.
TextBlock^ textBlock;
class MyClass
{
TextBlock^ textBlock;
};
Per l'equivalente in C++/WinRT, vedi Inizializzazione ritardata.
Properties
Le estensioni del linguaggio C++/CX includono il concetto di proprietà. Quando si scrive codice sorgente C++/CX, è possibile accedere a una proprietà come se fosse un campo. C++ Standard non ha il concetto di una proprietà, quindi, in C++/WinRT, chiami le funzioni get e set.
Negli esempi seguenti, XboxUserId, UserState, PresenceDeviceRecords e Size sono tutte proprietà.
Recupero di un valore da una proprietà
Ecco come ottenere un valore della 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();
}
Si noti che la funzione PresenceDeviceRecords restituisce un oggetto Windows Runtime che ha una funzione Size. Poiché l'oggetto restituito è anche un tipo proiettato C++/WinRT, lo dereferenziamo usando 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 modello simile. In primo luogo, in C++/CX.
record->UserState = newValue;
Per fare l'equivalente in C++/WinRT, si chiama una funzione con lo stesso nome della proprietà e le si passa un argomento.
record.UserState(newValue);
Creazione di un'istanza di una classe
Si lavora con un oggetto C++/CX tramite un handle, comunemente noto come riferimento hat (^). Si crea un nuovo oggetto tramite la ref new parola chiave , 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; in modo da poterlo allocare nello stack o come campo di un oggetto. Non usare ref new (né new) per allocare un oggetto C++/WinRT. Dietro le quinte, RoActivateInstance è ancora in fase di chiamata.
using namespace winrt::Windows::Storage::Streams;
struct Sample
{
private:
Buffer m_gamerPicBuffer{ MAX_IMAGE_SIZE };
};
Se una risorsa è costosa da inizializzare, è comune ritardare l'inizializzazione fino a quando non è effettivamente necessaria. Come già accennato, 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. Si noti l'uso del costruttore std::nullptr_t . Per altre informazioni sul 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 };
};
Impatto del costruttore predefinito sulle raccolte
I tipi di raccolta C++ usano il costruttore predefinito, che può comportare la costruzione di oggetti imprevisti.
| 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 di riferimenti vuoti | std::vector<TextBox^> boxes(10); |
// Creates 10 TextBox objects!std::vector<TextBox> boxes(10); |
std::vector<TextBox> boxes(10, nullptr); |
| Impostare 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 }; |
| Coppia | 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 si dispone di Platform::Array^ (vedere Port Platform::Array^) in C++/CX, è possibile scegliere di convertirlo in uno std::vector in C++/WinRT (in effetti, qualsiasi contenitore contiguo) anziché lasciarlo come matrice. La scelta di std::vector offre vantaggi.
Ad esempio, mentre esiste una sintassi abbreviata per la creazione di un vettore a dimensione fissa di riferimenti vuoti (vedere la tabella precedente), non esiste una tale sintassi abbreviata per la creazione di una matrice di riferimenti vuoti. È necessario ripetere nullptr per ogni elemento in una matrice. Se sono troppo pochi, gli extra saranno costruiti per impostazione predefinita.
Per un vettore, è possibile riempirlo con riferimenti vuoti all'inizializzazione (come nella tabella precedente) oppure è possibile riempirlo con riferimenti vuoti dopo l'inizializzazione con codice come questo.
std::vector<TextBox> boxes(10); // 10 default-constructed TextBoxes.
boxes.resize(10, nullptr); // 10 empty references.
Altre informazioni sull'esempio std::map
L'operatore [] pedice per std::map si comporta in questo modo.
- Se la chiave viene trovata nella mappa, restituire un riferimento al valore esistente (che è possibile sovrascrivere).
- Se la chiave non viene trovata nella mappa, creare una nuova voce nella mappa costituita dalla chiave (spostata, se mobile) e da un valore costruito per impostazione predefinita e restituire un riferimento al valore (che è quindi possibile sovrascrivere).
In altre parole, l'operatore [] crea sempre una voce nella mappa. Questo è diverso da C#, Java e JavaScript.
Conversione da una classe di runtime di base a una derivata
È comune avere un riferimento a base noto che fa riferimento a un oggetto di un tipo derivato. In C++/CX si utilizza dynamic_cast per eseguire il cast del riferimento alla classe base in un riferimento alla classe derivata.
dynamic_cast è in realtà solo una chiamata nascosta a QueryInterface. Ecco un esempio tipico: si gestisce un evento di modifica della proprietà di dipendenza e si vuole eseguire il cast da DependencyObject al tipo effettivo proprietario della proprietà di dipendenza.
void BgLabelControl::OnLabelChanged(Microsoft::UI::Xaml::DependencyObject^ d, Microsoft::UI::Xaml::DependencyPropertyChangedEventArgs^ e)
{
BgLabelControl^ theControl{ dynamic_cast<BgLabelControl^>(d) };
if (theControl != nullptr)
{
// succeeded ...
}
}
Il codice C++/WinRT equivalente sostituisce con dynamic_cast una chiamata alla funzione IUnknown::try_as , che incapsula QueryInterface. È anche possibile chiamare, in alternativa, IUnknown::as, che genera un'eccezione se la query dell'interfaccia richiesta (l'interfaccia predefinita del tipo richiesto) non restituisce alcun risultato. Ecco un esempio di codice 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 ...
}
}
Classi derivate
Per derivare da una classe di runtime, la classe base deve essere componibile. C++/CX non richiede di adottare misure particolari per rendere componibili le proprie classi, ma C++/WinRT sì. Usare la parola chiave unsealed per indicare che si vuole che la classe sia utilizzabile come classe di base.
unsealed runtimeclass BasePage : Microsoft.UI.Xaml.Controls.Page
{
...
}
runtimeclass DerivedPage : BasePage
{
...
}
Nella classe di intestazione di implementazione è necessario includere il file di intestazione della classe di base prima di includere l'intestazione generata automaticamente per la classe derivata. In caso contrario, si otterranno errori come "Uso non valido di questo tipo 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 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.
});
Anziché una funzione lambda, è possibile scegliere di implementare il delegato tramite una funzione libera o come puntatore a una funzione membro. Per altre info, vedi Gestire gli eventi usando delegati in C++/WinRT.
Se si esegue la conversione da una codebase C++/CX in cui gli eventi e i delegati vengono usati internamente (non tra file binari), winrt::d elegate consentirà di replicare tale modello in C++/WinRT. Vedere anche Delegati con parametri, segnali semplici e callback all'interno di un progetto.
Revoca di un delegato
In C++/CX si usa l'operatore -= per revocare una registrazione 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 il boxing dei valori scalari in oggetti. C++/WinRT richiede di chiamare in modo esplicito la funzione winrt::box_value . Entrambe le lingue richiedono che si esegua esplicitamente l'unboxing. Vedi Boxing e unboxing con C++/WinRT.
Nelle tabelle seguenti si useranno queste definizioni.
| C++/CX | C++/WinRT |
|---|---|
int i; |
int i; |
String^ s; |
winrt::hstring s; |
Object^ o; |
IInspectable o; |
| Operation | C++/CX | C++/WinRT |
|---|---|---|
| Pugilato | o = 1;o = "string"; |
o = box_value(1);o = box_value(L"string"); |
| Apertura della confezione | i = (int)o;s = (String^)o; |
i = unbox_value<int>(o);s = unbox_value<winrt::hstring>(o); |
C++/CX e C# generano eccezioni se si tenta di eseguire l'unboxing di un puntatore null in un tipo di valore. C++/WinRT considera questo un errore di programmazione e va in crash. In C++/WinRT usa la funzione winrt::unbox_value_or se vuoi gestire il caso in cui l'oggetto non sia del tipo che pensavi fosse.
| Scenario | C++/CX | C++/WinRT |
|---|---|---|
| Eseguire l'unboxing di un numero intero noto | i = (int)o; |
i = unbox_value<int>(o); |
| Se o è null | Platform::NullReferenceException |
Scontro |
| Se o non è un boxed int | Platform::InvalidCastException |
Scontro |
| Eseguire l'unboxing di int, usare il valore di riserva se è null; generare un arresto anomalo se è qualsiasi altra cosa | i = o ? (int)o : fallback; |
i = o ? unbox_value<int>(o) : fallback; |
| Unbox int, se possibile; usare il fallback per qualsiasi altro elemento | 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 è in alcuni modi un tipo di valore e in altri modi un tipo 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. Ma non deriva da IInspectable, quindi non è tecnicamente un oggetto. Inoltre, una HSTRING null rappresenta la stringa vuota. Il boxing di elementi non derivati da IInspectable viene eseguito eseguendo il wrapping all'interno di un oggetto IReference<T> e il Windows Runtime fornisce un'implementazione standard sotto forma di oggetto PropertyValue (i tipi personalizzati vengono segnalati come PropertyType::OtherType).
C++/CX rappresenta una stringa Windows Runtime come tipo riferimento, mentre C++/WinRT proietta una stringa come tipo valore. Ciò significa che una stringa null boxed può avere rappresentazioni diverse a seconda di come ci si è arrivati.
Inoltre, C++/CX consente di dereferenziare un valore String^null, nel qual caso si comporta come la stringa "".
| Behavior | C++/CX | C++/WinRT |
|---|---|---|
| Dichiarazioni | Object^ o;String^ s; |
IInspectable o;hstring s; |
| Categoria tipo stringa | Tipo di riferimento | Tipo di valore |
| null HSTRING come proietta | (String^)nullptr |
hstring{} |
Sono null e "" identici? |
Yes | Yes |
| 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 |
Concetti di base di boxing e unboxing.
| Operation | C++/CX | C++/WinRT |
|---|---|---|
| Incapsulare una stringa | o = s;La stringa vuota diventa nullptr. |
o = box_value(s);La stringa vuota diventa un oggetto non Null. |
| Eseguire l'unboxing di una stringa nota | s = (String^)o;L'oggetto Null diventa una stringa vuota. InvalidCastException se non è una stringa. |
s = unbox_value<hstring>(o);Arresti anomali causati da oggetti nulli. Arresto anomalo se non è una stringa. |
| Eseguire l'unboxing di una possibile stringa | s = dynamic_cast<String^>(o);L'oggetto null o un valore che non è una stringa diventa una stringa vuota. |
s = unbox_value_or<hstring>(o, fallback);Un valore nullo o non di tipo stringa diventa il valore predefinito. Stringa vuota mantenuta. |
Concorrenza e operazioni asincrone
La libreria PPL (Parallel Patterns Library) (concurrency::task, ad esempio) è stata aggiornata per supportare riferimenti hat C++/CX.
Con C++/WinRT, dovresti invece usare le coroutine e co_await. Per altre info ed esempi di codice, vedi Concorrenza e operazioni asincrone con C++/WinRT.
Utilizzo di oggetti nel markup XAML
In un progetto C++/CX puoi utilizzare membri privati e elementi denominati dal markup XAML. In C++/WinRT, tuttavia, tutte le entità utilizzate tramite l'estensione di markup XAML {x:Bind} devono essere esposte pubblicamente in IDL.
Inoltre, l'associazione a un valore booleano visualizza true o false in C++/CX, ma mostra Windows. Foundation.IReference'1<Boolean> in C++/WinRT.
Per altre info ed esempi di codice, vedi Utilizzo di oggetti dal markup.
Mappatura dei tipi Platform di C++/CX ai tipi di C++/WinRT
C++/CX fornisce diversi tipi di dati nello spazio dei nomi Platform . Questi tipi non sono standard C++, quindi è possibile usarli solo quando si abilitano le estensioni del linguaggio Windows Runtime (Visual Studio proprietà del progetto C/C++>General>Consume Windows Runtime Extension>Yes (/ZW)). La tabella seguente consente di convertire i tipi di piattaforma ai relativi equivalenti in C++/WinRT. Al termine, poiché C++/WinRT è standard C++, puoi disattivare l'opzione /ZW .
| C++/CX | C++/WinRT |
|---|---|
| Platform::Agile^ | winrt::agile_ref |
| Platform::Array^ | Vedere Port 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 (WinUI 3 usa Microsoft::UI::Xaml::Window anziché CoreWindow).
winrt::agile_ref<Microsoft::UI::Xaml::Window> m_window;
Eseguire il porting di Platform::Array^
Nei casi in cui C++/CX richiede l'uso di una matrice, C++/WinRT consente di usare qualsiasi contenitore contiguo. Vedere Come il costruttore predefinito influisce sulle raccolte per un motivo per cui std::vector è una scelta ottimale.
Pertanto, ogni volta che si dispone di platform::Array^ in C++/CX, le opzioni di conversione includono l'uso di un elenco di inizializzatori, un std::array o std::vector. Per altre info ed esempi di codice, vedi Elenchi di inizializzatori standard e matrici e vettori Standard.
Convertire Platform::Exception^ in winrt::hresult_error
Il tipo Platform::Exception^ viene generato in C++/CX quando un'API Windows Runtime restituisce un valore HRESULT non S_OK. L'equivalente C++/WinRT è winrt::hresult_error.
Per passare a C++/WinRT, modificare tutto il codice che usa Platform::Exception^ per usare 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 eccezioni.
| 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 | ERRORE_ANNULLATO |
| 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 |
Si noti che ogni classe (tramite la classe base hresult_error ) fornisce una funzione to_abi , che restituisce il valore HRESULT dell'errore e una funzione di messaggio , 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^ 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 };
Porta Platform::String^ a winrt::hstring
Platform::String^ equivale al tipo ABI HSTRING di Windows Runtime. Per C++/WinRT, l'equivalente è winrt::hstring. Ma con C++/WinRT puoi chiamare le API di Windows Runtime usando tipi stringa wide della libreria standard C++, ad esempio std::wstring, e/o stringhe letterali wide. Per altri dettagli ed esempi di codice, vedere Gestione delle stringhe in C++/WinRT.
Con C++/CX, è possibile accedere alla proprietà Platform::String::D ata per recuperare la stringa come matrice const in stile C wchar_t* (ad esempio, per passarla a std::wcout).
auto var{ titleRecord->TitleName->Data() };
Per eseguire la stessa operazione con C++/WinRT, puoi usare la funzione hstring::c_str per ottenere una versione stringa C con terminazione Null, proprio come puoi da std::wstring.
auto var{ titleRecord.TitleName().c_str() };
Quando si tratta di implementare API che accettano o restituiscono stringhe, in genere si modifica qualsiasi codice C++/CX che usa Platform::String^ per usare winrt::hstring .
Ecco un esempio di API C++/CX che accetta una stringa.
void LogWrapLine(Platform::String^ str);
Per C++/WinRT è possibile dichiarare l'API in MIDL 3.0 in questo modo.
// LogType.idl
void LogWrapLine(String str);
La toolchain C++/WinRT genererà quindi il codice sorgente per te 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 passare a alternative.
int i{ 2 };
auto s{ std::to_wstring(i) }; // s is a std::wstring with value L"2".
C++/WinRT supporta anche winrt::to_hstring per un numero limitato di tipi. È necessario aggiungere sovraccarichi per tutti gli eventuali tipi aggiuntivi che si desidera convertire in stringa.
| Language | Converti int in stringa | Convertire l'enumerazione in una stringa |
|---|---|---|
| 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); |
Nel caso in cui si voglia convertire un enum in stringa, sarà necessario 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 implicitamente dal data binding.
<TextBlock>
You have <Run Text="{Binding FlowerCount}"/> flowers.
</TextBlock>
<TextBlock>
Most recent status is <Run Text="{x:Bind LatestOperation.Status}"/>.
</TextBlock>
Questi binding eseguiranno la conversione winrt::to_hstring della proprietà associata. Nel caso del secondo esempio ( statusEnum), è necessario fornire il proprio overload di winrt::to_hstring, in caso contrario verrà visualizzato un errore del compilatore.
Creazione di stringhe
C++/CX e C++/WinRT rinviano allo standard std::wstringstream per la compilazione di stringhe.
| Operation | C++/CX | C++/WinRT |
|---|---|---|
| Aggiungi stringa, mantenendo i valori null | stream.print(s->Data(), s->Length); |
stream << std::wstring_view{ s }; |
| Aggiungi stringa, arresta al primo null | stream << s->Data(); |
stream << s.c_str(); |
| Estrarre il risultato | ws = stream.str(); |
ws = stream.str(); |
Altri esempi
Negli esempi seguenti ws è una variabile di tipo std::wstring. Inoltre, mentre C++/CX può costruire una piattaforma::String da una stringa a 8 bit, C++/WinRT non esegue questa operazione.
| Operation | C++/CX | C++/WinRT |
|---|---|---|
| Costruire una stringa dal valore letterale | String^ s = "hello";String^ s = L"hello"; |
// winrt::hstring s{ "hello" }; // Doesn't compilewinrt::hstring s{ L"hello" }; |
| Convertire da std::wstring, preservando i caratteri nulli | 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 |
| Converti da std::wstring, fermarsi al primo carattere 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 |
| Convertire in std::wstring, mantenendo i valori Null | std::wstring ws{ s->Data(), s->Length };ws = std::wstring(s>Data(), s->Length); |
std::wstring ws{ s };ws = s; |
| Convertire in std::wstring, interrompere al primo carattere null | std::wstring ws{ s->Data() };ws = s->Data(); |
std::wstring ws{ s.c_str() };ws = s.c_str(); |
| Passare un valore letterale al metodo | Method("hello");Method(L"hello"); |
// Method("hello"); // Doesn't compileMethod(L"hello"); |
| Passare std::wstring al 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
- modello di struttura winrt::delegate
- winrt::hresult_error struct
- struct winrt::hstring
- spazio dei nomi WinRT
Argomenti correlati
Note
Molti argomenti C++/WinRT stanno eseguendo la migrazione dalla documentazione UWP a questa sezione. Fino al completamento della migrazione, i collegamenti nell'elenco seguente potrebbero portare alla sezione della documentazione UWP. La proiezione del linguaggio C++/WinRT è la stessa per le app UWP e WinUI 3, in modo che il contenuto sia applicabile in entrambi i contesti. Qualsiasi modello specifico della piattaforma UWP(ad esempio il ciclo di vita dell'app o Windows.UI le API dello spazio dei nomi) viene annotato in modo esplicito in tali articoli.
- C++/CX
- Creare eventi in C++/WinRT
- Concorrenza e operazioni asincrone con C++/WinRT
- Utilizzare API con C++/WinRT
- Gestire gli eventi usando delegati in C++/WinRT
- Interoperabilità tra C++/WinRT e C++/CX
- Asincronia e interoperabilità tra C++/WinRT e C++/CX
- Informazioni di riferimento sul linguaggio di definizione dell'interfaccia Microsoft 3.0
- Passare a C++/WinRT da WRL
- Gestione delle stringhe in C++/WinRT