Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Este tópico é o primeiro de uma série que descreve como você pode portar o código-fonte em seu projeto C++/CX para seu equivalente em C++/WinRT.
Se o seu projeto também estiver usando tipos da Windows Runtime C++ Template Library (WRL), consulte Migrar de WRL para C++/WinRT.
Estratégias para portar
Vale destacar que a portabilidade de C++/CX para C++/WinRT é geralmente simples, com a única exceção da migração de tarefas PPL (Biblioteca de Padrões Paralelos) para corrotinas. Os modelos são diferentes. Não há um mapeamento um a um natural de tarefas PPL para corrotinas e não há uma forma simples de portar mecanicamente o código que funciona para todos os casos. Para obter ajuda com esse aspecto específico da migração e com as opções de interoperabilidade entre os dois modelos, consulte Assincronia e interoperabilidade entre C++/WinRT e C++/CX.
As equipes de desenvolvimento normalmente relatam que, depois que elas concluem a etapa de portabilidade do código assíncrono, o restante do trabalho de portabilidade é, em grande parte, mecânico.
Portabilidade em uma passagem
Se você estiver em condições de portar todo o projeto de uma só vez, precisará apenas deste tópico para obter as informações de que precisa (e não precisará dos tópicos sobre interoperabilidade que vêm a seguir). Recomendamos que você comece criando um novo projeto em Visual Studio usando um dos modelos de projeto do C++/WinRT (consulte Visual Studio suporte para C++/WinRT). Em seguida, mova seus arquivos de código-fonte para esse novo projeto e porte todo o código-fonte C++/CX para C++/WinRT enquanto você faz isso.
Como alternativa, se você preferir fazer o trabalho de portabilidade em seu projeto C++/CX existente, precisará adicionar suporte a C++/WinRT a ele. As etapas que você segue para fazer isso são descritas na tomada de um projeto C++/CX e na adição de suporte ao C++/WinRT. Quando terminar de fazer a portabilidade, você terá transformado o que era um projeto C++/CX puro em um projeto C++/WinRT puro.
Note
Se você tiver um projeto de componente do Windows Runtime, a migração de uma só vez será sua única opção. Um projeto de componente Windows Runtime escrito em C++ deve conter todo o código-fonte C++/CX ou todo o código-fonte C++/WinRT. Eles não podem coexistir nesse tipo de projeto.
Migrando um projeto gradualmente
Com exceção de projetos de componente Windows Runtime, conforme mencionado na seção anterior, se o tamanho ou a complexidade da base de código for necessário para portar seu projeto gradualmente, você precisará de um processo de portabilidade no qual, por um tempo, o código C++/CX e C++/WinRT exista lado a lado no mesmo projeto. Além de ler este tópico, consulte também Interoperabilidade entre C++/WinRT e C++/CX e Assíncrona e interoperabilidade entre C++/WinRT e C++/CX. Esses tópicos fornecem informações e exemplos de código mostrando como interoperar entre as duas projeções de idioma.
Para preparar um projeto para um processo de portabilidade gradual, uma opção é adicionar suporte do C++/WinRT ao seu projeto C++/CX. As etapas que você segue para fazer isso são descritas na tomada de um projeto C++/CX e na adição de suporte ao C++/WinRT. Em seguida, você pode portar gradualmente de lá.
Outra opção é criar um novo projeto em Visual Studio usando um dos modelos de projeto do C++/WinRT (consulte Visual Studio suporte para C++/WinRT). Em seguida, adicione suporte a C++/CX a esse projeto. As etapas que você deve seguir para fazer isso estão descritas em Partindo de um projeto C++/WinRT e adicionando suporte a C++/CX. Em seguida, você pode começar a mover seu código-fonte para isso e portar parte do código-fonte C++/CX para C++/WinRT enquanto faz isso.
Em ambos os casos, você vai interoperar (as duas formas) entre o código do C++/WinRT e qualquer do C++/CX que você ainda não tiver portado.
Note
Tanto o C++/CX quanto o SDK do Windows declaram tipos no namespace raiz Windows. Um tipo de Windows projetado em C++/WinRT tem o mesmo nome totalmente qualificado que o tipo de Windows, mas é colocado no namespace do winrt do C++. Esses namespaces distintos permitem que você faça a portabilidade de C++/CX para C++/WinRT em seu próprio ritmo.
Migrando um projeto XAML gradualmente
Importante
Para um projeto que usa XAML, a qualquer momento todos os tipos de página XAML precisam ser totalmente C++/CX ou totalmente C++/WinRT. Você ainda pode misturar o C++/CX e o C++/WinRT fora dos tipos de página XAML dentro do mesmo projeto (em seus modelos e viewmodels e em outro lugar).
Para esse cenário, o fluxo de trabalho que recomendamos é criar um novo projeto C++/WinRT e copiar o código-fonte e a marcação do projeto C++/CX. Contanto que todos os tipos de página XAML sejam C++/WinRT, você pode adicionar novas páginas XAML com Project>Add Novo Item...>Visual C++>Página em branco (C++/WinRT).
Como alternativa, é possível usar um WRC (componente do Windows Runtime) para fatorar o código do projeto XAML C++/CX à medida que você faz a portabilidade.
- Você pode criar um novo projeto C++/CX WRC, mover o máximo de código C++/CX possível para esse projeto e, em seguida, alterar o projeto XAML para C++/WinRT.
- Ou você pode criar um novo projeto C++/WinRT WRC, deixar o projeto XAML como C++/CX e começar a portar C++/CX para C++/WinRT e mover o código resultante para fora do projeto XAML e para o projeto de componente.
- Você também pode ter um projeto de componente C++/CX ao lado de um projeto de componente C++/WinRT na mesma solução, fazer referência a ambos do seu projeto de aplicativo e, gradualmente, portar de um para o outro. Novamente, consulte Interoperabilidade entre C++/WinRT e C++/CX para obter mais detalhes sobre como usar as duas projeções de linguagem no mesmo projeto.
Primeiras etapas na portabilidade de um projeto C++/CX para C++/WinRT
Não importa qual será sua estratégia de portabilidade (portabilidade em uma passagem ou portabilidade gradual), sua primeira etapa é preparar seu projeto para portabilidade. Aqui está uma recapitulação do que descrevemos em Estratégias para portabilidade em termos do tipo de projeto com o qual você começará e como configurá-lo.
- Portabilidade em uma passagem. Crie um novo projeto em Visual Studio usando um dos modelos de projeto do C++/WinRT. Mova os arquivos do projeto C++/CX para esse novo projeto e porte o código-fonte C++/CX.
- Portando um projeto não XAML gradualmente. Você pode optar por adicionar suporte do C++/WinRT ao seu projeto C++/CX (consulte Tomando um projeto C++/CX e adicionando suporte a C++/WinRT) e portar gradualmente. Ou você pode optar por criar um novo projeto C++/WinRT e adicionar suporte C++/CX a isso (consulte Tomando um projeto C++/WinRT e adicionando suporte a C++/CX), mover arquivos e portar gradualmente.
- Portando um projeto XAML gradualmente. Crie um projeto C++/WinRT, mova os arquivos e a porte gradualmente. A qualquer momento, os tipos de página XAML devem estar inteiramente em C++/WinRT ou inteiramente em C++/CX.
O restante deste tópico se aplica independentemente da estratégia de portabilidade escolhida. Ele contém um catálogo de detalhes técnicos envolvidos na portabilidade do código-fonte de C++/CX para C++/WinRT. Se você estiver portando gradualmente, convém conferir Interoperabilidade entre C++/WinRT e C++/CX e Assincronia e interoperabilidade entre C++/WinRT e C++/CX.
Convenções de nomenclatura de arquivos
Arquivos de marcação XAML
| Origem do arquivo | C++/CX | C++/WinRT |
|---|---|---|
| Arquivos XAML do desenvolvedor | MyPage.xaml MyPage.xaml.h MyPage.xaml.cpp |
MyPage.xaml MyPage.h MyPage.cpp MyPage.idl (veja abaixo) |
| Arquivos XAML gerados | MyPage.xaml.g.h MyPage.xaml.g.hpp |
MyPage.xaml.g.h MyPage.xaml.g.hpp MyPage.g.h |
Observe que C++/WinRT remove o .xaml dos nomes de arquivo *.h e *.cpp.
C++/WinRT adiciona um arquivo de desenvolvedor adicional, o arquivo Midl (.idl). O C++/CX gera esse arquivo automaticamente internamente, adicionando a ele todos os membros públicos e protegidos. No C++/WinRT, você mesmo adiciona e cria o arquivo. Para obter mais detalhes, exemplos de código e um passo a passo da criação de IDL, consulte controles XAML; associar a uma propriedade C++/WinRT.
Veja também Fatoração de classes de runtime em arquivos MIDL (.idl)
Classes de runtime
O C++/CX não impõe restrições aos nomes dos arquivos de cabeçalho; É comum colocar várias definições de classe de runtime em um único arquivo de cabeçalho, especialmente para classes pequenas. Mas O C++/WinRT requer que cada classe de runtime tenha seu próprio arquivo 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 arquivos de cabeçalho de nome diferente para controles personalizados XAML. Você precisará renomear esse arquivo 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 de arquivo de cabeçalho
O C++/CX não exige que você inclua nenhum arquivo de cabeçalho especial, pois ele gera automaticamente arquivos de cabeçalho de .winmd arquivos internamente. No C++/CX, é comum usar diretivas using para namespaces consumidos por nome.
using namespace Windows::Media::Playback;
String^ NameOfFirstVideoTrack(MediaPlaybackItem^ item)
{
return item->VideoTracks->GetAt(0)->Name;
}
A using namespace Windows::Media::Playback diretiva nos permite escrever MediaPlaybackItem sem um prefixo de namespace. Também abordamos o Windows.Media.Core namespace, porque item->VideoTracks->GetAt(0) retorna um Windows.Media.Core.VideoTrack. Mas não tínhamos que digitar o nome VideoTrack em lugar nenhum, então não precisávamos de uma using Windows.Media.Core diretiva.
Mas o C++/WinRT exige que você inclua um arquivo de cabeçalho correspondente a cada namespace consumido, mesmo que você não o nomeie.
#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, embora o evento MediaPlaybackItem.AudioTracksChanged seja do tipo TypedEventHandler<MediaPlaybackItem, Windows. Foundation.Collections.IVectorChangedEventArgs>, não precisamos incluir winrt/Windows.Foundation.Collections.h porque não usamos esse evento.
O C++/WinRT também exige que você inclua arquivos de cabeçalho para os namespaces que são consumidos pela marcação XAML.
<!-- MainPage.xaml -->
<Rectangle Height="400"/>
Usar a classe Rectangle significa que você precisa adicionar essa inclusão.
// MainPage.h
#include <winrt/Microsoft.UI.Xaml.Shapes.h>
Se você esquecer um arquivo de cabeçalho, tudo será compilado bem, mas você receberá erros do vinculador porque as consume_ classes estão ausentes.
Passagem de parâmetro
Ao escrever o código-fonte do C++/CX, você passa os tipos C++/CX como parâmetros de função como referência de circunflexo (^).
void LogPresenceRecord(PresenceRecord^ record);
No C++/WinRT, para funções síncronas, você deve usar const& parâmetros por padrão. Isso evita cópia e sobrecarga encaixada. Entretanto, as corrotinas precisam usar passar-por-valor para garantir a captura pelo valor e evitar problemas de tempo de vida (para ver mais detalhes, confira Operações assíncronas e simultaneidade com C++/WinRT).
void LogPresenceRecord(PresenceRecord const& record);
IASyncAction LogPresenceRecordAsync(PresenceRecord const record);
Um objeto C++/WinRT é fundamentalmente um valor que mantém um ponteiro da interface para o objeto do Windows Runtime subjacente. Quando você copia um objeto C++/WinRT, o compilador copia o ponteiro da interface encapsulado, aumentando sua contagem de referência. A consequente destruição da cópia envolve reduzir a contagem de referência. Portanto, apenas incorre na sobrecarga de uma cópia quando necessário.
Referências de variáveis e campos
Ao escrever código-fonte em C++/CX, você usa variáveis hat (^) para referenciar objetos do Windows Runtime e o operador de seta (->) para desreferenciar uma variável hat.
IVectorView<User^>^ userList = User::Users;
if (userList != nullptr)
{
for (UINT32 iUser = 0; iUser < userList->Size; ++iUser)
...
Ao fazer a portabilidade para o código C++/WinRT equivalente, você gastar um bom tempo removendo os circunflexos e alterando o operador seta (->) para o operador ponto (.). Os tipos projetados por C++/WinRT são valores e não ponteiros.
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 ângulo de visão do C++/CX inicializa-o como nulo. Aqui está um exemplo de código C++/CX no qual criamos uma variável/campo do tipo correto, mas um que não é inicializado. Em outras palavras, ele não se refere inicialmente a um TextBlock; pretendemos atribuir uma referência posteriormente.
TextBlock^ textBlock;
class MyClass
{
TextBlock^ textBlock;
};
Para obter o equivalente em C++/WinRT, consulte Inicialização atrasada.
Properties
As extensões de linguagem C++/CX incluem o conceito de propriedades. Ao escrever o código-fonte C++/CX, você pode acessar uma propriedade como se fosse um campo. O C++ padrão não tem o conceito de propriedade, por isso, em C++/WinRT, você chama as funções get e set.
Nos exemplos a seguir, XboxUserId, UserState, PresenceDeviceRecords e Size são todas propriedades.
Recuperar o valor de uma propriedade
Veja como obter um valor de 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 C++/WinRT equivalente 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();
}
Observe que a função PresenceDeviceRecords retorna um objeto Windows Runtime que tem uma função Size. Como o objeto retornado também é um tipo projetado em C++/WinRT, desreferenciamos usando o operador de ponto para chamar Tamanho.
Definir uma propriedade como um novo valor
Definir uma propriedade como um novo valor segue um padrão semelhante. Primeiro, em C++/CX.
record->UserState = newValue;
Para fazer o equivalente em C++/WinRT, chame uma função com o mesmo nome da propriedade e passe um argumento.
record.UserState(newValue);
Criar uma instância de uma classe
Trabalhe com o objeto do C++/CX por meio de um identificador, conhecido como uma referência de circunflexo (^). Você cria um novo objeto por meio da ref new palavra-chave, que, por sua vez, chama 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 do C++/WinRT é um valor; portanto, você pode alocá-lo na pilha ou como um campo de um objeto. Você nunca usa ref new (nem new) para alocar um objeto C++/WinRT. Nos bastidores, RoActivateInstance ainda está sendo chamado.
using namespace winrt::Windows::Storage::Streams;
struct Sample
{
private:
Buffer m_gamerPicBuffer{ MAX_IMAGE_SIZE };
};
Se um recurso for caro para inicializar, é comum atrasar a inicialização dele até que ele seja realmente necessário. Como já mencionado, o construtor padrão para uma referência de ângulo de visão do C++/CX inicializa-o como nulo.
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. Observe o uso do construtor std::nullptr_t . Para obter mais informações sobre esse construtor, consulte 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 coleções
Os tipos de coleção C++ usam o construtor padrão, o que pode resultar na construção de objeto não intencional.
| 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 de 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); |
| Definir um valor em um 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); |
| Matriz de referências vazias | TextBox^ boxes[2]; |
// Creates 2 TextBox objects!TextBox boxes[2]; |
TextBox boxes[2] = { nullptr, nullptr }; |
| Par | std::pair<TextBox^, String^> p; |
// Creates a TextBox!std::pair<TextBox, String> p; |
std::pair<TextBox, String> p{ nullptr, nullptr }; |
Mais informações sobre coleções de referências vazias
Sempre que você tiver um Platform::Array^ (consulte Port Platform::Array^) em C++/CX, você terá a opção de convertê-lo em um std::vector no C++/WinRT (na verdade, qualquer contêiner contíguo), em vez de deixá-lo como um array. Há vantagens em escolher std::vector.
Por exemplo, embora haja uma abreviação para criar um vetor de tamanho fixo de referências vazias (consulte a tabela acima), não há essa abreviação para criar uma matriz de referências vazias. Você precisa repetir nullptr para cada elemento em uma matriz. Se você tiver poucos, os extras serão construídos por padrão.
Para um vetor, você pode preenchê-lo com referências vazias na inicialização (como na tabela acima) ou 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 informações sobre o exemplo std::map
O operador subscrito [] para std::map se comporta desta forma.
- Se a chave for encontrada no mapa, retorne uma referência ao valor existente (que você pode sobrescrever).
- Se a chave não for encontrada no mapa, crie uma nova entrada no mapa que consiste na chave (movida, se móvel) e um valor construído por padrão e retorne uma referência ao valor (que você pode substituir).
Em outras palavras, o [] operador sempre cria uma entrada no mapa. Isso é diferente de C#, Java e JavaScript.
Convertendo de uma classe de runtime base em uma derivada
É comum ter uma referência à base que você sabe que se refere a um objeto de um tipo derivado. No C++/CX, use dynamic_cast para converter a referência à base em uma referência para derivado. O dynamic_cast é realmente apenas uma chamada oculta para QueryInterface. Aqui está um exemplo típico em que você está manipulando um evento de alteração de propriedade de dependência e deseja converter DependencyObject de volta para o tipo real que tem a propriedade da 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 C++/WinRT equivalente substitui dynamic_cast por uma chamada à função IUnknown::try_as, que encapsula QueryInterface. Você também tem a opção de chamar IUnknown::as, em vez disso, que gera uma exceção se a consulta para a interface necessária (a interface padrão do tipo que você está solicitando) 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 tempo de execução, a classe base deve ser componível. O C++/CX não exige que você execute nenhuma etapa especial para tornar suas classes composíveis, mas o C++/WinRT o faz. Use a palavra-chave não selada para indicar que deseja que sua classe seja utilizável como uma classe base.
unsealed runtimeclass BasePage : Microsoft.UI.Xaml.Controls.Page
{
...
}
runtimeclass DerivedPage : BasePage
{
...
}
Na classe de cabeçalho de implementação, você deve incluir o arquivo de cabeçalho de classe base antes de incluir o cabeçalho gerado automaticamente para a classe derivada. Caso contrário, você receberá erros como "Uso ilegal desse tipo como uma 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>
{
...
}
}
Processamento de eventos com um delegado
Aqui está um exemplo típico de manipulação de 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.
});
Esse é o 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, você pode optar por implementar o delegado como uma função livre como uma função de ponteiro para membro. Para obter mais informações, consulte Manipular eventos usando delegados em C++/WinRT.
Se você estiver migrando de uma base de código em C++/CX na qual eventos e delegados são usados internamente (não entre binários), então winrt::delegate ajudará você a reproduzir esse padrão em C++/WinRT. Veja também delegados parametrizados, sinais simples e callbacks dentro de um projeto.
Revogando um delegado
No C++/CX, você usa o -= operador para revogar um registro de evento anterior.
myButton->Click -= token;
Esse é o equivalente em C++/WinRT.
myButton().Click(token);
Para obter mais informações e opções, consulte Revogar um delegado registrado.
Conversão boxing e unboxing
C++/CX encapsula automaticamente valores escalares em objetos. O C++/WinRT exige que você chame a função winrt::box_value explicitamente. Ambas as linguagens exigem que você desencaixote explicitamente. Consulte Boxing e unboxing com C++/WinRT.
Nas tabelas a seguir, usaremos essas 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"); |
| Conversão unboxing | i = (int)o;s = (String^)o; |
i = unbox_value<int>(o);s = unbox_value<winrt::hstring>(o); |
O C++/CX e o C# gerarão exceções se você tentar fazer a conversão unboxing de um ponteiro nulo em um tipo de valor. O C++/WinRT considera isso um erro de programação e falha. No C++/WinRT, use a função winrt::unbox_value_or se quiser lidar com o caso em que o objeto não é do tipo que você pensou que era.
| Scenario | C++/CX | C++/WinRT |
|---|---|---|
| Fazer a conversão unboxing de um 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 convertido | Platform::InvalidCastException |
Colisão |
| Fazer a conversão unboxing de int, se for nulo, usar fallback; falhar, em qualquer outra situação | i = o ? (int)o : fallback; |
i = o ? unbox_value<int>(o) : fallback; |
| Fazer a conversão unboxing de int, se possível; usar fallback para qualquer outra situação | auto box = dynamic_cast<IBox<int>^>(o);i = box ? box->Value : fallback; |
i = unbox_value_or<int>(o, fallback); |
Conversão boxing e unboxing de uma cadeia de caracteres
Uma cadeia de caracteres é, de certa forma, um tipo de valor e, de outras maneiras, um tipo de referência. C++/CX e C++/WinRT tratam as cadeias de caracteres de forma diferente.
O tipo ABI HSTRING é um ponteiro para uma cadeia de caracteres com contagem de referências. Mas não deriva de IInspectable, portanto, tecnicamente não é um objeto. Além disso, um HSTRING nulo representa a cadeia de caracteres vazia. A conversão boxing de itens não derivados de IInspectable é feita encapsulando-os dentro de um IReference<T> e o Windows Runtime fornece uma implementação padrão na forma do objeto PropertyValue (os tipos personalizados são relatados como PropertyType::OtherType).
C++/CX representa uma cadeia de caracteres Windows Runtime como um tipo de referência; enquanto C++/WinRT projeta uma cadeia de caracteres como um tipo de valor. Isso significa que uma cadeia de caracteres nula em caixa pode ter representações diferentes dependendo de como você chegou lá.
Além disso, C++/CX permite que você desreferenciar uma cadeia de caracteres nula ^, nesse caso, ela se comporta como a cadeia de caracteres "".
| Behavior | C++/CX | C++/WinRT |
|---|---|---|
| Declarações | Object^ o;String^ s; |
IInspectable o;hstring s; |
| Categoria de tipo de cadeia de caracteres | Tipo de referência | Tipo de valor |
| null HSTRING é projetado como | (String^)nullptr |
hstring{} |
São nulos e "" idênticos? |
Yes | Yes |
| Validade do valor nulo | s = nullptr;s->Length == 0 (válido) |
s = hstring{};s.size() == 0 (válido) |
| Se você atribuir cadeia de caracteres nula ao objeto | o = (String^)nullptr;o == nullptr |
o = box_value(hstring{});o != nullptr |
Se você atribuir "" ao objeto |
o = "";o == nullptr |
o = box_value(hstring{L""});o != nullptr |
Conversões boxing e unboxing básicas.
| Operation | C++/CX | C++/WinRT |
|---|---|---|
| Encapsular uma cadeia de caracteres | o = s;A cadeia de caracteres vazia torna-se nullptr. |
o = box_value(s);A cadeia de caracteres vazia torna-se um objeto não nulo. |
| Fazer a conversão unboxing de uma cadeia de caracteres conhecida | s = (String^)o;O objeto nulo torna-se uma cadeia de caracteres vazia. InvalidCastException se não for uma cadeia de caracteres. |
s = unbox_value<hstring>(o);O objeto nulo falha. Falha se não for uma cadeia de caracteres. |
| Fazer a conversão unboxing de uma possível cadeia de caracteres | s = dynamic_cast<String^>(o);Objeto nulo ou que não seja string torna-se uma string vazia. |
s = unbox_value_or<hstring>(o, fallback);Nulo ou não cadeia de caracteres se torna fallback. Cadeia de caracteres vazia preservada. |
Simultaneidade e operações assíncronas
A PPL (Biblioteca de Padrões Paralelos) (concurrency::task, por exemplo) foi atualizada para dar suporte às referências de ângulo de visão do C++/CX.
Para o C++/WinRT, você deverá usar corrotinas e co_await. Para obter mais informações e exemplos de código, consulte Simultaneidade e operações assíncronas com C++/WinRT.
Como consumir objetos por meio da marcação XAML
Em um projeto C++/CX, você pode consumir membros privados e elementos nomeados da marcação XAML. Mas, no C++/WinRT, todas as entidades consumidas usando a extensão de marcação XAML {x:Bind} devem ser expostas publicamente no IDL.
Além disso, a associação a um valor booliano exibe true ou false em C++/CX, mas exibe Windows.Foundation.IReference`1<Boolean> em C++/WinRT.
Para obter mais informações e exemplos de código, consulte Consuming objects from markup.
Mapeando tipos C++/CX Platform para tipos C++/WinRT
O C++/CX fornece vários tipos de dados no namespace da plataforma . Esses tipos não são padrão do C++, portanto você só pode usá-los quando habilita as extensões de linguagem do Windows Runtime (propriedade do projeto no Visual Studio C/C++>Geral>Consumir Extensão do Windows Runtime>Sim (/ZW)). A tabela a seguir ajuda você a migrar dos tipos Platform para seus equivalentes em C++/WinRT. Depois de fazer isso, já que C++/WinRT é padrão C++, você pode desativar a opção /ZW .
| C++/CX | C++/WinRT |
|---|---|
| Platform::Agile^ | winrt::agile_ref |
| Platform::Array^ | Consulte 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^ no C++/CX representa uma classe Windows Runtime que pode ser acessada de qualquer thread. O equivalente C++/WinRT é winrt::agile_ref.
Em C++/CX.
Platform::Agile<Windows::UI::Core::CoreWindow> m_window;
No 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 você use uma matriz, o C++/WinRT permite que você use qualquer contêiner contíguo. Veja como o construtor padrão afeta coleções por um motivo pelo qual std::vector é uma boa opção.
Portanto, sempre que você tiver um Platform::Array^ em C++/CX, suas opções de migração incluem usar uma lista de inicializadores, um std::array ou um std::vector. Para obter mais informações e exemplos de código, consulte listas de inicializadores Padrão e matrizes e vetores Standard.
Migrar Platform::Exception^ para winrt::hresult_error
O tipo Platform::Exception^ é produzido no C++/CX quando uma API Windows Runtime retorna um HRESULT não S_OK. O equivalente C++/WinRT é winrt::hresult_error.
Para portar para C++/WinRT, altere todo o código que usa 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 essas 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 |
Observe que cada classe (por meio da classe base hresult_error ) fornece uma função to_abi , que retorna o HRESULT do erro e uma função de mensagem , que retorna a representação de cadeia de caracteres desse HRESULT.
Aqui está um exemplo de como gerar 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" };
Portar Platform::Object^ para winrt::Windows::Foundation::IInspectable
Como todos os tipos C++/WinRT, winrt::Windows::Foundation::IInspectable é um tipo de valor. Veja como inicializar uma variável desse tipo como nula.
winrt::Windows::Foundation::IInspectable var{ nullptr };
Migrar Platform::String^ para winrt::hstring
Platform::String^ é equivalente ao tipo Windows Runtime HSTRING ABI. Para C++/WinRT, o equivalente é winrt::hstring. Porém, com o C++/WinRT, você pode chamar as APIs do Windows Runtime usando tipos de cadeia de caracteres longas da Biblioteca Padrão do C++, como std::wstring, e/ou literais de cadeias de caracteres longas. Para obter mais detalhes e exemplos de código, consulte Tratamento de cadeia de caracteres em C++/WinRT.
Com C++/CX, você pode acessar a propriedade Platform::String::D ata para recuperar a cadeia de caracteres como uma matriz const wchar_t* no estilo C (por exemplo, para passá-la para std::wcout).
auto var{ titleRecord->TitleName->Data() };
Para fazer o mesmo com C++/WinRT, você pode usar a função hstring::c_str para obter uma versão de cadeia de caracteres de estilo C terminada em nulo, da mesma forma que você pode em std::wstring.
auto var{ titleRecord.TitleName().c_str() };
Quando se trata de implementar APIs que usam ou retornam cadeias de caracteres, você normalmente altera qualquer código C++/CX que usa Platform::String^ para usar winrt::hstring .
Aqui está um exemplo de uma API C++/CX que usa uma cadeia de caracteres.
void LogWrapLine(Platform::String^ str);
Para C++/WinRT, você pode declarar essa API em MIDL 3.0 assim.
// LogType.idl
void LogWrapLine(String str);
O conjunto de ferramentas do C++/WinRT gerará então um código-fonte para você semelhante 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 disponibiliza esse recurso diretamente, mas você pode recorrer a alternativas.
int i{ 2 };
auto s{ std::to_wstring(i) }; // s is a std::wstring with value L"2".
O C++/WinRT também dá suporte a winrt::to_hstring para um número limitado de tipos. Você precisará adicionar sobrecargas para os tipos adicionais que deseja converter em cadeia de caracteres.
| Linguagem | Converter int em string | Converter enumeração em string |
|---|---|---|
| C++/CX | String^ result = "hello, " + intValue.ToString(); |
String^ result = "status: " + status.ToString(); |
| C++/WinRT | hstring result = L"hello, " + to_hstring(intValue); |
// must define overload (see below)hstring result = L"status: " + to_hstring(status); |
No caso de converter um enum em cadeia de caracteres, você precisará 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));
}
}
}
Em geral, essas conversões em cadeia de caracteres são consumidas implicitamente pela vinculação de dados.
<TextBlock>
You have <Run Text="{Binding FlowerCount}"/> flowers.
</TextBlock>
<TextBlock>
Most recent status is <Run Text="{x:Bind LatestOperation.Status}"/>.
</TextBlock>
Essas associações executarão winrt::to_hstring da propriedade associada. No caso do segundo exemplo (o StatusEnum), você deve fornecer sua própria sobrecarga de winrt::to_hstring, caso contrário, você receberá um erro do compilador.
Construção de strings
C++/CX e C++/WinRT recorrem ao std::wstringstream padrão para a construção de strings.
| Operation | C++/CX | C++/WinRT |
|---|---|---|
| Acrescentar cadeia de caracteres, mantendo os valores nulos | stream.print(s->Data(), s->Length); |
stream << std::wstring_view{ s }; |
| Anexar cadeia de caracteres, parar no primeiro caractere 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 C++/CX pode construir uma Plataforma::Cadeia de Caracteres de uma cadeia de caracteres de 8 bits, C++/WinRT não faz isso.
| Operation | C++/CX | C++/WinRT |
|---|---|---|
| Construir uma cadeia de caracteres com base no 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 caracteres 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 |
| Fazer a conversão 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 em std::wstring, preservando 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, interromper no primeiro caractere nulo | std::wstring ws{ s->Data() };ws = s->Data(); |
std::wstring ws{ s.c_str() };ws = s.c_str(); |
| Passar o literal para o método | Method("hello");Method(L"hello"); |
// Method("hello"); // Doesn't compileMethod(L"hello"); |
| Passe std::wstring para o 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
- namespace winrt
Tópicos relacionados:
Note
Muitos tópicos do C++/WinRT estão em processo de migração da documentação da UWP para esta seção. Até que a migração seja concluída, os links na lista abaixo podem levar à seção de documentos UWP. A projeção da linguagem C++/WinRT é a mesma para aplicativos UWP e WinUI 3, de modo que o conteúdo seja aplicável em ambos os contextos. Quaisquer padrões específicos da UWP (como o ciclo de vida do aplicativo ou APIs do namespace Windows.UI) são explicitamente indicados nesses artigos.
- C++/CX
- Eventos de autor em C++/WinRT
- Operações simultâneas e assíncronas com C++/WinRT
- Consumir APIs com C++/WinRT
- Manipular eventos usando delegados no C++/WinRT
- Interoperabilidade entre C++/WinRT e C++/CX
- Assincronia e interoperabilidade entre C++/WinRT e C++/CX
- Referência da linguagem de definição de interface de Microsoft 3.0
- Migrar do WRL para o C++/WinRT
- Manipulação de cadeia de caracteres em C++/WinRT
Windows developer