Что нового в C++/WinRT

В этой статье описаны возможности и изменения, реализованные в новых версиях C++/WinRT.

Сводка по последним улучшениям и дополнениям за март 2020 г.

Время сборки сократилось на 23 %

Разработчики C++/WinRT вместе с разработчиками компилятора C++ сделали все возможное, чтобы сократить время на сборку. Мы корпели над анализом работы компилятора в попытке понять, как можно изменить структуру внутренних компонентов C++/WinRT, чтобы компилятор C++ быстрее выполнял компиляцию, и как улучшить работу компилятора C++ с библиотекой C++/WinRT. В результате мы оптимизировали C++/WinRT для компилятора, а компилятор — для C++/WinRT.

Давайте рассмотрим самый худший сценарий компиляции предварительно скомпилированного заголовка (PCH), содержащего заголовки каждого пространства имен проекции C++/WinRT.

Версия Размер PCH (в байтах) Время (с)
C++/WinRT (за июль) с Visual C++ 16.3 3 004 104 632 31
C++/WinRT версии 2.0.200316.3 с Visual C++ 16.5 2 393 515 336 24

Уменьшение размера на 20 % и сокращение времени сборки на 23 %

Улучшенная поддержка MSBuild

Мы приложили немало усилий, чтобы улучшить поддержку MSBuild с учетом большого количества различных сценариев.

Ускорено кэширование фабрики

Мы оптимизировали метод встраивания критических путей в кэше фабрики, чтобы ускорить выполнение.

Это улучшение не влияет на размер кода, как описано ниже в разделе Создание оптимизированного кода обработки исключений (EH), если приложение часто использует обработку исключений C++, можно сжать двоичный файл с помощью параметра /d2FH4, который включен по умолчанию для новых проектов, созданных в Visual Studio 2019 16.3 и более поздних версий.

Повышена эффективность упаковки-преобразования

Повышена эффективность winrt::box_value при использовании с приложениями XAML (см. статью Упаковка-преобразование и распаковка-преобразование скалярных значений в IInspectable с помощью C++/WinRT). Вы наверняка заметите уменьшение размера кода в приложениях, часто выполняющих упаковку-преобразование.

Поддержка реализации COM-интерфейсов, реализующих IInspectable

Теперь с помощью C++/WinRT вы можете реализовать COM-интерфейс (не для среды выполнения Windows), который реализует IInspectable. Дополнительные сведения см. в статье о поддержке реализации COM-интерфейсов, реализующих IInspectable.

Улучшения блокировки модулей

Функции управления блокировкой модулей теперь поддерживают сценарии размещения и позволяют устранить блокировку на уровне модулей. Дополнительные сведения см. в статье Улучшения блокировки модулей.

Поддержка вывода сведений об ошибках, не относящихся к среде выполнения Windows

Некоторые API (даже некоторые API среды выполнения Windows) сообщают об ошибках, не используя интерфейсы API для определения источников ошибок в среде выполнения Windows. Для таких случаев в C++/WinRT снова используется COM-интерфейс вывода сведений об ошибках. См. статью о поддержке в C++/WinRT вывода сведений об ошибках, не относящихся к WinRT.

Поддержка модуля C++

Модуль C++ снова поддерживается, но только в экспериментальном режиме. Эта функция пока что не готова для компилятора C++.

Более эффективное возобновление работы сопрограмм

В C++/WinRT уже налажена работа сопрограмм, но мы продолжаем искать способы их улучшения. См. статью Улучшение масштабируемости при возобновлении работы сопрограмм.

Добавлены асинхронные вспомогательные функции when_all и when_any

Вспомогательная функция when_all создает объект операции IAsyncAction, которая выполняется после завершения всех указанных ожидающих операций. Вспомогательная функция when_any создает объект операции IAsyncAction, которая выполняется после завершения всех указанных ожидающих операций.

См. статьи о добавлении асинхронных вспомогательных функций when_any и when_all.

Другие оптимизации и дополнения

