Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этой статье, являющейся первой в серии, описано, как перенести исходный код из проекта C++/CX в его эквивалент на C++/WinRT.
Если проект также использует библиотеку шаблонов среды выполнения Windows C++ (WRL), типы см. в разделе Переход на C++/WinRT из WRL.
Стратегии переноса
Стоит знать, что перенос из C++/CX в C++/WinRT обычно прост, исключением является переход от задач библиотеки параллельных шаблонов (PPL) к корутинам. Модели отличаются. Нет естественного однозначного соответствия между задачами PPL и корутинами, и нет простого способа автоматически перенести код, который работает во всех возможных случаях. Сведения об этом конкретном аспекте переноса и об опциях взаимодействия между двумя моделями см. в разделе Асинхронность и взаимодействие между C++/WinRT и C++/CX.
Команды разработчиков обычно сообщают, что после того, как они преодолеют проблемы переноса асинхронного кода, остальная часть работы по переносу в значительной степени является механической.
Перенос за один проход
Если вы можете перенести весь проект за один раз, вам потребуется только этот раздел для получения необходимой информации (и вам не понадобятся темы взаимодействия и, которые следуют за этим). Мы рекомендуем начать с создания нового проекта в Visual Studio, используя один из шаблонов проектов C++/WinRT (см. поддержку Visual Studio для C++/WinRT). Затем переместите файлы исходного кода в новый проект и перенесите весь исходный код C++/CX в C++/WinRT по мере этого.
Кроме того, если вы предпочитаете выполнять перенос в существующем проекте C++/CX, вам потребуется добавить в него поддержку C++/WinRT. Описанные ниже действия описаны в принятии проекта C++/CX и добавлении поддержки C++/WinRT. По завершении переноса вы превратили чистый проект C++/CX в чистый проект C++/WinRT.
Замечание
Если у вас есть проект компонента среды выполнения Windows, перенос в одном проходе является единственным вариантом. Проект компонента среды выполнения Windows, написанный на C++, должен содержать либо весь исходный код C++/CX, либо весь исходный код C++/WinRT. Они не могут сосуществовать в этом типе проекта.
Перенос проекта постепенно
За исключением проектов компонентов среды выполнения Windows, как упоминалось в предыдущем разделе, если размер или сложность вашей базы кода делает необходимым постепенный перенос проекта, вам потребуется процесс переноса, в течение которого код C++/CX и C++/WinRT временно существуют параллельно в том же проекте. Помимо чтения этого раздела, также посмотрите взаимодействие между C++/WinRT и C++/CX и асинхронию и взаимодействие между C++/WinRT и C++/CX. В этих разделах содержатся сведения и примеры кода, показывающие, как взаимодействовать между двумя проекциями языка.
Чтобы подготовить проект к постепенному переносу, можно добавить поддержку C++/WinRT в проект C++/CX. Описанные ниже действия описаны в принятии проекта C++/CX и добавлении поддержки C++/WinRT. Затем можно постепенно перенести оттуда.
Другой вариант — создать новый проект в Visual Studio, используя один из шаблонов проекта C++/WinRT (см. поддержку Visual Studio для C++/WinRT). Затем добавьте поддержку C++/CX в этот проект. Шаги, которые вы должны выполнить для этого, описаны в создании проекта C++/WinRT и добавлении поддержки C++/CX. Затем вы можете начать перенос исходного кода в этот код и переносить некоторые исходного кода C++/CX в C++/WinRT.
В любом случае, вы будете осуществлять двустороннее взаимодействие между вашим кодом C++/WinRT и любым кодом C++/CX, который еще не перенесен.
Замечание
Как C++/CX, так и windows SDK объявляют типы в корневом пространстве имен Windows. Тип Windows, проецируемый в C++/WinRT, имеет то же полное имя, что и тип Windows, но он помещается в пространство имен winrt C++. Эти отдельные пространства имен позволяют вам переносить код с C++/CX на C++/WinRT в удобном для вас темпе.
Постепенный перенос проекта XAML
Это важно
Для проекта, использующего XAML, в любое время все типы страниц XAML должны быть полностью C++/CX или полностью C++/WinRT. Вы всё ещё можете смешивать C++/CX и C++/WinRT вне типов страниц XAML в одном проекте (в ваших моделях и моделях представлений, и в других частях проекта).
В этом сценарии рабочий процесс, который мы рекомендуем, следующий: создайте новый проект в C++/WinRT и скопируйте исходный код и разметку из проекта C++/CX. Если все типы страниц XAML являются C++/WinRT, вы можете добавить новые XAML-страницы с помощью Project>Добавить новый элемент...>Visual C++>Пустая страница (C++/WinRT).
Кроме того, компонент среды выполнения Windows (WRC) можно использовать для вывода кода из проекта XAML C++/CX при его переносе.
- Вы можете создать новый проект WRC C++/CX, переместить столько кода C++/CX, сколько можно сделать в этом проекте, а затем изменить проект XAML на C++/WinRT.
- Или можно создать новый проект C++/WinRT WRC, оставить проект XAML как C++/CX и начать перенос C++/CX в C++/WinRT и переместить полученный код из проекта XAML и в проект компонента.
- Кроме того, вы можете использовать проект компонента C++/CX вместе с проектом компонента C++/WinRT в одном решении, ссылаться на оба из проекта приложения и постепенно переносить код из одного в другой. См. также взаимодействие между C++/WinRT и C++/CX, а также для получения дополнительных сведений об использовании двух языковых проекций в одном проекте.
Первые шаги при переносе проекта C++/CX в C++/WinRT
Независимо от выбранной вами стратегии переноса (переноса за один раз или поэтапного переноса), первым шагом будет подготовка вашего проекта к переносу. Вот краткий обзор того, что мы описали в стратегиях переноса проекта с точки зрения типа проекта, с которого вы начнете, и как его настроить.
- перенос за один проход. Создайте проект в Visual Studio с помощью одного из шаблонов проектов C++/WinRT. Переместите файлы из проекта C++/CX в новый проект и перенесите исходный код C++/CX.
- Постепенное портирование проекта, отличного от XAML. Вы можете выбрать добавление поддержки C++/WinRT в ваш проект C++/CX (см. о том, как проект C++/CX и добавление поддержки C++/WinRT) и постепенно переносить. Вы также можете создать проект C++/WinRT и добавить поддержку C++/CX в него (см. Создание проекта C++/WinRT и добавление поддержки C++/CX), перемещение файлов поверх и постепенное перемещение файлов через порт.
- Постепенный перенос проекта XAML. Создайте новый проект C++/WinRT, перемещайте файлы и постепенно переносите код. В любое время типы страниц XAML должны быть всех C++/WinRT или всех C++/CX.
Остальная часть этого раздела применяется независимо от выбранной стратегии переноса. Он содержит каталог технических сведений, связанных с переносом исходного кода из C++/CX в C++/WinRT. Если вы постепенно выполняете портирование, то, скорее всего, вы также захотите ознакомиться с взаимодействием между C++/WinRT и C++/CX, а также с Асинхронностью и взаимодействием между C++/WinRT и C++/CX.
Соглашения об именовании файлов
Файлы разметки XAML
Источник файла | C++/CX | C++/WinRT |
---|---|---|
XAML-файлы разработчика | MyPage.xaml MyPage.xaml.h MyPage.xaml.cpp |
MyPage.xaml MyPage.h MyPage.cpp MyPage.idl (см. ниже) |
сгенерированные файлы XAML | MyPage.xaml.g.h MyPage.xaml.g.hpp |
MyPage.xaml.g.h MyPage.xaml.g.hpp MyPage.g.h |
Обратите внимание, что C++/WinRT удаляет .xaml
из *.h
и *.cpp
имен файлов.
C++/WinRT добавляет дополнительный файл разработчика, Midl-файл (idl). C++/CX автоматически создает этот файл внутренне, добавляя в него каждый общедоступный и защищенный элемент. В C++/WinRT вы добавляете и создаете файл самостоятельно. Дополнительные сведения, примеры кода и пошаговое руководство по созданию IDL см. в элементах управления XAML; привязка к свойству C++/WinRT.
Также см. классы среды выполнения Factoring в midl-файлах (IDL)
Классы среды выполнения
C++/CX не накладывает ограничения на имена файлов заголовков; Обычно несколько определений классов среды выполнения помещают в один файл заголовка, особенно для небольших классов. Но C++/WinRT требует, чтобы каждый класс среды выполнения имеет собственный файл заголовка с именем класса.
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 { ... }; } |
Менее распространено (но всё же допустимо) в C++/CX использование заголовочных файлов с разными именами для пользовательских элементов управления XAML. Чтобы соответствовать имени класса, необходимо переименовать этот файл заголовка.
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 { ... }; } |
Требования к файлу заголовка
C++/CX не требует включения специальных файлов заголовков, так как он автоматически создает файлы заголовков из .winmd
файлов. Обычно в C++/CX используются директивы using
для пространств имен, используемых по имени.
using namespace Windows::Media::Playback;
String^ NameOfFirstVideoTrack(MediaPlaybackItem^ item)
{
return item->VideoTracks->GetAt(0)->Name;
}
Директива using namespace Windows::Media::Playback
позволяет записывать MediaPlaybackItem
без префикса пространства имен. Мы также коснулись пространства имен Windows.Media.Core
, так как item->VideoTracks->GetAt(0)
возвращает Windows.Media.Core.VideoTrack. Но нам не нужно вводить имя VideoTrack в любом месте, поэтому нам не нужна директива using Windows.Media.Core
.
Для C++/WinRT требуется включать файл заголовка для каждого потребляемого пространства имен, даже если вы его не указываете.
#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();
}
С другой стороны, несмотря на то, что событие MediaPlaybackItem.AudioTracksChanged имеет тип TypedEventHandler<MediaPlaybackItem, Windows.Foundation.Collections.IVectorChangedEventArgs>, нам не нужно включать winrt/Windows.Foundation.Collections.h
, поскольку мы не использовали это событие.
C++/WinRT также требует включения файлов заголовков для пространств имен, используемых разметкой XAML.
<!-- MainPage.xaml -->
<Rectangle Height="400"/>
Использование класса Rectangle означает, что необходимо добавить эту директиву включения.
// MainPage.h
#include <winrt/Windows.UI.Xaml.Shapes.h>
Если вы забудете файл заголовка, всё скомпилируется нормально, но возникнут ошибки компоновки, так как consume_
классы отсутствуют.
Передача параметров
При написании исходного кода C++/CX вы передаете типы C++/CX в качестве параметров функции в виде ссылок на hat (^).
void LogPresenceRecord(PresenceRecord^ record);
В C++/WinRT для синхронных функций по умолчанию следует использовать параметры const&
. Это позволит избежать копирования и перекрывающих накладных расходов. Но ваши корутины должны использовать сквозное значение, чтобы обеспечить их захват по значению и избежать проблем со временем существования (дополнительные сведения см. в разделе параллелизм и асинхронные операции с C++/WinRT).
void LogPresenceRecord(PresenceRecord const& record);
IASyncAction LogPresenceRecordAsync(PresenceRecord const record);
Объект C++/WinRT является фундаментальным значением, которое содержит указатель интерфейса на резервный объект среды выполнения Windows. При копировании объекта C++/WinRT компилятор копирует инкапсулированный указатель интерфейса, добавив его число ссылок. В конечном итоге уничтожение копии включает уменьшение количества ссылок. Таким образом, накладные расходы на копию возникают только при необходимости.
Ссылки на переменные и поля
При написании исходного кода C++/CX используются переменные hat (^) для ссылки на объекты среды выполнения Windows и оператор со стрелками (->) для разыменовки переменной шляпы.
IVectorView<User^>^ userList = User::Users;
if (userList != nullptr)
{
for (UINT32 iUser = 0; iUser < userList->Size; ++iUser)
...
При переносе в эквивалентный код C++/WinRT можно получить длинный путь, удалив шляпы и изменив оператор стрелки (->) на оператор dot (.). Проецируемые типы C++/WinRT — это значения, а не указатели.
IVectorView<User> userList = User::Users();
if (userList != nullptr)
{
for (UINT32 iUser = 0; iUser < userList.Size(); ++iUser)
...
Конструктор по умолчанию для управляемой ссылки C++/CX инициализирует её как null. Ниже приведен пример кода C++/CX, в котором мы создадим переменную или поле правильного типа, но неинициализированную. Другими словами, изначально он не ссылается на TextBlock; мы планируем назначить ссылку позже.
TextBlock^ textBlock;
class MyClass
{
TextBlock^ textBlock;
};
Эквивалент в C++/WinRT см. в разделе отложенной инициализации.
Свойства
Расширения языка C++/CX включают концепцию свойств. При написании исходного кода C++/CX можно получить доступ к свойству, как если бы это было поле. Стандарт C++ не имеет концепции свойства, поэтому в C++/WinRT вызывается функция получения и задания функций.
В следующих примерах XboxUserId, UserState, PresenceDeviceRecords, и Size — все они являются свойствами.
Получение значения из свойства
Вот как получить значение свойства в C++/CX.
void Sample::LogPresenceRecord(PresenceRecord^ record)
{
auto id = record->XboxUserId;
auto state = record->UserState;
auto size = record->PresenceDeviceRecords->Size;
}
Эквивалентный исходный код C++/WinRT вызывает функцию с тем же именем, что и свойство, но без параметров.
void Sample::LogPresenceRecord(PresenceRecord const& record)
{
auto id = record.XboxUserId();
auto state = record.UserState();
auto size = record.PresenceDeviceRecords().Size();
}
Обратите внимание, что функция PresenceDeviceRecords возвращает объект среды выполнения Windows, который сам имеет функцию Size. Так как возвращаемый объект также является проецируемым типом C++/WinRT, мы разыменовываем с помощью оператора точки, чтобы вызвать Size.
Задание свойства новому значению
Установка свойства на новое значение следует аналогичному шаблону. Во-первых, в C++/CX.
record->UserState = newValue;
Чтобы выполнить эквивалент в C++/WinRT, необходимо вызвать функцию с тем же именем, что и свойство, и передать аргумент.
record.UserState(newValue);
Создание экземпляра класса
Вы работаете с объектом C++/CX с помощью дескриптора, известного как ссылка на шляпу (^). Вы создаете новый объект с помощью ключевого слова ref new
, который, в свою очередь, вызывает RoActivateInstance для активации нового экземпляра класса среды выполнения.
using namespace Windows::Storage::Streams;
class Sample
{
private:
Buffer^ m_gamerPicBuffer = ref new Buffer(MAX_IMAGE_SIZE);
};
Объект C++/WinRT — это значение; поэтому его можно выделить в стеке или в виде поля объекта. Вы никогда не используйте ref new
(ни new
) для выделения объекта C++/WinRT. За кулисами вызывается RoActivateInstance.
using namespace winrt::Windows::Storage::Streams;
struct Sample
{
private:
Buffer m_gamerPicBuffer{ MAX_IMAGE_SIZE };
};
Если ресурс является дорогостоящим для инициализации, обычно откладывать инициализацию его до тех пор, пока он не потребуется. Как уже упоминалось, конструктор по умолчанию для ссылки на шляпу C++/CX инициализирует его значение null.
using namespace Windows::Storage::Streams;
class Sample
{
public:
void DelayedInit()
{
// Allocate the actual buffer.
m_gamerPicBuffer = ref new Buffer(MAX_IMAGE_SIZE);
}
private:
Buffer^ m_gamerPicBuffer;
};
Тот же код, перенесенный в C++/WinRT. Обратите внимание на использование конструктора std::nullptr_t. Дополнительные сведения об этом конструкторе см. в отложенная инициализация.
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 };
};
Как конструктор по умолчанию влияет на коллекции
Типы коллекций C++ используют конструктор по умолчанию, что может привести к непреднамеренному созданию объектов.
Сценарий | C++/CX | C++/WinRT (неправильно) | C++/WinRT (корректно) |
---|---|---|---|
Локальная переменная, изначально пустая | TextBox^ textBox; |
TextBox textBox; // Creates a TextBox! |
TextBox textBox{ nullptr }; |
Переменная-член, первоначально пустая | class C { TextBox^ textBox; }; |
class C { TextBox textBox; // Creates a TextBox! }; |
class C { TextBox textbox{ nullptr }; }; |
Глобальная переменная, изначально пустая | TextBox^ g_textBox; |
TextBox g_textBox; // Creates a TextBox! |
TextBox g_textBox{ nullptr }; |
Вектор пустых ссылок | std::vector<TextBox^> boxes(10); |
// Creates 10 TextBox objects! std::vector<TextBox> boxes(10); |
std::vector<TextBox> boxes(10, nullptr); |
Установка значения в карте | 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); |
Массив пустых ссылок | TextBox^ boxes[2]; |
// Creates 2 TextBox objects! TextBox boxes[2]; |
TextBox boxes[2] = { nullptr, nullptr }; |
Пара | std::pair<TextBox^, String^> p; |
// Creates a TextBox! std::pair<TextBox, String> p; |
std::pair<TextBox, String> p{ nullptr, nullptr }; |
Дополнительные сведения о коллекциях пустых ссылок
Каждый раз, когда у вас есть Platform::Array^ (см. портирование в Platform::Array^) в C++/CX, вы можете перенести его в std::vector в C++/WinRT (на самом деле, в любой непрерывный контейнер) нежели чем оставлять в виде массива. Преимущества выбора std::vectorочевидны.
Например, хотя существует сокращение для создания вектора фиксированного размера пустых ссылок (см. таблицу выше), такого сокращения для создания массива пустых ссылок не существует. Необходимо повторить nullptr
для каждого элемента в массиве. Если у вас недостаточно элементов, то недостающие будут созданы по умолчанию.
Для вектора можно заполнить его пустыми ссылками на этапе инициализации (как в таблице выше), или заполнить его пустыми ссылками после инициализации, например, с помощью такого кода.
std::vector<TextBox> boxes(10); // 10 default-constructed TextBoxes.
boxes.resize(10, nullptr); // 10 empty references.
Дополнительные сведения о примере std::map
Оператор []
подстрока для std::map ведет себя следующим образом.
- Если ключ найден на карте, верните ссылку на существующее значение (которое можно перезаписать).
- Если ключ не найден в словаре, создайте в словаре новую запись, состоящую из ключа (перемещаемого, если возможно) и значения, созданного по умолчанию, и верните ссылку на это значение (которое затем можно перезаписать).
Другими словами, оператор []
всегда создает запись в карте. Это отличается от C#, Java и JavaScript.
Преобразование из базового класса среды выполнения в производный
Обычно у вас есть ссылка на базу, которую вы знаете, относится к объекту производного типа. В C++/CX используется dynamic_cast
для приведения ссылки на базовый класс в ссылку на производный класс.
dynamic_cast
на самом деле просто является скрытым вызовом для QueryInterface. Вот типичный пример: вы обрабатываете событие изменения свойства зависимости и хотите преобразовать из DependencyObject обратно к исходному типу, которому принадлежит это свойство зависимости.
void BgLabelControl::OnLabelChanged(Windows::UI::Xaml::DependencyObject^ d, Windows::UI::Xaml::DependencyPropertyChangedEventArgs^ e)
{
BgLabelControl^ theControl{ dynamic_cast<BgLabelControl^>(d) };
if (theControl != nullptr)
{
// succeeded ...
}
}
Эквивалентный код C++/WinRT заменяет dynamic_cast
вызовом функции IUnknown::try_as, которая инкапсулирует QueryInterface. Кроме того, у вас есть возможность вызывать IUnknown::as, который выбрасывает исключение, если запрос на требуемый интерфейс (интерфейс является интерфейсом по умолчанию для запрашиваемого типа) не возвращается. Ниже приведен пример кода 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 ...
}
}
Производные классы
Чтобы получить производный от класса среды выполнения, базовый класс должен быть составной. C++/CX не требует выполнения каких-либо специальных действий, чтобы сделать классы компонуемыми, но C++/WinRT делает. Вы используете ключевое слово , чтобы указать, что вы хотите, чтобы ваш класс мог использоваться в качестве базового класса.
unsealed runtimeclass BasePage : Windows.UI.Xaml.Controls.Page
{
...
}
runtimeclass DerivedPage : BasePage
{
...
}
В классе заголовка реализации необходимо включить файл заголовка базового класса перед включением автогенерированного заголовка для производного класса. В противном случае вы получите такие ошибки, как "Недопустимое использование этого типа в качестве выражения".
// 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>
{
...
}
}
Обработка событий с помощью делегата
Ниже приведен типичный пример обработки события в C++/CX с помощью лямбда-функции в качестве делегата в данном случае.
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.
});
Это эквивалентно в 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.
});
Вместо лямбда-функции вы можете реализовать делегат как независимую функцию или как указатель на функцию-член. Дополнительные сведения см. в разделе Обработка событий с помощью делегатов в C++/WinRT.
Если вы портируете из базы кода C++/CX, где события и делегаты используются внутри (не между двоичными файлами), то winrt::delegate поможет вам воспроизвести этот шаблон в C++/WinRT. См. также параметризованные делегаты , простые сигналы и обратные вызовы в проекте.
Отзыв делегата
В C++/CX оператор -=
используется для отмены предыдущей регистрации событий.
myButton->Click -= token;
Это эквивалентно в C++/WinRT.
myButton().Click(token);
Для получения дополнительной информации и вариантов см. раздел Отзыв зарегистрированного делегата.
Бокс и распаковка
C++/CX автоматически упаковывает скаляры в объекты. C++/WinRT требует, чтобы вы явно вызывали функцию winrt::box_value. Оба языка требуют явной распаковки. См. упаковку и распаковку с помощью C++/WinRT.
В следующих таблицах мы будем использовать эти определения.
C++/CX | C++/WinRT |
---|---|
int i; |
int i; |
String^ s; |
winrt::hstring s; |
Object^ o; |
IInspectable o; |
Операция | C++/CX | C++/WinRT |
---|---|---|
Бокс | o = 1; o = "string"; |
o = box_value(1); o = box_value(L"string"); |
Распаковки | i = (int)o; s = (String^)o; |
i = unbox_value<int>(o); s = unbox_value<winrt::hstring>(o); |
C++/CX и C# вызывают исключения, если попытаться распаковать null-указатель в значимый тип. C++/WinRT считает это ошибкой программирования и приводит к сбою. В C++/WinRT используйте функцию winrt::unbox_value_or, если вы хотите обработать случай, когда объект не относится к типу, который вы подумали.
Сценарий | C++/CX | C++/WinRT |
---|---|---|
Распаковка известного целого числа | i = (int)o; |
i = unbox_value<int>(o); |
Если o имеет значение NULL | Platform::NullReferenceException |
Авария |
Если o не является упакованным int | Platform::InvalidCastException |
Авария |
Распаковать int, использовать резервное значение, если это значение NULL; вывести ошибку, если что-либо другое | i = o ? (int)o : fallback; |
i = o ? unbox_value<int>(o) : fallback; |
Уберите упаковку int, если возможно; используйте резервный вариант для любого другого типа данных. | auto box = dynamic_cast<IBox<int>^>(o); i = box ? box->Value : fallback; |
i = unbox_value_or<int>(o, fallback); |
Бокс и распаковка строки
Строка в некотором смысле является типом значения и другими способами ссылочным типом. C++/CX и C++/WinRT обрабатывают строки по-разному.
Тип ABI HSTRING — это указатель на строку, в которой ведется подсчет ссылок. Но он не является производным от IInspectable, поэтому технически не является объектом . Кроме того, null HSTRING представляет пустую строку. Боксирование объектов, не производных от IInspectable, осуществляется путем их упаковки внутри IReference<T>, при этом Windows Runtime предоставляет стандартную реализацию в виде объекта PropertyValue (пользовательские типы передаются как PropertyType::OtherType).
C++/CX представляет строку среды выполнения Windows в качестве ссылочного типа, тогда как C++/WinRT проецирует строку в качестве типа значения. Это означает, что прямоугольная строка NULL может иметь различные представления в зависимости от того, как вы там попали.
Кроме того, C++/CX позволяет разыменовать значение NULL String^, в этом случае он ведет себя как строка ""
.
Поведение | C++/CX | C++/WinRT |
---|---|---|
Объявления | Object^ o; String^ s; |
IInspectable o; hstring s; |
Категория строкового типа | Ссылочный тип | Тип значения |
NULL используется в проектах HSTRING | (String^)nullptr |
hstring{} |
Совпадают ли значения NULL и "" ? |
Да | Да |
Валидность null | s = nullptr; s->Length == 0 (допустимо) |
s = hstring{}; s.size() == 0 (допустимо) |
Если присвоить пустую строку объекту | o = (String^)nullptr; o == nullptr |
o = box_value(hstring{}); o != nullptr |
Если вы назначаете "" объекту |
o = ""; o == nullptr |
o = box_value(hstring{L""}); o != nullptr |
Базовый бокс и распаковка.
Операция | C++/CX | C++/WinRT |
---|---|---|
Заключить строку в рамку | o = s; Пустая строка становится nullptr. |
o = box_value(s); Пустая строка становится ненулевой объектом. |
Распаковка известной строки | s = (String^)o; Пустой объект становится пустой строкой. Возникает исключение InvalidCastException, если это не строка. |
s = unbox_value<hstring>(o); Объект NULL вызывает сбой. Программа аварийно завершится, если входные данные не являются строкой. |
Распакуйте возможную строку | s = dynamic_cast<String^>(o); Пустой объект или объект, который не является строкой, становится пустой строкой. |
s = unbox_value_or<hstring>(o, fallback); Значение NULL или не являющееся строкой превращается в резервное значение. Пустая строка сохранена. |
Конкурентность и асинхронные операции
Библиотека параллельных шаблонов (PPL) (concurrency::task, например) была обновлена для поддержки ссылок на ^ (шляпу) в C++/CX.
Для C++/WinRT следует использовать корутины и co_await
вместо этого. Дополнительные сведения и примеры кода см. в разделе «Параллелизм и асинхронные операции с C++/WinRT».
Использование объектов из разметки XAML
В проекте на C++/CX вы можете использовать частные члены и именованные элементы из разметки XAML. Но в C++/WinRT все сущности, используемые с помощью расширения разметки XAML {x:Bind}, должны предоставляться публично в IDL.
Кроме того, привязка к логическому типу отображает true
или false
в C++/CX, но отображается Windows.Foundation.IReference`1<логический тип> в C++/WinRT.
Дополнительные сведения и примеры кода смотрите в разделе Использование объектов из разметки.
Сопоставление типов платформы C++/CX с типами C++/WinRT
C++/CX предоставляет несколько типов данных в пространстве имен платформы /ZW
.
C++/CX | C++/WinRT |
---|---|
Платформа Agile^ | winrt::agile_ref |
Platform::Array^ | См. порт Platform::Array^ |
Платформа::Исключение^ | winrt::hresult_error |
Platform::InvalidArgumentException^ | winrt::hresult_invalid_argument |
Platform::Object^ | winrt::Windows::Foundation::IInspectable |
Platform::String^ | winrt::hstring |
Порт Platform::Agile^ в winrt::agile_ref
Тип Platform::Agile^ в C++/CX представляет класс среды выполнения Windows, к которому можно получить доступ из любого потока. Эквивалент C++/WinRT равен winrt::agile_ref.
В C++/CX.
Platform::Agile<Windows::UI::Core::CoreWindow> m_window;
В C++/WinRT.
winrt::agile_ref<Windows::UI::Core::CoreWindow> m_window;
Порт Platform::Array^
В случаях, когда C++/CX требует использования массива, C++/WinRT позволяет использовать любой смежный контейнер. См. Как конструктор по умолчанию влияет на коллекции, почему std::vector — хороший выбор.
Поэтому, если у вас есть Platform::Array^ в C++/CX, ваши варианты переноса включают использование списка инициализатора, std::arrayили std::vector. Дополнительные сведения и примеры кода см. в стандартных списках инициализаторов и стандартных массивах и векторах.
Порт Platform::Exception^winrt::hresult_error
Тип Platform::Exception^ создается в C++/CX, когда API среды выполнения Windows возвращает HRESULT не со значением S_OK. Эквивалент C++/WinRT winrt::hresult_error.
Чтобы перенести на C++/WinRT, измените весь код, использующий Platform::Exception^ на использование winrt::hresult_error.
В C++/CX.
catch (Platform::Exception^ ex)
В C++/WinRT.
catch (winrt::hresult_error const& ex)
C++/WinRT предоставляет эти классы исключений.
Тип исключения | Базовый класс | HRESULT |
---|---|---|
winrt::hresult_error | вызов hresult_error::to_abi | |
winrt::hresult_access_denied | winrt::hresult_error | E_ACCESSDENIED (Доступ запрещён) |
winrt::hresult_canceled | winrt::hresult_error | ОШИБКА_ОТМЕНЕНО |
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 | Недопустимое назначение делегата |
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 |
Обратите внимание, что каждый класс (через базовый класс hresult_error) предоставляет функцию to_abi, которая возвращает HRESULT ошибки и функцию сообщения, которая возвращает строковое представление этого HRESULT.
Ниже приведен пример возникновения исключения в C++/CX.
throw ref new Platform::InvalidArgumentException(L"A valid User is required");
И эквивалент в C++/WinRT.
throw winrt::hresult_invalid_argument{ L"A valid User is required" };
Порт Platform::Object^ в winrt::Windows::Foundation::IInspectable
Как и все типы C++/WinRT, winrt::Windows::Foundation::IInspectable — это тип значения. Вот как инициализировать переменную этого типа до null.
winrt::Windows::Foundation::IInspectable var{ nullptr };
Порт Platform::String^ в winrt::hstring
Platform::String^ эквивалентен типу ABI среды выполнения Windows HSTRING. Для C++/WinRT эквивалентен winrt::hstring. Но с помощью C++/WinRT можно вызывать API среды выполнения Windows с помощью расширенных типов строк библиотеки C++, таких как std::wstringи /или широкие строковые литералы. Дополнительные сведения и примеры кода см. в обработке строк в C++/WinRT.
С помощью C++/CX можно получить доступ к свойству Platform::String::Data, чтобы извлечь строку в формате массива в стиле C const wchar_t* (например, для передачи его в std::wcout).
auto var{ titleRecord->TitleName->Data() };
Чтобы сделать то же самое с C++/WinRT, можно использовать функцию hstring::c_str, чтобы получить версию строки в стиле C, завершающуюся null, так же, как и из std::wstring.
auto var{ titleRecord.TitleName().c_str() };
При реализации API, которые принимают или возвращают строки, вы обычно изменяете любой код C++/CX, использующий Platform::String^, чтобы использовать winrt::hstring.
Ниже приведен пример API C++/CX, который принимает строку.
void LogWrapLine(Platform::String^ str);
Для C++/WinRT можно объявить этот API в MIDL 3.0, как показано ниже.
// LogType.idl
void LogWrapLine(String str);
Цепочка инструментов C++/WinRT создаст исходный код для вас, который выглядит следующим образом.
void LogWrapLine(winrt::hstring const& str);
ToString()
Типы C++/CX предоставляют метод Object::ToString.
int i{ 2 };
auto s{ i.ToString() }; // s is a Platform::String^ with value L"2".
C++/WinRT не предоставляет эту функцию напрямую, но вы можете воспользоваться альтернативами.
int i{ 2 };
auto s{ std::to_wstring(i) }; // s is a std::wstring with value L"2".
C++/WinRT также поддерживает winrt::to_hstring для ограниченного количества типов. Вам потребуется добавить перегрузки для любых дополнительных типов, которые требуется строковать.
Язык | Преобразовать int в строку | Преобразование перечисления в строку |
---|---|---|
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); |
При преобразовании перечисления в строку вам потребуется реализовать 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));
}
}
}
Эти строкификации часто неявно используются в привязке данных.
<TextBlock>
You have <Run Text="{Binding FlowerCount}"/> flowers.
</TextBlock>
<TextBlock>
Most recent status is <Run Text="{x:Bind LatestOperation.Status}"/>.
</TextBlock>
Эти привязки будут выполнять winrt::to_hstring для связанного свойства. В случае второго примера (StatusEnum), необходимо указать собственную перегрузку winrt::to_hstring, в противном случае вы получите ошибку компилятора.
Формирование строки
C++/CX и C++/WinRT используют стандарт std::wstringstream для формирования строк.
Операция | C++/CX | C++/WinRT |
---|---|---|
Добавление строки, сохранение значений NULL | stream.print(s->Data(), s->Length); |
stream << std::wstring_view{ s }; |
Добавление строки, остановка при первом значении NULL | stream << s->Data(); |
stream << s.c_str(); |
Извлечение результата | ws = stream.str(); |
ws = stream.str(); |
Дополнительные примеры
В приведенных ниже примерах ws является переменной типа std::wstring. Кроме того, в то время как C++/CX может создавать Platform::String из 8-разрядной строки, C++/WinRT этого не делает.
Операция | C++/CX | C++/WinRT |
---|---|---|
Создание строки из литерала | String^ s = "hello"; String^ s = L"hello"; |
// winrt::hstring s{ "hello" }; // Doesn't compile winrt::hstring s{ L"hello" }; |
Преобразование из std::wstringс сохранением нулей | 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 |
Преобразование из std::wstring, остановка на первом значении 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 |
Преобразование в std::wstring, сохранение значений NULL | std::wstring ws{ s->Data(), s->Length }; ws = std::wstring(s>Data(), s->Length); |
std::wstring ws{ s }; ws = s; |
Преобразование в std::wstring, остановка на первом значении NULL | std::wstring ws{ s->Data() }; ws = s->Data(); |
std::wstring ws{ s.c_str() }; ws = s.c_str(); |
Передача литерала в метод | Method("hello"); Method(L"hello"); |
// Method("hello"); // Doesn't compile Method(L"hello"); |
Передайте std::wstring в метод | Method(ref new String(ws.c_str(), (uint32_t)ws.size()); // Stops on first null |
Method(ws); // param::winrt::hstring accepts std::wstring_view |
Важные API
- winrt::delegate шаблон структуры
- winrt::hresult_error структура
- winrt::hstring структура
- пространство имен winrt
Связанные темы
- C++/CX
- Мероприятия для авторов в C++/WinRT
- Конкурентность и асинхронные операции в C++/WinRT
- Использовать API с C++/WinRT
- Обработка событий с использованием делегатов в C++/WinRT
- Взаимодействие между C++/WinRT и C++/CX
- Асинхронность и взаимодействие между C++/WinRT и C++/CX
- справочник по языку определения интерфейса Майкрософт 3.0
- Переход на C++/WinRT из WRL
- Обработка строк в C++/WinRT