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 portare il codice sorgente dal progetto C++/CX al suo equivalente in C++/WinRT.
Se il progetto usa anche tipi WRL (Windows Runtime C++ Template Library), vedere 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 Parallel Patterns Library (PPL) task alle coroutine. I modelli sono diversi. Non esiste una mappatura uno-a-uno naturale delle attività del PPL alle coroutine e non esiste un modo semplice per trasporre 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, vedere Asynchrony 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 supporto di Visual Studio per C++/WinRT). Spostare quindi i file di codice sorgente in tale nuovo progetto e, mentre lo si fa, convertire tutto il codice sorgente da 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.
Annotazioni
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. Non possono coesistere in questo tipo di progetto.
Migrazione graduale di un progetto
Ad eccezione dei progetti di componente 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, vedi 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 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 convertire gradualmente da lì.
Un'altra opzione consiste nel creare un nuovo progetto in Visual Studio usando uno dei modelli di progetto C++/WinRT (vedere supporto di Visual Studio 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 in tale codice e convertire alcune del codice sorgente C++/CX in C++/WinRT come si fa.
In entrambi i casi, interopererai (in entrambi i modi) tra il tuo codice C++/WinRT e qualsiasi codice C++/CX che non hai ancora portato.
Annotazioni
Sia C++/CX che l'SDK di Windows dichiarano i tipi nello spazio dei nomi principale Windows. Un tipo di 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 di di 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, è possibile aggiungere nuove pagine XAML con Progetto>Aggiungi nuovo elemento...>Visual C++>Pagina vuota (C++/WinRT).
In alternativa, puoi usare un componente Windows Runtime (WRC) per estrarre il codice dal progetto XAML 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 come C++/CX e quindi iniziare a portare C++/CX a C++/WinRT e a spostare il codice risultante dal progetto XAML al progetto componente.
- Si potrebbe anche avere un progetto di componente C++/CX insieme a un progetto di componente C++/WinRT all'interno della stessa soluzione, fare riferimento a entrambi dal tuo progetto di applicazione, e passare gradualmente da uno all'altro. Anche in questo caso, consulta Interoperabilità tra C++/WinRT e C++/CX per ulteriori dettagli sull'utilizzo delle due proiezioni del codice nello stesso progetto.
Passaggi preliminari per la conversione di un progetto C++/CX in C++/WinRT
Indipendentemente dalla strategia di porting che adotterai (porting in una sola fase o porting graduale), il primo passaggio consiste nel preparare il progetto per il porting. Ecco un riepilogo di quanto descritto in Strategie per il porting di, per quanto riguarda il tipo di progetto con cui inizierai 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.
- Trasferimento graduale di un progetto non XAML. È possibile scegliere di aggiungere il supporto C++/WinRT al progetto C++/CX (vedere Acquisizione di un progetto C++/CX e aggiunta graduale del supporto C++/WinRT), e effettuare il porting 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), spostare gradualmente i file e la porta.
- Conversione graduale di un progetto XAML. Creare un nuovo progetto C++/WinRT, spostare i file e effettuare gradualmente il porting. In qualsiasi momento i tipi di pagina XAML devono essere tutti i C++/WinRT o tutti I 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 stai effettuando il porting in modo graduale, probabilmente desidererai anche consultare l'interoperabilità tra C++/WinRT e C++/CX e l'asincronia e l'interoperabilità tra C++/WinRT e C++/CX.
Convenzioni 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 generati XAML | 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 il .xaml dai nomi di file *.h e *.cpp.
C++/WinRT aggiunge un file aggiuntivo per sviluppatori, il file Midl (.idl). C++/CX genera automaticamente questo file internamente, aggiungendo ogni membro pubblico e protetto. In C++/WinRT aggiungi e crea il file manualmente. Per altri dettagli, esempi di codice e procedura dettagliata sulla creazione di IDL, vedi controlli XAML; eseguire l'associazione a una proprietà C++/WinRT.
Vedere anche l'integrazione delle 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 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.hpartial ref class LongNameForA { ... } |
LongNameForA.hnamespace implements {struct LongNameForA { ... };} |
Requisiti dei file di intestazione
C++/CX non richiede l'inclusione di file di intestazione speciali, perché genera internamente i file di intestazione dai file di .winmd. In C++/CX è comune usare direttive using per gli spazi dei nomi usati per 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 il prefisso namespace. Abbiamo anche toccato lo spazio dei nomi Windows.Media.Core, perché item->VideoTracks->GetAt(0) restituisce un Windows.Media.Core.VideoTrack. Ma non è stato necessario digitare il nome VideoTrack ovunque, quindi non abbiamo avuto bisogno di una direttiva using Windows.Media.Core.
Ma C++/WinRT richiede di includere un file header corrispondente a ogni spazio dei nomi utilizzato, anche se non lo menzioni.
#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, poiché quel tipo di evento non è stato utilizzato.
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
// MainPage.h
#include <winrt/Windows.UI.Xaml.Shapes.h>
Se si dimentica un file di intestazione, tutto verrà compilato correttamente, ma si otterranno errori del linker perché mancano le classi consume_.
Passaggio di parametri
Quando si scrive codice sorgente C++/CX, si passano i tipi C++/CX come parametri di funzione usando riferimenti hat (^).
void LogPresenceRecord(PresenceRecord^ record);
In C++/WinRT, per le funzioni sincrone, è consigliabile usare i parametri const& per impostazione predefinita. In questo modo si evitano copie e sovraccarichi interbloccati. Tuttavia, le coroutine dovrebbero usare *pass-by-value* per garantire che acquisiscano per valore ed evitino problemi di durata (per ulteriori dettagli, vedi concorrenza e operazioni asincrone con C++/WinRT).
void LogPresenceRecord(PresenceRecord const& record);
IASyncAction LogPresenceRecordAsync(PresenceRecord const record);
Un oggetto C++/WinRT è fondamentalmente un'entità che contiene un puntatore di interfaccia all'oggetto di runtime di Windows sottostante. Quando si copia un oggetto C++/WinRT, il compilatore copia il puntatore incapsulato dell'interfaccia, incrementando il suo conteggio di 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 nel codice C++/WinRT equivalente, è possibile ottenere un lungo percorso rimuovendo i cappelli e modificando l'operatore freccia (->) all'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, vedere Inizializzazione ritardata.
Proprietà
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, PresenceDeviceRecordse 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 a sua volta una funzione Size. Poiché l'oggetto restituito è anche un tipo proiettato C++/WinRT, lo dereferenziamo utilizzando 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 eseguire l'equivalente in C++/WinRT, chiamare una funzione con lo stesso nome della proprietà e passare 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 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; in modo da poterlo allocare nello stack o come campo di un oggetto. Non mai usare ref new (né new) per allocare un oggetto C++/WinRT. Dietro le quinte, RoActivateInstance viene ancora chiamato.
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. Nota l'uso del costruttore std::nullptr_t. Per ulteriori informazioni su quel 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.
| Sceneggiatura | C++/CX | C++/WinRT (errato) | C++/WinRT (corretto) |
|---|---|---|---|
| Variabile locale, inizialmente vuota | TextBox^ textBox; |
TextBox textBox; // Creates a TextBox! |
TextBox textBox{ nullptr }; |
| Variabile di 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 }; |
| Paio | 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 ha un Platform::Array^ (vedere come portare Platform::Array^) in C++/CX, è possibile convertirlo in uno std::vector in C++/WinRT (in effetti, qualsiasi contenitore contiguo) invece di lasciarlo come matrice. La scelta di std::vectoroffre 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 ce ne sono troppo pochi, gli extra verranno costruiti automaticamente.
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 di std::map
L'operatore [] di indice per std::map funziona 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 composta dalla chiave (spostata, se mobile) e un valore costruito di defaulte restituire un riferimento al valore (che puoi quindi 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 usa dynamic_cast per eseguire il cast il riferimento alla base in un riferimento alla derivata. Il dynamic_cast è in realtà solo una chiamata nascosta a QueryInterface. Di seguito è riportato 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(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 il dynamic_cast con una chiamata alla funzione IUnknown::try_as, che incapsula QueryInterface. È anche possibile chiamare IUnknown::as, che genera invece un'eccezione se si esegue una query per l'interfaccia richiesta (l'interfaccia predefinita del tipo richiesto) non viene restituita. 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 derivare da una classe di runtime, la classe base deve essere componibile. C++/CX non richiede alcuna procedura speciale per rendere componibili le classi, ma C++/WinRT sì. Si utilizza la parola chiave non sigillato per indicare che si vuole che la classe sia utilizzabile come classe di base.
unsealed runtimeclass BasePage : Windows.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, riceverai 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 come funzione libera o come puntatore a funzione membro. Per ulteriori informazioni, vedi Gestire gli eventi usando i delegati in C++/WinRT.
Quando si esegue la conversione da una base di codice C++/CX in cui gli eventi e i delegati vengono utilizzati internamente (non tra file binari), winrt::delegate aiuterà a replicare tale modello in C++/WinRT. Si veda 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 dell'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 converte automaticamente gli scalari in oggetti. C++/WinRT richiede di chiamare in modo esplicito la funzione winrt::box_value. Entrambe le lingue richiedono di fare unboxing in modo esplicito. Vedere 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; |
| Operazione | C++/CX | C++/WinRT |
|---|---|---|
| Boxe | 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 effettuare l'unboxing di un puntatore nullo in un tipo valore. C++/WinRT considera questo un errore di programmazione e si blocca. 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.
| Sceneggiatura | C++/CX | C++/WinRT |
|---|---|---|
| Estrarre un numero intero noto | i = (int)o; |
i = unbox_value<int>(o); |
| Se o è null | Platform::NullReferenceException |
Scontro |
| Se o non è un int incapsulato | Platform::InvalidCastException |
Scontro |
| Disimballa int, usa il fallback se null; crasha se qualcos'altro. | i = o ? (int)o : fallback; |
i = o ? unbox_value<int>(o) : fallback; |
| Se possibile, deincapsulare int; utilizzare il fallback per qualsiasi altra cosa | 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, un null HSTRING rappresenta la stringa vuota. Il boxing di elementi non derivati da IInspectable viene eseguito incapsulandoli all'interno di un IReference<T>, e Windows Runtime fornisce un'implementazione standard sotto forma di oggetto PropertyValue (i tipi personalizzati sono rappresentati come PropertyType::OtherType).
C++/CX rappresenta una stringa di Windows Runtime come tipo riferimento; mentre C++/WinRT proietta una stringa come tipo valore. Ciò significa che una stringa nulla incapsulata può avere rappresentazioni diverse a seconda del modo in cui ci si è arrivati.
C++/CX consente inoltre di dereferenziare un valore null String^, in tal caso si comporta come la stringa "".
| Comportamento | C++/CX | C++/WinRT |
|---|---|---|
| Dichiarazioni | Object^ o;String^ s; |
IInspectable o;hstring s; |
| Categoria tipo stringa | Tipo di riferimento | Tipo di valore |
| progetti come HSTRING | (String^)nullptr |
hstring{} |
Null e "" identici? |
Sì | Sì |
| Validità del 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 |
Il boxing di base e l'unboxing.
| Operazione | C++/CX | C++/WinRT |
|---|---|---|
| Imballa una stringa | o = s;La stringa vuota diventa nullptr. |
o = box_value(s);La stringa vuota diventa un oggetto non Null. |
| Estrarre 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 dall'oggetto Null. Arresto anomalo se non è una stringa. |
| Estrarre una stringa potenziale | s = dynamic_cast<String^>(o);Un oggetto Null o non stringa diventa una stringa vuota. |
s = unbox_value_or<hstring>(o, fallback);Nulla o una stringa non valida si trasforma in fallback. Stringa vuota preservata. |
Concorrenza e operazioni asincrone
La libreria PPL (Parallel Patterns Library) (concurrency::task, ad esempio) è stata aggiornata per supportare i riferimenti hat C++/CX.
Per C++/WinRT, devi usare le coroutine e co_await. Per altre info ed esempi di codice, vedi Concorrenza e Operazioni Asincrone con C++/WinRT.
Consumo di oggetti dal markup XAML
In un progetto C++/CX puoi utilizzare membri privati e elementi denominati dal markup XAML. Tuttavia, in C++/WinRT, tutte le entità consumate utilizzando 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<Booleano> in C++/WinRT.
Per ulteriori informazioni ed esempi di codice, consulta Utilizzo di oggetti dal markup.
Mapping dei tipi della piattaforma C++/CX ai tipi C++/WinRT
C++/CX fornisce diversi tipi di dati nello spazio dei nomi piattaforma. Questi tipi non sono standard C++, quindi è possibile usarli solo quando si abilitano le estensioni del linguaggio Windows Runtime (proprietà del progetto di Visual Studio C/C++>Generale>Utilizzare l'estensione Windows Runtime>Sì (/ZW)). La tabella seguente ti aiuta a trasportare i tipi della piattaforma ai loro equivalenti in C++/WinRT. Una volta completato, 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.
winrt::agile_ref<Windows::UI::Core::CoreWindow> m_window;
Porta 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 capire perché std::vector è una buona scelta.
Pertanto, ogni volta che si dispone di un Platform::Array^ in C++/CX, le opzioni di conversione includono l'uso di un elenco di inizializzatori, un std::arrayo un std::vector. Per ulteriori informazioni ed esempi di codice, vedi gli elenchi di inizializzatori 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 HRESULT non S_OK. L'equivalente C++/WinRT è winrt::hresult_error.
Per eseguire la conversione in 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 | chiamata 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_CAMBIATO_STATO |
| winrt::hresult_class_not_available | winrt::hresult_error | CLASS_E_CLASSNOTAVAILABLE |
| winrt::hresult_illegal_delegate_assignment | winrt::hresult_error | Assegnazione di delega illegale |
| 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 l'HRESULT dell'errore, e una funzione message, che restituisce la rappresentazione in 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. Con C++/WinRT, tuttavia, puoi chiamare le API di Windows Runtime usando i tipi di stringa wide della Libreria Standard di C++, ad esempio std::wstringe/o valori letterali di stringa wide. Per altri dettagli ed esempi di codice, vedere gestione delle stringhe in C++/WinRT.
Con C++/CX, è possibile accedere alla proprietà
auto var{ titleRecord->TitleName->Data() };
Per eseguire la stessa operazione con C++/WinRT, puoi usare la funzione hstring::c_str per ottenere una versione di stringa stile C terminata con null, proprio come puoi fare 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 tale API in MIDL 3.0 così.
// LogType.idl
void LogWrapLine(String str);
La toolchain C++/WinRT genererà quindi un codice sorgente che apparirà 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. Devi aggiungere overload per qualsiasi tipo aggiuntivo che vuoi convertire in stringa.
| Lingua | Conversione di int a stringa | Converti enum in 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 di conversione in stringa di un enum, è 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 stringificazioni vengono spesso utilizzate in modo implicito dall'associazione dati (data binding).
<TextBlock>
You have <Run Text="{Binding FlowerCount}"/> flowers.
</TextBlock>
<TextBlock>
Most recent status is <Run Text="{x:Bind LatestOperation.Status}"/>.
</TextBlock>
Queste associazioni eseguiranno 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.
Costruzione di stringhe
C++/CX e C++/WinRT rinviano allo standard std::wstringstream per la compilazione di stringhe.
| Operazione | C++/CX | C++/WinRT |
|---|---|---|
| Accoda stringa, conservando i valori null | stream.print(s->Data(), s->Length); |
stream << std::wstring_view{ s }; |
| Aggiungi stringa, interrompi 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 un Platform::String da una stringa a 8 bit, C++/WinRT non esegue questa operazione.
| Operazione | 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, mantenendo i valori 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 |
| Eseguire la conversione da std::wstring, fermarsi al primo carattere nullo | 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 |
| Eseguire la conversione 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, fermarsi al primo valore 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 struct winrt::d elegate
- struct winrt::hresult_error
- struct winrt::hstring
- spazio dei nomi winrt
Argomenti correlati
- C++/CX
- Creare eventi in C++/WinRT
- concorrenza e operazioni asincrone con C++/WinRT
- Consumare le API con C++/WinRT
- Gestire gli eventi usando delegati in C++/WinRT
- L'interoperabilità tra C++/WinRT e C++/CX
- L'Asincronia e l'interoperabilità tra C++/WinRT e C++/CX
- Microsoft Interface Definition Language 3.0 riferimento
- Passare da WRL a C++/WinRT
- gestione delle stringhe in C++/WinRT