Кроме того, в этой версии исправлено множество ошибок, выполнена некоторая оптимизация и внесены дополнения, включая различные улучшения для упрощения отладки, оптимизации внутренних компонентов и реализаций по умолчанию. Полный список см. по этой ссылке: https://github.com/microsoft/xlang/pulls?q=is%3Apr+is%3Aclosed.

Новости и изменения в C++/WinRT 2.0

Дополнительную информацию о C++/WinRT Visual Studio Extension (VSIX), пакете Microsoft.Windows.CppWinRT NuGet и инструменте cppwinrt.exe, а также о том, как получить и установить их, см. в статье Поддержка Visual Studio для C++/WinRT, XAML, расширения VSIX и пакета NuGet.

Изменения в C++/WinRT Visual Studio Extension (VSIX) для версии 2.0

  • Визуальный отладчик теперь поддерживает Visual Studio 2019; а также продолжает поддерживать Visual Studio 2017.
  • Исправлены многочисленные ошибки.

Изменения в пакете Microsoft.Windows.CppWinRT NuGet для версии 2.0

  • Инструмент cppwinrt.exe теперь включен в пакет NuGet Microsoft.Windows.CppWinRT. Этот инструмент генерирует заголовки платформенной проекции для каждого проекта по запросу. Таким образом, инструмент cppwinrt.exe больше не зависит от пакета SDK для Windows (хотя для обеспечения совместимости инструмент по-прежнему поставляется в составе пакета SDK).
  • Чтобы включить параллельные сборки, cppwinrt.exe теперь генерирует заголовки проекции в каждой платформе или конкретной промежуточной папке конфигурации ($IntDir).
  • Поддержка сборки C++/WinRT (реквизитов/целей) теперь полностью задокументирована, на случай, если вы захотите вручную настроить файлы проекта. См. файл сведений пакета NuGet Microsoft.Windows.CppWinRT.
  • Исправлены многочисленные ошибки.

Изменения в C++/WinRT для версии 2.0

Открытый исходный код

Средство cppwinrt.exe принимает файл метаданных среды выполнения Windows (.winmd) и создает из него стандартную библиотеку на основе файла заголовка C++, которая проецирует API, описанные в метаданных. Таким образом, вы можете использовать эти API из кода C++/WinRT.

Теперь этот инструмент — это проект с полностью открытым исходным кодом, доступный на GitHub. Перейдите на страницу Microsoft/cppwinrt.

Библиотеки xlang

Полностью переносимая библиотека заголовков (для анализа формата метаданных ECMA-335, используемого средой выполнения Windows) формирует основу для всех последующих инструментов среды выполнения Windows и xlang. В частности, мы также полностью переписали инструмент cppwinrt.exe, используя библиотеки xlang. Это обеспечивает гораздо более точные запросы метаданных, решая несколько давних проблем с проекцией языков C++/WinRT.

Меньше зависимостей

Благодаря средству чтения метаданных xlang сам инструмент cppwinrt.exe имеет меньше зависимостей. Это делает его гораздо гибче, а также более пригодным для использования в большем количестве сценариев, особенно в ограниченных средах сборки. Следует отметить, что он больше не зависит от RoMetadata.dll.   Это зависимости для cppwinrt.exe 2.0.  

  • ADVAPI32.dll
  • KERNEL32.dll
  • SHLWAPI.dll
  • XmlLite.dll

Все эти библиотеки DLL доступны не только в Windows 10, но и в более ранних версиях вплоть до Windows 7, а также в Windows Vista. Теперь при необходимости можно запустить cppwinrt.exe для создания заголовков C++ проекта на старом сервере сборки под управлением Windows 7. Приложив немного усилий, можно даже запустить C++/WinRT в Windows 7, если вам это требуется.

