Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Este tópico é o primeiro de uma série que descreve como pode portar o código-fonte do seu projeto C++/CX para o seu equivalente em C++/WinRT.
Se o seu projeto também utilizar tipos de Biblioteca de Modelos C++ do Windows Runtime (WRL), consulte Passar de WRL para C++/WinRT.
Estratégias para portabilidade
Vale a pena saber que a migração de C++/CX para C++/WinRT é, regra geral, simples, com a única exceção da passagem de tarefas da Parallel Patterns Library (PPL) para corrotinas. Os modelos são diferentes. Não existe uma correspondência biunívoca natural entre tarefas PPL e corrotinas, nem uma forma simples de transpor mecanicamente o código que funcione em todos os casos. Para ajuda com este aspeto específico da portabilidade, e as suas opções para interoperar entre os dois modelos, veja Assíncronia e interoperabilidade entre C++/WinRT e C++/CX.
As equipas de desenvolvimento relatam habitualmente que, uma vez superada a dificuldade de portar o seu código assíncrono, o restante trabalho de portabilidade é maioritariamente mecânico.
Migração numa única passagem
Se conseguires portar todo o teu projeto de uma só vez, então só precisarás deste tema para a informação de que precisas (e não vais precisar dos tópicos de interoperabilidade que se seguem a este). Recomendamos que comece por criar um novo projeto no Visual Studio usando um dos modelos de projeto C++/WinRT (veja o suporte do Visual Studio para C++/WinRT). Depois move os ficheiros do código-fonte para esse novo projeto e porta todo o código-fonte C++/CX para C++/WinRT à medida que o fazes.
Alternativamente, se preferires fazer o trabalho de portabilidade no teu projeto C++/CX existente, então terás de adicionar suporte a C++/WinRT. Os passos que segue para o fazer estão descritos em Pegar num projeto C++/CX e adicionar suporte a C++/WinRT. Quando terminares de portar, já terás transformado o que era um projeto puramente C++/CX num projeto puro C++/WinRT.
Note
Se tiveres um projeto de componente do Windows Runtime, migrar de uma só vez é a tua única opção. Um projeto componente Windows Runtime escrito em C++ deve conter todo o código-fonte C++/CX, ou todo o código-fonte C++/WinRT. Não podem coexistir neste tipo de projeto.
Portabilidade gradual de um projeto
Com exceção dos projetos de componentes do Windows Runtime, como mencionado na secção anterior, se o tamanho ou complexidade da sua base de código tornar necessário portar o seu projeto gradualmente, então precisará de um processo de portabilidade em que, durante algum tempo, o código C++/CX e C++/WinRT exista lado a lado no mesmo projeto. Além de ler este tópico, veja também Interop entre C++/WinRT e C++/CX e Assíncronia, e Interop entre C++/WinRT e C++/CX. Esses tópicos fornecem informações e exemplos de código que mostram como interoperar entre as projeções das duas línguas.
Para preparar um projeto para um processo gradual de portabilidade, uma opção é adicionar suporte a C++/WinRT ao seu projeto C++/CX. Os passos que segue para o fazer estão descritos em Pegar num projeto C++/CX e adicionar suporte a C++/WinRT. Pode então migrar gradualmente a partir desse ponto.
Outra opção é criar um novo projeto no Visual Studio usando um dos modelos de projeto C++/WinRT (ver suporte Visual Studio para C++/WinRT). E depois adicionar suporte a C++/CX a esse projeto. Os passos que segue para o fazer são descritos em Pegar num projeto C++/WinRT e adicionar suporte para C++/CX. Depois podes começar a mover o teu código-fonte para lá e portar parte do código-fonte C++/CX para C++/WinRT à medida que o fazes.
Em qualquer dos casos, vais interoperar (de ambas as formas) entre o teu código C++/WinRT e qualquer código C++/CX que ainda não tenhas portado.
Note
Tanto o C++/CX como o SDK do Windows declaram tipos no espaço de nomes de raiz Windows. Um tipo Windows projetado em C++/WinRT tem o mesmo nome totalmente qualificado que o tipo Windows, mas está colocado no espaço de nomes winrt em C++. Estes namespaces distintos permitem-te portar de C++/CX para C++/WinRT ao teu próprio ritmo.
Portabilidade gradual de um projeto XAML
Important
Para um projeto que usa XAML, a qualquer momento todos os tipos de página XAML têm de ser inteiramente C++/CX ou inteiramente C++/WinRT. Ainda podes misturar C++/CX e C++/WinRT fora dos tipos de página XAML dentro do mesmo projeto (nos teus modelos e viewmodels, e noutros locais).
Neste cenário, o fluxo de trabalho que recomendamos é criar um novo projeto C++/WinRT e copiar o código-fonte e a marcação a partir do projeto C++/CX. Desde que todos os seus tipos de página XAML sejam C++/WinRT, pode adicionar novas páginas XAML com Projeto>Adicionar Novo Item...>Visual C++>Página em Branco (C++/WinRT).
Em alternativa, podes usar um componente do Windows Runtime (WRC) para fatorar código do projeto XAML C++/CX à medida que o portas.
- Podes criar um novo projeto WRC em C++/CX, transferir o máximo de código C++/CX possível para esse projeto, e depois mudar o projeto XAML para C++/WinRT.
- Ou pode criar um novo projeto WRC em C++/WinRT, deixar o projeto XAML como C++/CX e começar a portar C++/CX para C++/WinRT, movendo o código resultante para fora do projeto XAML e para o projeto componente.
- Também podes ter um projeto de componente C++/CX juntamente com um projeto de componente C++/WinRT dentro da mesma solução, referenciar ambos do teu projeto de aplicação e portar gradualmente de um para o outro. Mais uma vez, veja Interop entre C++/WinRT e C++/CX para mais detalhes sobre a utilização das projeções das duas linguagens no mesmo projeto.
Primeiros passos na portabilidade de um projeto C++/CX para C++/WinRT
Independentemente da sua estratégia de portabilidade (portar de uma só vez ou de forma gradual), o primeiro passo é preparar o seu projeto para portabilidade. Aqui está um resumo do que descrevemos em Estratégias para portabilidade , em termos do tipo de projeto com que vais começar e como o configurar.
- Fazer o porting numa só passagem. Crie um novo projeto no Visual Studio usando um dos templates de projeto C++/WinRT. Move os ficheiros do teu projeto C++/CX para esse novo projeto e porta o código-fonte C++/CX.
- Portar um projeto que não seja XAML gradualmente. Pode optar por adicionar suporte a C++/WinRT ao seu projeto C++/CX (veja Pegar num projeto C++/CX e adicionar suporte a C++/WinRT) e portar gradualmente. Ou pode optar por criar um novo projeto C++/WinRT e adicionar suporte a C++/CX (veja Pegar num projeto C++/WinRT e adicionar suporte a C++/CX), mover ficheiros e portar gradualmente.
- Portando um projeto XAML gradualmente. Criar um novo projeto C++/WinRT, mover ficheiros e portar gradualmente. Em qualquer momento, os tipos de página XAML devem ser todos C++/ WinRT ou todos C++/CX.
O resto deste tópico aplica-se independentemente da estratégia de portabilidade que escolher. Contém um catálogo de detalhes técnicos envolvidos na portabilidade do código-fonte de C++/CX para C++/WinRT. Se estiveres a migrar gradualmente, provavelmente também quererás consultar Interoperabilidade entre C++/WinRT e C++/CX e Assincronia e interoperabilidade entre C++/WinRT e C++/CX.
Convenções de nomenclatura de ficheiros
Ficheiros de marcação XAML
| Origem do ficheiro | C++/CX | C++/WinRT |
|---|---|---|
| Ficheiros XAML para desenvolvedores | MyPage.xaml MyPage.xaml.h MyPage.xaml.cpp |
MyPage.xaml MyPage.h MyPage.cpp MyPage.idl (ver abaixo) |
| Ficheiros XAML gerados | MyPage.xaml.g.h MyPage.xaml.g.hpp |
MyPage.xaml.g.h MyPage.xaml.g.hpp MyPage.g.h |
Repare que o C++/WinRT remove o .xaml dos nomes de ficheiro *.h e *.cpp.
C++/WinRT adiciona um ficheiro adicional de programador, o ficheiro Midl (.idl). O C++/CX gera automaticamente este ficheiro internamente, adicionando-lhe todos os membros públicos e protegidos. Em C++/WinRT, adicionas e escreves o ficheiro tu próprio. Para mais detalhes, exemplos de código e uma explicação passo a passo sobre como criar IDL, consulte Controlos XAML; associar a uma propriedade C++/WinRT.
Veja também Incluir classes de runtime em ficheiros MIDL (.idl)
Classes de execução
O C++/CX não impõe restrições aos nomes dos teus ficheiros de cabeçalho; É comum colocar várias definições de classes de execução num único ficheiro de cabeçalho, especialmente para classes pequenas. Mas o C++/WinRT exige que cada classe de runtime tenha o seu próprio ficheiro de cabeçalho com o nome da 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 { ... };} |
Menos comum (mas ainda legal) em C++/CX é usar ficheiros de cabeçalho com nomes diferentes para controlos personalizados XAML. Terá de renomear estes ficheiros de cabeçalho para corresponder ao nome da 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 { ... };} |
Requisitos do ficheiro de cabeçalho
O C++/CX não exige que incluas ficheiros de cabeçalho especiais, porque gera automaticamente ficheiros de cabeçalho a partir de .winmd ficheiros. É comum em C++/CX usar diretivas using para namespaces que utiliza pelo nome.
using namespace Windows::Media::Playback;
String^ NameOfFirstVideoTrack(MediaPlaybackItem^ item)
{
return item->VideoTracks->GetAt(0)->Name;
}
A using namespace Windows::Media::Playback diretiva permite-nos escrever MediaPlaybackItem sem um prefixo de namespace. Também abordámos o Windows.Media.Core espaço de nomes, porque item->VideoTracks->GetAt(0) devolve um Windows.Media.Core.VideoTrack. Mas não tínhamos de escrever o nome VideoTrack em lado nenhum, por isso não precisávamos de uma using Windows.Media.Core diretiva.
Mas o C++/WinRT exige que incluas um ficheiro de cabeçalho correspondente a cada namespace que consomes, mesmo que não o nomees.
#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();
}
Por outro lado, mesmo que o evento MediaPlaybackItem.AudioTracksChanged seja do tipo TypedEventHandler<MediaPlaybackItem, Windows. Foundation.Collections.IVectorChangedEventArgs>, não precisamos de incluir winrt/Windows.Foundation.Collections.h porque não usámos esse evento.
O C++/WinRT também exige que incluas ficheiros de cabeçalho para namespaces que são consumidos pela marcação XAML.
<!-- MainPage.xaml -->
<Rectangle Height="400"/>
Usar a classe Rectangle significa que tens de adicionar este include.
// MainPage.h
#include <winrt/Microsoft.UI.Xaml.Shapes.h>
Se te esqueceres de um ficheiro de cabeçalho, tudo compilará corretamente, mas obterás erros de ligação porque faltam as classes consume_.
Passagem de parâmetros
Ao escrever código-fonte em C++/CX, os tipos de C++/CX passam-se como parâmetros de função na forma de referências assinaladas com acento circunflexo (^).
void LogPresenceRecord(PresenceRecord^ record);
No C++/WinRT, para funções síncronas, deve usar parâmetros const& por predefinição. Isso evita cópias e sobrecarga interligada. Mas as suas corrotinas devem usar pass-by-value para garantir que capturam por valor e evitar problemas ao longo da vida (para mais detalhes, veja Concorrência e operações assíncronas com C++/WinRT).
void LogPresenceRecord(PresenceRecord const& record);
IASyncAction LogPresenceRecordAsync(PresenceRecord const record);
Um objeto C++/WinRT é, fundamentalmente, um valor que armazena um ponteiro de interface para o objeto subjacente do Windows Runtime. Quando copias um objeto C++/WinRT, o compilador copia o ponteiro de interface encapsulado, incrementando a sua contagem de referências. A destruição eventual da cópia envolve a diminuição da contagem de referências. Por isso, só assuma a sobrecarga de uma cópia quando for necessário.
Referências de variáveis e de campo
Ao escrever código-fonte C++/CX, utiliza-se variáveis hat (^) para referenciar objetos do Windows Runtime, e o operador seta (->) para desreferenciar uma variável hat.
IVectorView<User^>^ userList = User::Users;
if (userList != nullptr)
{
for (UINT32 iUser = 0; iUser < userList->Size; ++iUser)
...
Ao portar para o código equivalente C++/WinRT, pode avançar muito removendo os hats e mudando o operador seta (->) para o operador dot (.). Os tipos projetados em C++/WinRT são valores, e não apontadores.
IVectorView<User> userList = User::Users();
if (userList != nullptr)
{
for (UINT32 iUser = 0; iUser < userList.Size(); ++iUser)
...
O construtor padrão para uma referência de chapéu C++/CX inicializa-a como nula. Aqui está um exemplo de código C++/CX em que criamos uma variável/campo do tipo correto, mas sem inicializar. Por outras palavras, não faz inicialmente referência a um TextBlock; pretendemos atribuir-lhe uma referência mais tarde.
TextBlock^ textBlock;
class MyClass
{
TextBlock^ textBlock;
};
Para o equivalente em C++/WinRT, veja Inicialização atrasada.
Properties
As extensões da linguagem C++/CX incluem o conceito de propriedades. Ao escrever código-fonte C++/CX, pode aceder a uma propriedade como se fosse um campo. O C++ padrão não tem o conceito de propriedade, por isso, em C++/WinRT, chama-se as funções get e set.
Nos exemplos que se seguem, XboxUserId, UserState, PresenceDeviceRecords e Size são todas propriedades.
Recuperar um valor de uma propriedade
Aqui está como se obtém o valor de uma propriedade em C++/CX.
void Sample::LogPresenceRecord(PresenceRecord^ record)
{
auto id = record->XboxUserId;
auto state = record->UserState;
auto size = record->PresenceDeviceRecords->Size;
}
O código-fonte equivalente C++/WinRT chama uma função com o mesmo nome da propriedade, mas sem parâmetros.
void Sample::LogPresenceRecord(PresenceRecord const& record)
{
auto id = record.XboxUserId();
auto state = record.UserState();
auto size = record.PresenceDeviceRecords().Size();
}
Note que a função PresenceDeviceRecords devolve um objeto Windows Runtime que por sua vez tem uma função Size. Como o objeto devolvido também é um tipo projetado em C++/WinRT, fazemos a desreferenciação com o operador ponto para chamar Size.
Atribuir uma propriedade a um novo valor
Atribuir um valor novo a uma propriedade segue um padrão semelhante. Primeiro, em C++/CX.
record->UserState = newValue;
Para fazer o equivalente em C++/WinRT, chama-se uma função com o mesmo nome da propriedade e passa um argumento.
record.UserState(newValue);
Criando uma instância de uma classe
Trabalhas com um objeto C++/CX através de um handle, conhecido como referência hat (^). Crias um novo objeto através da ref new palavra-chave, que por sua vez chama o RoActivateInstance para ativar uma nova instância da classe de runtime.
using namespace Windows::Storage::Streams;
class Sample
{
private:
Buffer^ m_gamerPicBuffer = ref new Buffer(MAX_IMAGE_SIZE);
};
Um objeto C++/WinRT é um valor; Por isso, podes alocá-lo na pilha, ou como campo de um objeto.
Nunca usas ref new (nem new) para alocar um objeto C++/WinRT. Nos bastidores, o RoActivateInstance continua a ser chamado.
using namespace winrt::Windows::Storage::Streams;
struct Sample
{
private:
Buffer m_gamerPicBuffer{ MAX_IMAGE_SIZE };
};
Se um recurso for caro de inicializar, é comum adiar a inicialização até ser realmente necessário. Como já foi mencionado, o construtor padrão para uma referência chapéu C++/CX inicializa-a como nula.
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;
};
O mesmo código portado para C++/WinRT. Note a utilização do construtor std::nullptr_t . Para mais informações sobre esse construtor, veja Inicialização atrasada.
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 };
};
Como o construtor padrão afeta as coleções
Os tipos de coleção em C++ usam o construtor padrão, o que pode resultar na construção não intencional de objetos.
| Scenario | C++/CX | C++/WinRT (incorreto) | C++/WinRT (correto) |
|---|---|---|---|
| Variável local, inicialmente vazia | TextBox^ textBox; |
TextBox textBox; // Creates a TextBox! |
TextBox textBox{ nullptr }; |
| Variável membro, inicialmente vazia | class C {TextBox^ textBox;}; |
class C {TextBox textBox; // Creates a TextBox!}; |
class C {TextBox textbox{ nullptr };}; |
| Variável global, inicialmente vazia | TextBox^ g_textBox; |
TextBox g_textBox; // Creates a TextBox! |
TextBox g_textBox{ nullptr }; |
| Vetor de referências vazias | std::vector<TextBox^> boxes(10); |
// Creates 10 TextBox objects!std::vector<TextBox> boxes(10); |
std::vector<TextBox> boxes(10, nullptr); |
| Defina um valor num mapa | std::map<int, TextBox^> boxes;boxes[2] = value; |
std::map<int, TextBox> boxes;// Creates a TextBox at 2,// then overwrites it!boxes[2] = value; |
std::map<int, TextBox> boxes;boxes.insert_or_assign(2, value); |
| Array de referências vazias | TextBox^ boxes[2]; |
// Creates 2 TextBox objects!TextBox boxes[2]; |
TextBox boxes[2] = { nullptr, nullptr }; |
| Associar | std::pair<TextBox^, String^> p; |
// Creates a TextBox!std::pair<TextBox, String> p; |
std::pair<TextBox, String> p{ nullptr, nullptr }; |
Mais sobre coleções de referências vazias
Sempre que tens um Platform::Array^ (ver Port Platform::Array^) em C++/CX, tens a opção de o portar para um std::vector em C++/WinRT (na verdade, qualquer contentor contíguo) em vez de o deixar como um array. Há vantagens em escolher std::vector.
Por exemplo, embora exista uma abreviação para criar um vetor de tamanho fixo de referências vazias (ver tabela acima), não existe tal abreviação para criar um array de referências vazias. Tens de repetir nullptr para cada elemento num array. Se tiveres poucos elementos, os elementos extra serão inicializados por defeito.
Para um vetor, pode preenchê-lo com referências vazias na inicialização (como na tabela acima), ou pode preenchê-lo com referências vazias após a inicialização com código como este.
std::vector<TextBox> boxes(10); // 10 default-constructed TextBoxes.
boxes.resize(10, nullptr); // 10 empty references.
Mais sobre o exemplo std::map
O [] operador de índice para std::map funciona assim.
- Se a chave for encontrada no mapa, devolve uma referência ao valor existente (que pode sobrescrever).
- Se a chave não for encontrada no mapa, então cria uma nova entrada no mapa que consista na chave (movida, se móvel) e um valor construído por defeito, e devolve uma referência ao valor (que depois podes sobrescrever).
Por outras palavras, o [] operador cria sempre uma entrada no mapa. Isto é diferente do C#, Java e JavaScript.
Conversão de uma classe base em tempo de execução para uma derivada
É comum ter uma referência para uma classe base que sabemos que se refere a um objeto de um tipo derivado. Em C++/CX, costumas dynamic_casttransformar a referência-para-base numa referência-para-derivado. O dynamic_cast é, na verdade, apenas uma chamada oculta para QueryInterface. Suponha que tem um exemplo típico — está a processar um evento de alteração de uma propriedade de dependência e quer converter de DependencyObject novamente para o tipo real ao qual pertence a propriedade de dependência.
void BgLabelControl::OnLabelChanged(Microsoft::UI::Xaml::DependencyObject^ d, Microsoft::UI::Xaml::DependencyPropertyChangedEventArgs^ e)
{
BgLabelControl^ theControl{ dynamic_cast<BgLabelControl^>(d) };
if (theControl != nullptr)
{
// succeeded ...
}
}
O código equivalente C++/WinRT substitui o dynamic_cast por uma chamada à função IUnknown::try_as , que encapsula o QueryInterface. Também tem a opção de chamar IUnknown::as, o que gera uma exceção se a consulta para a interface necessária (a interface padrão do tipo que está a pedir) não for retornada. Aqui está um exemplo de código 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 ...
}
}
Classes derivadas
Para derivar de uma classe de execução, a classe base deve ser componível. C++/CX não exige que tomes passos especiais para tornar as tuas classes componíveis, mas C++/WinRT exige. Usas a palavra-chave não selada para indicar que queres que a tua classe seja utilizável como classe base.
unsealed runtimeclass BasePage : Microsoft.UI.Xaml.Controls.Page
{
...
}
runtimeclass DerivedPage : BasePage
{
...
}
Na sua classe de cabeçalho de implementação, deve incluir o ficheiro de cabeçalho da classe base antes de incluir o cabeçalho gerado automaticamente para a classe derivada. Caso contrário, vais receber erros como "Uso ilegal deste tipo como expressão".
// 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>
{
...
}
}
Gestão de eventos com um delegado
Aqui está um exemplo típico de como lidar com um evento em C++/CX, usando uma função lambda como delegado neste 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.
});
Isto é equivalente em 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.
});
Em vez de uma função lambda, pode optar por implementar o seu delegado como uma função livre, ou como uma função apontadora para membro. Para mais informações, veja Gerir eventos usando os delegados em C++/WinRT.
Se estiveres a migrar de uma base de código C++/CX em que eventos e delegates são usados internamente (não entre binários), então winrt::delegate ajudar-te-á a replicar esse padrão em C++/WinRT. Veja também delegados parametrizados, sinais simples e callbacks dentro de um projeto.
Revogação de um delegado
Em C++/CX usas o -= operador para revogar um registo de eventos anteriores.
myButton->Click -= token;
Isto é equivalente em C++/WinRT.
myButton().Click(token);
Para mais informações e opções, consulte Revogar um delegado registado.
Boxe e desembalagem
C++/CX encaixa automaticamente escalares em objetos. O C++/WinRT exige que chames explicitamente a função winrt::box_value . Ambas as línguas exigem que desembales explicitamente. Consulte Boxing e unboxing com C++/WinRT.
Nas tabelas que se seguem, usaremos estas definições.
| C++/CX | C++/WinRT |
|---|---|
int i; |
int i; |
String^ s; |
winrt::hstring s; |
Object^ o; |
IInspectable o; |
| Operation | C++/CX | C++/WinRT |
|---|---|---|
| Boxe | 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# criam exceções se tentares abrir um ponteiro nulo para um tipo de valor. O C++/WinRT considera isto um erro de programação, e crasha. Em C++/WinRT, usa a função winrt::unbox_value_or se quiseres lidar com o caso em que o objeto não é do tipo que pensavas.
| Scenario | C++/CX | C++/WinRT |
|---|---|---|
| Desencapsular um número inteiro conhecido | i = (int)o; |
i = unbox_value<int>(o); |
| Se o for nulo | Platform::NullReferenceException |
Colisão |
| Se o não for um int encapsulado | Platform::InvalidCastException |
Colisão |
| Desempacotar int, usar alternativa se for nulo; falhar se for qualquer outra coisa | i = o ? (int)o : fallback; |
i = o ? unbox_value<int>(o) : fallback; |
| Desempacote int, se possível; use a alternativa para qualquer outro caso | auto box = dynamic_cast<IBox<int>^>(o);i = box ? box->Value : fallback; |
i = unbox_value_or<int>(o, fallback); |
Boxe e desembalagem de uma corda
Uma cadeia é, de certa forma, um tipo de valor e, noutros aspetos, um tipo de referência. C++/CX e C++/WinRT tratam as cadeias de forma diferente.
O tipo ABI HSTRING é um apontador para uma cadeia contada por referência. Mas não deriva de IInspectable, por isso tecnicamente não é um objeto. Além disso, uma HSTRING nula representa a cadeia vazia. O empacotamento de elementos não derivados de IInspectable faz-se envolvendo-os num IReference<T>, e o Windows Runtime fornece uma implementação padrão sob a forma do objeto PropertyValue (os tipos personalizados são indicados como PropertyType::OtherType).
C++/CX representa uma cadeia de Windows Runtime como tipo de referência; enquanto C++/WinRT projeta uma cadeia como tipo de valor. Isto significa que uma cadeia nula em caixa pode ter representações diferentes dependendo de como lá chegaste.
Além disso, C++/CX permite desreferenciar uma String^ nula, caso em que ela comporta-se como a string "".
| Behavior | C++/CX | C++/WinRT |
|---|---|---|
| Declarações | Object^ o;String^ s; |
IInspectable o;hstring s; |
| Categoria de tipos de cordas | Tipo de referência | Tipo de valor |
| projeta HSTRING nulos como | (String^)nullptr |
hstring{} |
São nulos e "" idênticos? |
Yes | Yes |
| Validade do nulo | s = nullptr;s->Length == 0 (válido) |
s = hstring{};s.size() == 0 (válido) |
| Se atribuires cadeia nula ao objeto | o = (String^)nullptr;o == nullptr |
o = box_value(hstring{});o != nullptr |
Se atribuires "" a um objeto |
o = "";o == nullptr |
o = box_value(hstring{L""});o != nullptr |
Boxe básico e desencaixe.
| Operation | C++/CX | C++/WinRT |
|---|---|---|
| Encapsular uma cadeia de caracteres | o = s;A string vazia torna-se nullptr. |
o = box_value(s);A cadeia vazia torna-se objeto não nulo. |
| Desencapsular uma cadeia de caracteres conhecida | s = (String^)o;O objeto nulo torna-se cadeia vazia. InvalidCastException se não for uma cadeia de caracteres. |
s = unbox_value<hstring>(o);O objeto nulo crasha. Falha se não for uma cadeia de caracteres. |
| Desempacotar uma cadeia de caracteres opcional | s = dynamic_cast<String^>(o);Objeto nulo ou valor que não seja uma cadeia de caracteres transforma-se em cadeia de caracteres vazia. |
s = unbox_value_or<hstring>(o, fallback);Nulo ou não-string torna-se um recurso. Fio vazio preservado. |
Simultaneidade e operações assíncronas
A Parallel Patterns Library (PPL) (concorrência::task, por exemplo) foi atualizada para suportar referências de chapéu C++/CX.
Para C++/WinRT, deves usar corrotinas e co_await em vez disso. Para mais informações e exemplos de código, veja Concorrência e operações assíncronas com C++/WinRT.
Consumir objetos a partir da marcação XAML
Num projeto C++/CX, pode consumir membros privados e elementos nomeados a partir da marcação XAML. Mas em C++/WinRT, todas as entidades consumidas ao usar a extensão de marcação XAML {x:Bind} devem ser expostas publicamente em IDL.
Além disso, a associação a um valor Boolean apresenta true ou false em C++/CX, mas mostra Windows.Foundation.IReference`1<Boolean> em C++/WinRT.
Para mais informações e exemplos de código, consulte Consumir objetos a partir de marcação.
Mapeamento de tipos Platform de C++/CX para tipos C++/WinRT
C++/CX fornece vários tipos de dados no namespace da Plataforma . Estes tipos não são C++ padrão, por isso só pode usá-los quando ativar extensões da linguagem Windows Runtime (propriedade do projeto Visual Studio C/C++>General>Consume Windows Runtime>Extension Yes (/ZW)). A tabela abaixo ajuda a migrar de tipos Platform para equivalentes em C++/WinRT. Depois de fazer isso, como C++/WinRT é C++ padrão, pode desativar essa /ZW opção.
| C++/CX | C++/WinRT |
|---|---|
| Platform::Agile^ | winrt::agile_ref |
| Plataforma::Array^ | Ver Port Platform::Array^ |
| Platform::Exception^ | winrt::hresult_error |
| Platform::InvalidArgumentException^ | winrt::hresult_invalid_argument |
| Platform::Object^ | winrt::Windows::Foundation::IInspectable |
| Platform::String^ | winrt::hstring |
Migrar Platform::Agile^ para winrt::agile_ref
O tipo Platform::Agile^ em C++/CX representa uma classe Windows Runtime que pode ser acedida a partir de qualquer thread. O equivalente em C++/WinRT é winrt::agile_ref.
Em C++/CX.
Platform::Agile<Windows::UI::Core::CoreWindow> m_window;
Em C++/WinRT (o WinUI 3 usa Microsoft::UI::Xaml::Window em vez de CoreWindow).
winrt::agile_ref<Microsoft::UI::Xaml::Window> m_window;
Port Platform::Array^
Nos casos em que o C++/CX exige que uses um array, o C++/WinRT permite usar qualquer contentor contíguo. Veja como o construtor padrão afeta as colecções por uma razão pela qual o std::vector é uma boa escolha.
Portanto, sempre que tens um Platform::Array^ em C++/CX, as tuas opções de portabilidade incluem usar uma lista de inicializadores, um std::array ou um std::vector. Para mais informações e exemplos de código, consulte listas de inicializadores padrão e arrays e vetores padrão.
Migrar Platform::Exception^ para winrt::hresult_error
O tipo Platform::Exception^ é gerado em C++/CX quando uma API do Windows Runtime devolve um valor HRESULT diferente de S_OK. O equivalente em C++/WinRT é winrt::hresult_error.
Para portar para C++/WinRT, altere todo o código que use Platform::Exception^ para usar winrt::hresult_error.
Em C++/CX.
catch (Platform::Exception^ ex)
Em C++/WinRT.
catch (winrt::hresult_error const& ex)
O C++/WinRT fornece estas classes de exceção.
| Tipo de exceção | Classe base | HRESULT |
|---|---|---|
| winrt::hresult_error | chamar 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 |
Note que cada classe (através da hresult_error classe base) fornece uma função to_abi , que devolve o HRESULT do erro, e uma função de mensagem , que devolve a representação da cadeia desse HRESULT.
Aqui está um exemplo de lançar uma exceção em C++/CX.
throw ref new Platform::InvalidArgumentException(L"A valid User is required");
E o equivalente em C++/WinRT.
throw winrt::hresult_invalid_argument{ L"A valid User is required" };
Migrar Platform::Object^ para winrt::Windows::Foundation::IInspectable
Como todos os tipos C++/WinRT, winrt::Windows::Foundation::IInspectable é um tipo de valor. Aqui está como se inicializa uma variável desse tipo como nula.
winrt::Windows::Foundation::IInspectable var{ nullptr };
Migrar Platform::String^ para winrt::hstring
Platform::String^ é equivalente ao tipo HSTRING ABI do Windows Runtime. Para C++/WinRT, o equivalente é winrt::hstring. Mas com C++/WinRT, pode chamar APIs do Windows Runtime usando tipos de cadeia de caracteres larga da Biblioteca Padrão do C++, como std::wstring, e/ou literais de cadeias de caracteres largas. Para mais detalhes e exemplos de código, veja Gestão de strings em C++/WinRT.
Com C++/CX, podes aceder à propriedade Platform::String::D ata para recuperar a cadeia como um array const wchar_t* ao estilo C (por exemplo, para a passar para std::wcout).
auto var{ titleRecord->TitleName->Data() };
Para fazer o mesmo com C++/WinRT, podes usar a função hstring::c_str para obter uma versão de string em estilo C com terminação nula, tal como podes fazer com std::wstring.
auto var{ titleRecord.TitleName().c_str() };
Quando se trata de implementar APIs que aceitam ou retornam strings, normalmente mudas qualquer código C++/CX que use Platform::String^ para usar winrt::hstring em vez disso.
Aqui está um exemplo de uma API C++/CX que aceita uma string.
void LogWrapLine(Platform::String^ str);
Para C++/WinRT podes declarar essa API no MIDL 3.0 assim.
// LogType.idl
void LogWrapLine(String str);
A cadeia de ferramentas C++/WinRT gera então o código-fonte para ti que se assemelha a este.
void LogWrapLine(winrt::hstring const& str);
ToString()
Os tipos C++/CX fornecem o método Object::ToString .
int i{ 2 };
auto s{ i.ToString() }; // s is a Platform::String^ with value L"2".
O C++/WinRT não oferece esta funcionalidade diretamente, mas pode recorrer a alternativas.
int i{ 2 };
auto s{ std::to_wstring(i) }; // s is a std::wstring with value L"2".
C++/WinRT também suporta winrt::to_hstring para um número limitado de tipos. Terás de adicionar sobrecargas para quaisquer tipos adicionais que queiras stringificar.
| Linguagem | Converter int em cadeia de caracteres | Transformar enum em cadeia de caracteres |
|---|---|---|
| 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); |
No caso de converter um enum em string, terá de fornecer a implementação de 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));
}
}
}
Estas conversões em cadeia de caracteres são frequentemente utilizadas de forma implícita pela associação de dados.
<TextBlock>
You have <Run Text="{Binding FlowerCount}"/> flowers.
</TextBlock>
<TextBlock>
Most recent status is <Run Text="{x:Bind LatestOperation.Status}"/>.
</TextBlock>
Estas ligações executarão winrt::to_hstring da propriedade vinculada. No caso do segundo exemplo (o StatusEnum), deve fornecer a sua própria sobrecarga de winrt::to_hstring; caso contrário, irá obter um erro de compilação.
Construção de cordas
C++/CX e C++/WinRT recorrem ao std::wstringstream padrão para criar cadeias de caracteres.
| Operation | C++/CX | C++/WinRT |
|---|---|---|
| Anexar cadeia de caracteres, preservando valores nulos | stream.print(s->Data(), s->Length); |
stream << std::wstring_view{ s }; |
| Acrescentar cadeia de caracteres, parar no primeiro carácter nulo | stream << s->Data(); |
stream << s.c_str(); |
| Extrair resultado | ws = stream.str(); |
ws = stream.str(); |
Mais exemplos
Nos exemplos abaixo, ws é uma variável do tipo std::wstring. Além disso, enquanto o C++/CX pode construir uma Platform::String a partir de uma string de 8 bits, o C++/WinRT não faz isso.
| Operation | C++/CX | C++/WinRT |
|---|---|---|
| Criar string a partir de um literal | String^ s = "hello";String^ s = L"hello"; |
// winrt::hstring s{ "hello" }; // Doesn't compilewinrt::hstring s{ L"hello" }; |
| Converter a partir de std::wstring, preservando os nulos | 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 |
| Converter a partir de std::wstring, parar no primeiro nulo | 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 |
| Converter para std::wstring, preservando os nulos | std::wstring ws{ s->Data(), s->Length };ws = std::wstring(s>Data(), s->Length); |
std::wstring ws{ s };ws = s; |
| Converter para std::wstring, parar no primeiro nulo | std::wstring ws{ s->Data() };ws = s->Data(); |
std::wstring ws{ s.c_str() };ws = s.c_str(); |
| Passar literal ao método | Method("hello");Method(L"hello"); |
// Method("hello"); // Doesn't compileMethod(L"hello"); |
| Passar std::wstring ao método | Method(ref new String(ws.c_str(),(uint32_t)ws.size()); // Stops on first null |
Method(ws);// param::winrt::hstring accepts std::wstring_view |
APIs importantes
- modelo de estrutura winrt::delegate
- winrt::hresult_error struct
- winrt::hstring struct
- Espaço de nomes winrt
Tópicos relacionados
Note
Muitos tópicos de C++/WinRT estão em processo de migração da documentação UWP para esta secção. Até a migração estar concluída, os links na lista abaixo podem conduzir à secção de documentação do UWP. A projeção da linguagem C++/WinRT é a mesma para aplicações UWP e WinUI 3, pelo que o conteúdo é aplicável em ambos os contextos. Quaisquer padrões específicos do UWP (como APIs do ciclo de vida da aplicação ou Windows.UI namespace) são explicitamente indicados nesses artigos.
- C++/CX
- Eventos de autor em C++/WinRT
- Concorrência e operações assíncronas com C++/WinRT
- Consumir APIs com C++/WinRT
- Gerir eventos usando delegados em C++/WinRT
- Interoperação entre C++/WinRT e C++/CX
- Assíncronia e interoperabilidade entre C++/WinRT e C++/CX
- Referência à Linguagem de Definição da Interface Microsoft 3.0
- Mudança para C++/WinRT a partir do WRL
- Gestão de strings em C++/WinRT
Windows developer