Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Este tópico é o primeiro de uma série que descreve como você pode portar o código-fonte em seu projeto
Se o seu projeto também estiver usando tipos de WRL (Biblioteca de Modelos C++) do Tempo de Execução do Windows, consulte Mover para C++/WinRT do WRL.
Estratégias de portabilidade
Vale a pena saber que a portabilidade de C++/CX para C++/WinRT é geralmente simples, com a única exceção de mudar de Parallel Patterns Library (PPL) tarefas para co-rotinas. Os modelos são diferentes. Não há um mapeamento um-para-um natural de tarefas PPL para co-rotinas, e não há uma maneira simples de portar mecanicamente o código que funciona para todos os casos. Para obter ajuda com este aspeto específico da migração e as opções de interoperação entre os dois modelos, consulte Assincronia e interoperabilidade entre C++/WinRT e C++/CX.
As equipas de desenvolvimento reportam de forma rotineira que, uma vez superado o obstáculo de migrar o seu código assíncrono, o restante do trabalho de transposição é, em grande parte, mecânico.
Portabilidade em uma única passagem
Se estiveres em posição de portar todo o teu projeto de uma só vez, precisarás apenas deste tópico para as informações de que necessitas (e não precisarás dos tópicos de interop que se seguem a este). Recomendamos que você comece criando um novo projeto no Visual Studio usando um dos modelos de projeto C++/WinRT (consulte suporte do Visual Studio 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 à medida que fizer 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 em Tomando um projeto C++/CX e adicionando suporte a C++/WinRT. No momento em que terminar a conversão, terá transformado o que era um projeto C++/CX puro em um projeto C++/WinRT puro.
Observação
Se tiver um projeto de componente do Windows Runtime, então a conversão de uma só vez é a sua única opção. Um projeto de componente do Tempo de Execução do Windows 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 neste tipo de projeto.
Portabilidade gradual de um projeto
Com exceção dos projetos de componentes do Tempo de Execução do Windows, conforme mencionado na seção anterior, se o tamanho ou a complexidade da sua base de código tornar necessário 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 Assincronia 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 linguagem.
Para preparar um projeto para um processo de portabilidade gradual, uma opção é adicionar suporte a C++/WinRT ao seu projeto C++/CX. As etapas que você segue para fazer isso são descritas em Tomando um projeto C++/CX e adicionando suporte a C++/WinRT. Você pode então migrar gradualmente a partir daí.
Outra opção é criar um novo projeto no Visual Studio usando um dos modelos de projeto C++/WinRT (consulte suporte do Visual Studio para C++/WinRT). E, em seguida, adicione suporte a C++/CX a esse projeto. As etapas que você segue para fazer isso são descritas em Tomando um projeto C++/WinRT e adicionando suporte a C++/CX. Em seguida, pode começar a mover o seu código-fonte para isso e portar alguns do código-fonte C++/CX para C++/WinRT à medida que prossegue.
Em ambos os casos, você interoperará (nos dois sentidos) entre seu código C++/WinRT e qualquer código C++/CX que ainda não tenha portado.
Observação
Tanto 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 Windows, mas é colocado no namespace C++ winrt. Esses namespaces distintos permitem que você faça a portabilidade de C++/CX para C++/WinRT no seu próprio ritmo.
Portabilidade gradual de um projeto XAML
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 C++/CX e C++/WinRT fora dos tipos de página XAML dentro do mesmo projeto (em seus modelos e viewmodels, e em outros lugares).
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>Adicionar Novo Item...>Página em Branco do Visual C++>(C++/WinRT).
Como alternativa, pode usar um componente Runtime do Windows (WRC) para extrair o código do projeto XAML C++/CX ao portá-lo.
- 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 WRC C++/WinRT, 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 componente.
- Você também pode ter um projeto de componente C++/CX ao lado de um projeto de componente C++/WinRT dentro da 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), seu primeiro passo é preparar seu projeto para portabilidade. Aqui está uma recapitulação do que descrevemos em Estratégias para portar em termos do tipo de projeto com o qual você começará e como configurá-lo.
- Portabilidade numa única passagem. Crie um novo projeto no Visual Studio usando um dos modelos de projeto C++/WinRT. Mova os arquivos do seu projeto C++/CX para esse novo projeto e porte o código-fonte C++/CX.
- Portar um projeto não XAML de forma gradual. Você pode optar por adicionar suporte a C++/WinRT ao seu projeto C++/CX (consulte Pegando 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 a C++/CX a isso (consulte Pegando um projeto C++/WinRT e adicionando suporte a C++/CX), mover arquivos e portar gradualmente.
- Portar gradualmente um projeto XAML. Crie um novo projeto C++/WinRT, mova arquivos e faça a portabilidade gradualmente. A qualquer momento, seus tipos de página XAML devem ser todos os C++/WinRT ou todos os 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, provavelmente também desejará ver 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 ficheiro | C++/CX | C++/WinRT |
|---|---|---|
| Arquivos XAML de desenvolvedor | MyPage.xaml MyPage.xaml.h MyPage.xaml.cpp |
MyPage.xaml Minha Página.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 MinhaPage.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). C++/CX gera automaticamente esse arquivo internamente, adicionando a ele todos os membros públicos e protegidos. Em 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; vincular a uma propriedade C++/WinRT.
Consulte também Factoring de classes de tempo de execução em ficheiros Midl (.idl)
Classes de tempo de execução
O C++/CX não impõe restrições aos nomes dos seus ficheiros de cabeçalho; É comum colocar várias definições de classe de tempo de execução em um único arquivo de cabeçalho, especialmente para classes pequenas. Mas C++/WinRT requer que cada classe de tempo de execução tenha seu próprio arquivo de cabeçalho nomeado após o nome da classe.
| C++/CX | C++/WinRT |
|---|---|
Comum.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 com nomes diferentes para controles personalizados XAML. Você precisará renomear esses arquivos 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 arquivo de cabeçalho
O C++/CX não exige que você inclua nenhum arquivo de cabeçalho especial, porque ele gera automaticamente internamente arquivos de cabeçalho a partir de arquivos .winmd. É comum em C++/CX usar diretivas using para namespaces que você consome pelo nome.
using namespace Windows::Media::Playback;
String^ NameOfFirstVideoTrack(MediaPlaybackItem^ item)
{
return item->VideoTracks->GetAt(0)->Name;
}
A diretiva using namespace Windows::Media::Playback nos permite escrever MediaPlaybackItem sem um prefixo de namespace. Mexemos também no namespace Windows.Media.Core, porque item->VideoTracks->GetAt(0) retorna um Windows.Media.Core.VideoTrack. Mas não precisávamos digitar o nome VideoTrack em nenhum lugar, então não precisávamos de uma diretiva using Windows.Media.Core.
Mas o C++/WinRT exige que você inclua um arquivo de cabeçalho correspondente a cada namespace que você consome, 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, mesmo que 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 usámos esse evento.
C++/WinRT também requer que você inclua arquivos de cabeçalho para namespaces que são consumidos pela marcação XAML.
<!-- MainPage.xaml -->
<Rectangle Height="400"/>
Usar a classe
// MainPage.h
#include <winrt/Windows.UI.Xaml.Shapes.h>
Se te esqueceres de um ficheiro de cabeçalho, tudo será compilado corretamente, mas irás receber erros de ligação porque faltam as classes consume_.
Passagem de parâmetros
Ao escrever código-fonte C++/CX, passa tipos C++/CX como parâmetros de função através de referências com chapéu (^).
void LogPresenceRecord(PresenceRecord^ record);
Em C++/WinRT, para funções síncronas, você deve usar parâmetros const& por padrão. Isso evitará cópias e sobrecargas interligadas. Mas suas co-rotinas devem usar pass-by-value para garantir que capturam por valor e evitar problemas de tempo de vida (para obter mais detalhes, consulte Simultaneidade 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 contém um ponteiro de interface para o objeto de suporte do Windows Runtime. Ao copiar um objeto C++/WinRT, o compilador copia o ponteiro da interface encapsulada, aumentando a sua contagem de referências. A eventual destruição da cópia implica o decréscimo da contagem de referências. Assim, só incorra na sobrecarga de uma cópia quando necessário.
Referências de variáveis e campos
Ao escrever o código-fonte C++/CX, você usa variáveis hat (^) para fazer referência a objetos do Tempo de Execução do Windows e o operador de seta (->) para cancelar a referência de 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, você pode obter um longo caminho removendo os chapéus e alterando o operador de seta (->) para o operador de ponto (.). Os tipos projetados em 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 hat C++/CX inicializa-a como null. Aqui está um exemplo de código C++/CX no qual criamos uma variável/campo do tipo correto, mas que não foi inicializado. Em outras palavras, ele não se refere inicialmente a um TextBlock, pretendemos atribuir uma referência mais tarde.
TextBlock^ textBlock;
class MyClass
{
TextBlock^ textBlock;
};
Para obter o equivalente em C++/WinRT, consulte Inicialização atrasada.
Propriedades
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 uma propriedade, portanto, em C++/WinRT, você chama as funções get e set.
Nos exemplos a seguir, XboxUserId , UserState, PresenceDeviceRecordse Size são todas propriedades.
Recuperando um 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 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();
}
Observe que a função PresenceDeviceRecords retorna um objeto do Windows Runtime que tem uma função Size. Como o objeto retornado também é um tipo projetado em C++/WinRT, desreferenciamos usando o operador ponto para chamar Size.
Atribuir um novo valor a uma propriedade
Definir uma propriedade para 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);
Criando uma instância de uma classe
Você manipula um objeto C++/CX por meio de um identificador para ele, usualmente conhecido como uma referência com chapéu (^). Você cria um novo objeto por meio da palavra-chave ref new, que, por sua vez, chama RoActivateInstance para ativar uma nova instância da classe runtime.
using namespace Windows::Storage::Streams;
class Sample
{
private:
Buffer^ m_gamerPicBuffer = ref new Buffer(MAX_IMAGE_SIZE);
};
Um objeto C++/WinRT é um valor, de modo que o pode alocar na pilha, ou como um campo de um objeto. Você nunca usar ref new (nem new) para alocar um objeto C++/WinRT. Nos bastidores, RoActivateInstance ainda está a ser 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 do tipo chapéu 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 as coleções
Os tipos de coleção C++ usam o construtor padrão, o que pode resultar em construção de objeto não intencional.
| Cenário | 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); |
| Definir 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); |
| 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 sobre coleções de referências vazias
Sempre que você tiver um Platform::Array^ (consulte Port Platform::Array^) em C++/CX, você tem a opção de portá-lo para um std::vetor em C++/WinRT (na verdade, qualquer contêiner contíguo) em vez de deixá-lo como uma matriz. Há vantagens em escolher std::vetor.
Por exemplo, embora haja uma abreviatura para criar um vetor de tamanho fixo de referências vazias (veja a tabela acima), não há essa abreviação para criar uma matriz de referências vazias. Você tem que repetir nullptr para cada elemento em uma matriz. Se você tiver muito pouco, então 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 você pode preenchê-lo com referências vazias pós-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 std::map exemplo
O operador subscrito [] para std::map se comporta assim.
- Se a chave for encontrada no mapa, retorne uma referência ao valor existente (que você pode substituir).
- Se a chave não for encontrada no mapa, crie uma nova entrada no mapa que consista 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 tempo de execução base para uma derivada
É comum ter uma referência base que sabes que se refere a um objeto de um tipo derivado. Em C++/CX, você usa dynamic_cast para converter referência para base em uma referência para derivada. O dynamic_cast é realmente apenas uma chamada oculta para QueryInterface. Aqui está um exemplo típico: você está lidando com um evento de propriedade de dependência alterada e deseja transmitir de DependencyObject de volta para o tipo real que possui a propriedade de dependência.
void BgLabelControl::OnLabelChanged(Windows::UI::Xaml::DependencyObject^ d, Windows::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 para a função IUnknown::try_as, que encapsula o QueryInterface. Você também tem a opção de chamar IUnknown::as, em vez disso, o que lança 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(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 ...
}
}
Classes derivadas
Para derivar de uma classe de tempo de execução, a classe base deve ser componível. C++/CX não exige que você execute nenhuma etapa especial para tornar suas classes compostáveis, mas C++/WinRT sim. Use a palavra-chave unsealed para indicar que deseja que a sua classe possa ser utilizada como classe base.
unsealed runtimeclass BasePage : Windows.UI.Xaml.Controls.Page
{
...
}
runtimeclass DerivedPage : BasePage
{
...
}
Em sua 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 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>
{
...
}
}
Tratamento 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 um 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.
});
Este é 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 seu delegado como uma função livre ou como uma função de ponteiro para membro. Para saber mais, veja manipular eventos usando delegados em C++/WinRT.
Se estiveres a portares de uma base de código C++/CX onde eventos e delegados são usados internamente (não através de binários), winrt::delegate ajudará a replicar esse padrão em C++/WinRT. Consulte também Delegados parametrizados, sinais simples e retornos de chamada num projeto.
Revogar um delegado
Em C++/CX, você usa o operador -= para revogar um registro de evento anterior.
myButton->Click -= token;
Este é o equivalente em C++/WinRT.
myButton().Click(token);
Para obter mais informações e opções, consulte Revogar um delegado registado.
Boxe e desembalagem
C++/CX encaixota automaticamente escalares em objetos. C++/WinRT requer que você chame a função winrt::box_value explicitamente. Ambos os idiomas exigem que você desencaixote explicitamente. Veja 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; |
| Funcionamento | 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# geram exceções se tentar desencaixotar um ponteiro nulo para um tipo de valor. C++/WinRT considera isso um erro de programação e falha. Em C++/WinRT, use a função winrt::unbox_value_or se quiser manipular o caso em que o objeto não é do tipo que você pensava que era.
| Cenário | C++/CX | C++/WinRT |
|---|---|---|
| Desencaixotar um número inteiro conhecido | i = (int)o; |
i = unbox_value<int>(o); |
| Se o é nulo | Platform::NullReferenceException |
Colisão |
| Se o não for um int embalado | Platform::InvalidCastException |
Colisão |
| Desencaixotar int, usar alternativa se nulo; falhar se outra coisa | i = o ? (int)o : fallback; |
i = o ? unbox_value<int>(o) : fallback; |
| Desencaixotar o 'int' caso seja possível; usar 'fallback' para qualquer outra coisa. | auto box = dynamic_cast<IBox<int>^>(o);i = box ? box->Value : fallback; |
i = unbox_value_or<int>(o, fallback); |
Boxe e unboxing de uma corda
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 cadeias de caracteres de forma diferente.
O tipo ABI HSTRING é um ponteiro para uma cadeia de caracteres contada por referência. Mas não deriva de IInspectable, por isso não é tecnicamente um objeto . Além disso, um HSTRING nulo representa a cadeia de caracteres vazia. O encaixotamento de elementos não derivados de IInspectable é feito encapsulando-os em um IReference<T>, e o Windows Runtime fornece uma implementação padrão através do objeto PropertyValue (tipos personalizados são indicados como PropertyType::OtherType).
C++/CX representa uma cadeia de caracteres do Tempo de Execução do Windows 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 dereferences uma nula String^, nesse caso, ela se comporta como a string "".
| Comportamento | 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 projeta como | (String^)nullptr |
hstring{} |
São null e "" idênticos? |
Sim | Sim |
| Validade de null | 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 |
Boxe básico e unboxing.
| Funcionamento | C++/CX | C++/WinRT |
|---|---|---|
| Encaixar uma cadeia de caracteres | o = s;String vazia torna-se nullptr. |
o = box_value(s);A cadeia de caracteres vazia torna-se objeto não nulo. |
| Desencaixotar 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. Crash se não for uma cadeia de caracteres. |
| Desencaixotar uma possível cadeia de caracteres | s = dynamic_cast<String^>(o);Objeto nulo ou objeto diferente de uma string transforma-se numa cadeia de caracteres vazia. |
s = unbox_value_or<hstring>(o, fallback);Um valor nulo ou não-string transforma-se num valor de substituição. Cadeia vazia preservada. |
Simultaneidade e operações assíncronas
A Biblioteca de Padrões Paralelos (PPL) (simultaneidade::task, por exemplo) foi atualizada para suportar referências de chapéu C++/CX.
Para C++/WinRT, você deve usar co-rotinas e co_await em vez disso. Para obter mais informações e exemplos de código, consulte Simultaneidade e operações assíncronas com C++/WinRT.
Consumindo objetos da marcação XAML
Em um projeto C++/CX, pode-se aceder a membros privados e a elementos nomeados na marcação XAML. Mas em 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 Boolean exibe true ou false em C++/CX, enquanto que exibe Windows.Foundation.IReference`1<Boolean> em C++/WinRT.
Para obter mais informações e exemplos de código, consulte Consumindo objetos de marcação.
Mapeamento de tipos de plataforma C++/CX para tipos C++/WinRT
C++/CX fornece vários tipos de dados no namespace Platform. Esses tipos não são padrão em C++, por isso, você só pode usá-los quando habilitar as extensões de linguagem do Windows Runtime (propriedade de projeto do Visual Studio C/C++>Geral>Utilizar a Extensão do Windows Runtime>Sim (/ZW)). A tabela abaixo ajuda a portar tipos de plataforma para seus equivalentes em C++/WinRT. Depois de fazer isso, como C++/WinRT é C++ padrão, você pode desativar a opção /ZW.
| C++/CX | C++/WinRT |
|---|---|
| Plataforma::Agile^ | winrt::agile_ref |
| Plataforma::Array^ | Consulte Port Platform::Array^ |
| Plataforma::Exceção^ | winrt::hresult_error |
| Plataforma::InvalidArgumentException^ | winrt::hresult_invalid_argument |
| Plataforma::Object^ | winrt::Windows::Fundação::IInspectable |
| Plataforma::String^ | winrt::hstring |
Port Platform::Agile^ para winrt::agile_ref
O tipo Platform::Agile^ em C++/CX representa uma classe do Windows Runtime que pode ser acedida a partir de qualquer thread. O equivalente a C++/WinRT é winrt::agile_ref.
Em C++/CX.
Platform::Agile<Windows::UI::Core::CoreWindow> m_window;
Em C++/WinRT.
winrt::agile_ref<Windows::UI::Core::CoreWindow> m_window;
Porta Plataforma::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. Consulte Como o construtor padrão afeta as coleções para entender por que a std::vector é uma boa escolha.
Assim, sempre que você tiver um Platform::Array^ em C++/CX, suas opções de portabilidade incluem o uso de uma lista de inicializadores, um std::arrayou um std::vetor. Para obter mais informações e exemplos de código, consulte listas de inicialização padrão e matrizes e vectors padrão.
Port Platform::Exception^ para winrt::hresult_error
O tipo Platform::Exception^ é produzido em C++/CX quando uma API do Runtime do Windows retorna um HRESULT diferente de 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)
C++/WinRT fornece essas classes de exceção.
| Tipo de exceção | Classe base | HRESULTADO |
|---|---|---|
| winrt::hresult_error | chamar hresult_error::to_abi | |
| winrt::hresult_access_denied | winrt::hresult_error | E_ACCESSDENIED |
| winrt::hresult_canceled | winrt::hresult_error | ERRO_CANCELADO |
| winrt::hresult_changed_state | winrt::hresult_error | E_CHANGED_STATE |
| winrt::hresult_class_not_available | winrt::hresult_error | CLASS_E_CLASSNAODISPONIVEL |
| 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 (argumento inválido em WinRT) | 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 | código de erro RPC_E_WRONG_THREAD (Thread Errada) |
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 mensagem função, que retorna a representação de cadeia de caracteres desse HRESULT.
Aqui está um exemplo de lançamento de 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" };
Port 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 null.
winrt::Windows::Foundation::IInspectable var{ nullptr };
Port Platform::String^ para winrt::hstring
Platform::String^ é equivalente ao tipo HSTRING ABI do Tempo de Execução do Windows. Para C++/WinRT, o equivalente é winrt::hstring. Mas com C++/WinRT, você pode chamar APIs do Tempo de Execução do Windows usando tipos de cadeia de caracteres amplas da Biblioteca Padrão do C++, como std::wstring,e/ou literais de cadeia de caracteres larga. Para obter mais detalhes e exemplos de código, consulte manipulação de strings em C++/WinRT.
Com C++/CX, você pode acessar a propriedade Platform::String::Data para recuperar a string como um array de estilo C const wchar_t* (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 no estilo C terminada em nulo, assim como pode fazer a partir de 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 a API em MIDL 3.0 assim.
// LogType.idl
void LogWrapLine(String str);
A cadeia de ferramentas C++/WinRT irá então gerar o código-fonte para si que é parecido com este.
void LogWrapLine(winrt::hstring const& str);
ToString()
Os tipos C++/CX fornecem o Object::ToString método.
int i{ 2 };
auto s{ i.ToString() }; // s is a Platform::String^ with value L"2".
O C++/WinRT não fornece diretamente esse recurso, mas você 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. Você precisará adicionar sobrecargas para quaisquer tipos adicionais que deseja stringificar.
| Língua | Converter int para string | Transformar enum 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 querer transformar um enum em string, será necessário 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));
}
}
}
Essas stringificações são frequentemente 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 vinculações executarão winrt::to_hstring da propriedade vinculada. 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 de compilação.
Construção de cadeias de caracteres
C++/CX e C++/WinRT utilizam o padrão std::wstringstream para a construção de strings.
| Funcionamento | C++/CX | C++/WinRT |
|---|---|---|
| Acrescentar cadeia de caracteres, preservando nulos | stream.print(s->Data(), s->Length); |
stream << std::wstring_view{ s }; |
| Acrescentar cadeia de caracteres, parar no primeiro 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 um Platform::String
| Funcionamento | C++/CX | C++/WinRT |
|---|---|---|
| Construir cadeia de caracteres a partir de literal | String^ s = "hello";String^ s = L"hello"; |
// winrt::hstring s{ "hello" }; // Doesn't compilewinrt::hstring s{ L"hello" }; |
| Converter de std::wstring, mantendo 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 de std::wstringe parar no primeiro null | String^ s = ref new String(ws.c_str()); |
winrt::hstring s{ ws.c_str() };s = winrt::hstring(ws.c_str());// s = ws.c_str(); // Doesn't compile |
| 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, parar no primeiro valor nulo | std::wstring ws{ s->Data() };ws = s->Data(); |
std::wstring ws{ s.c_str() };ws = s.c_str(); |
| Passar 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
- winrt::delegate struct modelo
- winrt::hresult_error struct
- winrt::hstring struct
- de namespace winrt
Tópicos relacionados
- C++/CX
- Eventos de autor em C++/WinRT
- Simultaneidade e operações assíncronas com C++/WinRT
- Consumir APIs com C++/WinRT
- Manipular eventos usando delegados em C++/WinRT
- Interoperabilidade entre C++/WinRT e C++/CX
- Assincronia e interoperabilidade entre C++/WinRT e C++/CX
- de referência do Microsoft Interface Definition Language 3.0
- Mudar de WRL para C++/WinRT
- Tratamento de cadeias de caracteres em C++/WinRT