Сравните приведенный выше список с зависимостями cppwinrt.exe 1.0.

  • ADVAPI32.dll
  • SHELL32.dll
  • api-ms-win-core-file-l1-1-0.dll
  • XmlLite.dll
  • api-ms-win-core-libraryloader-l1-2-0.dll
  • api-ms-win-core-processenvironment-l1-1-0.dll
  • RoMetadata.dll
  • SHLWAPI.dll
  • KERNEL32.dll
  • api-ms-win-core-rtlsupport-l1-1-0.dll
  • api-ms-win-core-heap-l1-1-0.dll
  • api-ms-win-core-timezone-l1-1-0.dll
  • api-ms-win-core-console-l1-1-0.dll
  • api-ms-win-core-localization-l1-2-0.dll
  • OLEAUT32.dll
  • api-ms-win-core-winrt-error-l1-1-0.dll
  • api-ms-win-core-winrt-error-l1-1-1.dll
  • api-ms-win-core-winrt-l1-1-0.dll
  • api-ms-win-core-winrt-string-l1-1-0.dll
  • api-ms-win-core-synch-l1-1-0.dll
  • api-ms-win-core-threadpool-l1-2-0.dll
  • api-ms-win-core-com-l1-1-0.dll
  • api-ms-win-core-com-l1-1-1.dll
  • api-ms-win-core-synch-l1-2-0.dll

Атрибут среды выполнения Windows noexcept

Среда выполнения Windows имеет новый атрибут [noexcept], который вы можете использовать для оформления методов и свойств в MIDL 3.0. Наличие атрибута указывает вспомогательным инструментам, что ваша реализация не вызывает исключение (и не возвращает ошибку HRESULT). Это позволяет языковым проекциям оптимизировать создание кода, избегая излишков для обработки исключений, которые требуются для поддержки вызовов двоичного интерфейса приложения (ABI), и которые потенциально могут завершиться сбоем.

C++/WinRT использует это преимущество, создавая реализации C++ noexcept как потребляющего кода, так и кода разработки. Если у вас есть безотказные методы или свойства API, и вас беспокоит размер кода, можете исследовать этот атрибут.

Создание оптимизированного кода

C++/WinRT теперь генерирует еще более эффективный исходный код C++, поэтому компилятор C++ может создавать более компактный и эффективный двоичный код. Многие из улучшений направлены на снижение затрат на обработку исключений за счет отказа от ненужной очистки информации. Для двоичных файлов, использующих большие объемы кода C++/WinRT, ожидаемое сокращение размера кода составит примерно 4 %. Код также более эффективен (работает быстрее) благодаря меньшему количеству команд.

Эти улучшения основаны на новой функции взаимодействия, которая также доступна для вас. Все типы C++/WinRT, являющиеся владельцами ресурсов, теперь содержат конструктор для назначения владельца напрямую, вместо предыдущей двухэтапной схемы.

ABI::Windows::Foundation::IStringable* raw = ...

IStringable projected(raw, take_ownership_from_abi);

printf("%ls\n", projected.ToString().c_str());

Создание оптимизированного кода обработки исключений (EH)

Это изменение дополняет работу, проделанную командой оптимизаторов Microsoft C++, чтобы снизить затраты на обработку исключений. Если в своем коде вы интенсивно используете двоичные интерфейсы приложений (ABI) (такие как COM), то увидите, что этот шаблон используется довольно часто.

int32_t Function() noexcept
{
    try
    {
        // code here constitutes unique value.
    }
    catch (...)
    {
        // code here is always duplicated.
    }
}

C++/WinRT генерирует этот шаблон для каждого реализованного API. При наличии тысяч функций API любая оптимизация может быть существенной. В прошлом оптимизатор не обнаруживал, что все блоки catch идентичны, поэтому дублировал большое количество кода каждого ABI (что, в свою очередь, способствовало убеждению, что использование исключений в системном коде приводит к созданию больших двоичных файлов). Однако, начиная с Visual Studio 2019, компилятор C++ сворачивает все эти функции catch и сохраняет только те, которые являются уникальными. В результате размер кода для двоичных файлов, которые в значительной степени зависят от этого шаблона, будет в целом сокращен на 18%. Теперь не только код EH более эффективен, чем использование кодов возврата, но и ушла в прошлое проблема больших двоичных файлов.

Улучшения добавочной сборки

Инструмент cppwinrt.exe теперь сравнивает выходные данные созданного файла заголовка / исходного файла с содержимым любого существующего файла на диске и записывает файл, только если файл действительно изменился. Это значительно экономит время на дисковых операциях ввода-вывода и гарантирует, что компилятор C++ не считает файлы "грязными". В результате во многих случаях повторная компиляция исключается или уменьшается.

Теперь создаются универсальные интерфейсы

Благодаря средству чтения метаданных xlang, C++/WinRT теперь создает все параметризованные или универсальные интерфейсы из метаданных. Интерфейсы, такие как Windows::Foundation::Collections::IVector<T>, теперь создаются из метаданных, а не вручную в winrt/base.h. В результате размер winrt/base.h был сокращен вдвое, а также прямо в коде были сгенерированы оптимизации (что было непросто сделать с использованием метода ручного запуска).

Важно!

Интерфейсы, такие, как в приведенном примере, теперь отображаются в соответствующих заголовках пространства имен, а не в winrt/base.h. Если вы еще этого не сделали, чтобы использовать интерфейс, придется включить соответствующий заголовок пространства имен.

Оптимизации компонентов

Это обновление добавляет поддержку нескольких дополнительных оптимизаций для использования C++/WinRT, описанных в следующих разделах. Так как эти оптимизации являются критическими изменениями (которые вам, возможно, придется немного модифицировать для обеспечения поддержки), вам нужно явно включить их. В Visual Studio задайте для свойства проекта Общие свойства>C++/WinRT>Оптимизировано значениеДа. Это действие аналогично добавлению <CppWinRTOptimized>true</CppWinRTOptimized> в файл проекта. Кроме того, оно аналогично добавлению параметра -opt[imize] при вызове cppwinrt.exe из командной строки.

Новый проект (из шаблона проекта) по умолчанию будет использовать -opt.

Универсальная конструкция и прямой доступ к реализации

Эти две оптимизации позволяют вашему компоненту иметь прямой доступ к его собственным типам реализации, даже если он использует только проектируемые типы. Нет необходимости использовать make, make_self или get_self, если необходимо просто использовать общедоступную область API. Ваши звонки будут скомпилированы для прямых звонков в реализацию, они также могут даже быть полностью встроенными.

Дополнительные сведения и примеры кода см. в статье о предоставлении согласия на использование универсального создания и прямого обращения к реализации.

Затираемые типы фабрик

Эта оптимизация позволяет избежать зависимостей #include в module.g.cpp, поэтому ее не нужно повторно компилировать каждый раз, когда меняется класс какой-либо одиночной реализации. В результате улучшается производительность сборки.

Умнее и эффективнее module.g.cpp для больших проектов с несколькими библиотеками

Файл module.g.cpp теперь также содержит два дополнительные составные вспомогательные приложения с именами winrt_can_unload_now и winrt_get_activation_factory. Они были разработаны для более крупных проектов, где DLL состоит из нескольких библиотек, каждая из которых имеет свои собственные классы среды выполнения. В этой ситуации нужно вручную соединить библиотеки DLL DllGetActivationFactory и DllCanUnloadNow. Эти вспомогательные приложения значительно облегчат вам задачу, избегая ложных ошибок при отправке. Флаг -lib инструмента cppwinrt.exe также может использоваться для предоставления каждой отдельной библиотеке своего собственного начального режима (а не winrt_xxx), чтобы функции каждой библиотеки могли иметь индивидуальное имя и, таким образом, безотлагательно объединяться.

Поддержка соподпрограмм

Поддержку соподпрограмм включено автоматически. Ранее поддержка находилась в нескольких местах, что, по нашему мнению, имело слишком ограничительный характер. Потом временно для v2.0 был необходим файл заголовка winrt/coroutine.h, но он больше не нужен. Поскольку асинхронные интерфейсы среды выполнения Windows теперь создаются, а не пишутся вручную, они находятся в winrt/Windows.Foundation.h. Кроме того, что он более удобен в обслуживании и поддержке, это означает, что помощникам соподпрограмм, таким как resume_foreground, больше не нужно привязываться к элементу конкретного заголовка пространства имен. Вместо этого они могут более естественным образом включать свои зависимости. Теперь это также позволяет resume_foreground поддерживать не только возобновление на заданном Windows::UI::Core::CoreDispatcher, но и может поддерживать возобновление на заданном Windows::System::DispatcherQueue. Ранее могло поддерживаться только на одном, а не обеих, так как определение могло находиться только в одном пространстве имен.

Вот пример поддержки — DispatcherQueue.

...
#include <winrt/Windows.System.h>
using namespace Windows::System;
...
fire_and_forget Async(DispatcherQueueController controller)
{
    bool queued = co_await resume_foreground(controller.DispatcherQueue());
    assert(queued);

    // This is just to simulate queue failure...
    co_await controller.ShutdownQueueAsync();

    queued = co_await resume_foreground(controller.DispatcherQueue());
    assert(!queued);
}

Вспомогательные приложения соподпрограмм теперь также имеют [[nodiscard]], из-за чего их удобнее использовать. Если вы забыли (или не понимаете, что должны сделать) co_await, чтобы они сработали с помощью [[nodiscard]], такие ошибки теперь выдают предупреждение компилятора.

Справка по диагностике прямого выделения (выделения стека)

Поскольку имена прогнозируемого класса и класса реализации (по умолчанию) одинаковы и различаются только по пространству имен, можно ошибочно принять одно за другое и случайно создать реализацию в стеке вместо того, чтобы использовать семейство вспомогательных приложений make. В некоторых случаях это может быть трудно диагностировать, потому что объект может быть уничтожен, пока выдающиеся ссылки все еще выполняются. Теперь утверждение выбирает его для отладочных сборок. Хотя утверждение не определяет выделение стека внутри соподпрограммы, тем не менее, оно помогает обнаруживать большинство таких ошибок.

Дополнительные сведения см. в статье Diagnosing direct allocations (Диагностика прямых выделений).

Улучшенные вспомогательные приложения захвата и делегаты variadic

Это обновление устраняет ограничение с помощью вспомогательных приложений захвата, а также поддерживает проектируемые типы. Время от времени это происходит с API взаимодействия среды выполнения Windows, когда они возвращают спроектированный тип.

Это обновление также добавляет поддержку get_strong и get_weak при создании делегата variadic (не для среды выполнения Windows).

Поддержка для отложенного удаления и безопасного QI во время уничтожения

Достаточно часто деструктор для объекта класса среды выполнения вызывает метод, который временно увеличивает значение счетчика ссылок. Когда счетчик ссылок сбрасывается до нуля, объект уничтожается повторно. В приложении XAML вам может потребоваться выполнить в деструкторе QueryInterface (QI), чтобы вызвать определенную реализацию очистки вверх или вниз по иерархии. Но счетчик ссылок объекта уже сбросился до нуля, поэтому QI тоже считается срабатыванием счетчика ссылок.

Это обновление позволяет предотвращать ложные срабатывания счетчика ссылок, гарантируя, что после сбрасывания до нуля его больше невозможно будет восстановить. Так вы сможете выполнять QI для любого временного объекта, который требуется во время уничтожения. Эта процедура неизбежна в некоторых приложениях/элементах управления XAML, и C++/WinRT теперь устойчив к ней.

Вы можете отложить уничтожение, указав статическую функцию final_release в типе реализации. Последний оставшийся указатель на объект в виде std::unique_ptr передается в final_release. После этого вы можете передать владение таким указателем другому контексту. Вы можете использовать метод QI с указателем без вызова двойного уничтожения. Но во время уничтожения объекта чистое изменение счетчика ссылок должно быть равно нулю.

final_release может возвращать значение void. Это объект асинхронной операции, например IAsyncAction или winrt::fire_and_forget.

struct Sample : implements<Sample, IStringable>
{
    hstring ToString()
    {
        return L"Sample";
    }

    ~Sample()
    {
        // Called when the unique_ptr below is reset.
    }

    static void final_release(std::unique_ptr<Sample> self) noexcept
    {
        // Move 'self' as needed to delay destruction.
    }
};

В приведенном ниже примере после освобождения MainPage (для конечного времени) вызывается final_release. Эта функция тратит пять секунд на ожидание (в пуле потоков), а затем возобновляет работу, используя Диспетчер страницы (для работы которого требуется QI/AddRef/Release). Затем она удаляет ресурс в таком потоке пользовательского интерфейса. Наконец, она очищает unique_ptr, что приводит к фактическому вызову деструктора MainPage. Даже в таком деструкторе вызывается DataContext, который требует выполнения QI для IFrameworkElement.

Вам не нужно реализовывать final_release в качестве сопрограммы. Но это работает и позволяет очень просто перенести уничтожение в другой поток, что и происходит в этом примере.

struct MainPage : PageT<MainPage>
{
    MainPage()
    {
    }

    ~MainPage()
    {
        DataContext(nullptr);
    }

    static IAsyncAction final_release(std::unique_ptr<MainPage> self)
    {
        co_await 5s;

        co_await resume_foreground(self->Dispatcher());
        co_await self->resource.CloseAsync();

        // The object is destructed normally at the end of final_release,
        // when the std::unique_ptr<MyClass> destructs. If you want to destruct
        // the object earlier than that, then you can set *self* to `nullptr`.
        self = nullptr;
    }
};

Дополнительные сведения см. в разделе Отложенное удаление.

Улучшено поддержку единого интерфейса наследования в стиле COM

Как и для программирования среды выполнения Windows, C++/WinRT также используется для разработки и использования интерфейсов API только для COM. Это обновление позволяет реализовать COM-сервер, где существует иерархия интерфейса. Это не требуется для среды выполнения Windows; но необходимо для некоторых реализаций COM.

Правильная обработка параметров out

Может быть сложно работать с параметрами out; особенно массивами среды выполнения Windows. С этим обновлением C++/WinRT значительно более надежный и устойчив к ошибкам, когда речь идет о параметрах и массивах out; не имеет значения, поступают ли эти параметры через языковую проекцию или от разработчика COM, который использует необработанный ABI и который совершает ошибку, не согласовывая переменные последовательно. В любом случае C++/WinRT теперь поступает правильно, когда дело доходит до передачи проектируемых типов в ABI (не забывая высвобождать любые ресурсы) и когда речь идет об обнулении или очистке параметров, поступающих строку ABI.

События теперь надежно обрабатывают недопустимые токены

Реализация winrt::event теперь корректно обрабатывает регистр, когда его метод remove вызывается с недопустимым значением токена (значением, которого нет в массиве).

Локальные переменные сопрограммы теперь уничтожаются до возвращения сопрограммы

Традиционный способ реализации типа сопрограммы может позволить уничтожение локальных переменных внутри сопрограммы после возврата или завершения выполнения сопрограммы (а не до окончательной приостановки). Возобновление работы любого официанта теперь отложено до окончательного приостановления, чтобы избежать этой проблемы и получить другие преимущества.

Новости и изменения в пакете SDK для Windows версии 10.0.17763.0 (Windows 10, версия 1809)

Таблица ниже содержит новости и изменения для C++/WinRT в пакете SDK для Windows версии 10.0.17763.0 (Windows 10, версия 1809).

Новые или измененные возможности Дополнительная информация
Критическое изменение. Для его компиляции C++/WinRT не зависит от заголовков из пакета SDK для Windows. См. статью Изоляция из файлов заголовков пакета SDK для Windows ниже.
Формат системы проекта Visual Studio изменился. См. статью Как перенастроить проект C++/WinRT на более позднюю версию пакета SDK для Windows ниже.
Существуют новые функции и базовые классы, которые помогут передать объект коллекции в функцию среды выполнения Windows или реализовать собственные свойства и типы коллекций. См. раздел Коллекции из C++/WinRT.
Вы можете использовать расширение разметки {Binding} со своими классами среды выполнения C++/WinRT. Дополнительные сведения и примеры кода см. в разделе Общие сведения о привязке данных.
Поддержка отмены корутин позволяет зарегистрировать обратный вызов отмены. Дополнительные сведения и примеры кода см. в разделе "Отмена асинхронной операции" и обратных вызовов отмены.
При создании делегата, указывающего на функцию-член, можно установить сильную или слабую ссылку на текущий объект (вместо необработанного этого указателя) в точке регистрации обработчика. Дополнительные сведения и примеры кода см. в подразделе Если вы используете функцию-член в качестве подраздела делегата раздела Безопасный доступ кэтому указателю с делегатом с обработкой событий.
Исправлены ошибки, которые были обнаружены улучшенным соответствием Visual Studio стандарту C++. Набор инструментов LLVM и Clang также лучше используется для проверки соответствия стандартам C++/WinRT. Вы больше не столкнетесь с проблемой, описанной в разделе "Почему не будет компилировать новый проект?" Я использую Visual Studio 2017 (версия 15.8.0 или более поздняя) и пакет SDK версии 17134

Прочие изменения.

  • Критическое изменение. winrt::get_abi(winrt::hstring const&) теперь возвращает void* вместо HSTRING. Вы можете использовать static_cast<HSTRING>(get_abi(my_hstring));, чтобы получить HSTRING. См. раздел Взаимодействие с HSTRING ABI.
  • Критическое изменение. winrt::put_abi(winrt::hstring&) теперь возвращает void** вместо HSTRING*. Вы можете использовать reinterpret_cast<HSTRING*>(put_abi(my_hstring));, чтобы получить HSTRING*. См. раздел Взаимодействие с HSTRING ABI.
  • Критическое изменение. HRESULT теперь проецируется как winrt::hresult. Если нужен HRESULT (для проверки типов или для поддержки характеристик типов), вы можете использовать static_cast для winrt::hresult. В противном случае winrt::hresult преобразуется в HRESULT, если вы включите unknwn.h до включения любых заголовков C++/WinRT.
  • Критическое изменение. GUID теперь проецируется как winrt::guid. Для API-интерфейсов, которые вы реализуете, необходимо использовать winrt::guid для параметров GUID. В противном случае winrt::guid преобразуется в GUID, если вы включите unknwn.h до включения любых заголовков C++/WinRT. См. раздел Взаимодействие со структурой GUID ABI.
  • Критическое изменение. Мы защитили конструктор winrt::handle_type constructor, сделав его явным (теперь с ним сложнее написать неправильный код). Если необходимо присвоить необработанное значение дескриптора, вместо этого вызовите функцию handle_type::attach function.
  • Критическое изменение. Цифровые подписи WINRT_CanUnloadNow и WINRT_GetActivationFactory были изменены. Эти функции не нужно объявлять вообще. Вместо этого добавьте winrt/base.h (который автоматически включается, когда вы включаете любые файлы заголовков пространства имен C++/WinRT Windows), чтобы включить объявления этих функций.
  • Для winrt::clock struct, from_FILETIME/to_FILETIME не рекомендуется вместо from_file_time/to_file_time.
  • Упрощены интерфейсы API, которые ожидают параметры IBuffer. Большинство интерфейсов API предпочитает коллекции, или массивы. Но нам показалось, что нужно упростить вызов интерфейсов API, использующих IBuffer. Это обновление обеспечивает прямой доступ к данным в реализации IBuffer. В нем используется то же соглашение об именовании данных, что и для контейнеров стандартных библиотек C++. Это соглашение также позволяет избежать противоречащих друг другу имен метаданных, которые обычно начинаются с заглавной буквы.
  • Улучшенное создание кода: различные улучшения для уменьшения размера кода, улучшения встраивания и оптимизации кэширования фабрики.
  • Убрана лишняя рекурсия. Когда командная строка ссылается на папку, а не на определенный .winmd, инструмент cppwinrt.exe больше не выполняет рекурсивный поиск файлов .winmd. Инструмент cppwinrt.exe теперь также более интеллектуально обрабатывает дубликаты, что делает его более устойчивым к ошибкам пользователя и неправильно сформированным файлам .winmd.
  • Защищенные интеллектуальные указатели. Раньше средству аннулирования событий не удавалось выполнить аннулирование во время присваивания и переноса нового значения. Это помогло выявить проблему, при которой классы интеллектуальных указателей не всегда надежно обрабатывали самоназначение; она коренится в шаблоне структуры winrt::com_ptr. Исправлен winrt:: com_ptr, а также обработчики событий для правильного обрабатывания семантики перемещения, чтобы они отзывались при назначении.

Важно!

Важные изменения были внесены в расширение C++/WinRT Visual Studio (VSIX), как в версии 1.0.181002.2, так и позже в версии 1.0.190128.4. Подробные сведения об этих изменениях и о том, как они влияют на существующие проекты, Поддержка Visual Studio для C++/WinRT и более ранних версий расширения VSIX.

Изоляция из файлов заголовков пакета SDK для Windows

Это — потенциально критическое изменение для кода.

Для его компиляции C++/WinRT больше не зависит от файлов заголовков из пакета SDK для Windows. Файлы заголовков в библиотеке среды выполнения C (CRT) и стандартной библиотеке шаблонов C++ (STL) также не содержат заголовков пакета SDK для Windows. Это улучшает соответствие стандартам, позволяет избежать непреднамеренных зависимостей и значительно уменьшает количество макросов, от которых вы должны защищаться.

Эта независимость означает, что C++/WinRT теперь стал более переносным и совместимым со стандартами, а это расширяет возможности его превращения в кросс-компилятор и кроссплатформенную библиотеку. Это также означает, что заголовки C++/WinRT не оказывают негативного влияния на макросы.

Если вы ранее оставили C++/WinRT для включения любых заголовков Windows в свой проект, то теперь нужно будет включить их самостоятельно. При таких обстоятельствах всегда лучше явно включать заголовки, от которых вы зависите, и не оставлять их для включения в другой библиотеке.

В настоящее время единственными исключениями для изоляции файла заголовка пакета SDK для Windows являются встроенные функции и числовые значения. Нет никаких известных проблем с этими последними оставшимися зависимостями.

В случае необходимости вы можете повторно включить в проекте взаимодействие с заголовками пакета SDK для Windows. Например, вы можете захотеть реализовать интерфейс COM (с корнем в IUnknown). Для этого примера добавьте unknwn.h перед тем, как включать заголовки C++/WinRT. Это приводит к тому, что базовая библиотека C++/WinRT позволяет различным обработчикам поддерживать классические интерфейсы COM. Пример кода см. в разделе Создание компонентов COM с помощью C++/WinRT. Точно так же явно включите любые другие заголовки пакета SDK для Windows, которые объявляют типы и/или функции, которые нужно вызвать.

Как изменить целевую платформу на C++/WinRT проекта до более поздней версии пакета SDK для Windows

Метод перенаправления вашего проекта, который может привести к наименьшему количеству проблем с компилятором и компоновщиком, также является наиболее трудоемким. Этот метод включает создание нового проекта (нацеленного на выбранную версию пакета SDK для Windows), а затем копирование файлов в новый проект из старого. Разделы старых файлов .vcxproj и .vcxproj.filters можно просто скопировать, чтобы сохранить добавленные файлы в Visual Studio.

Однако есть два других способа перенаправления проекта в Visual Studio.

  • Перейдите к свойству проекта Общие>Версия пакета SDK для Windows и выберите Все конфигурации и Все платформы. Установите версию пакета SDK для Windows на версию, на которую вы хотите перенаправить.
  • В Обозревателе решений щелкните правой кнопкой мыши узел проекта, выберите Перенаправление проектов, выберите версию (версии), на которую вы хотите перенаправить, а затем нажмите кнопку ОК.

Если после использования любого из этих двух методов вы обнаружите какие-либо ошибки компилятора или компоновщика, можете попробовать очистить решение (Выполнять сборку>Очистить решение и/или вручную удалить все временные папки и файлы), прежде чем пытаться выполнить сборку снова.

Если компилятор C++ будет создавать сообщение "Ошибка C2039: 'IUnknown': не является членом '`глобального пространства имен''", добавьте #include <unknwn.h> в начало вашего pch.h файла (перед добавлением заголовков C++/WinRT).

После этого вам также может понадобиться добавить #include <hstring.h>.

Компоновщик C++ будет создавать сообщение "Ошибка LNK2019: неразрешенный внешний символ _WINRT_CanUnloadNow@0, на который ссылается функция _VSDesignerCanUnloadNow@0", вы можете решить эту проблему, добавив #define _VSDESIGNER_DONT_LOAD_AS_DLL в свой файл pch.h.