Ответы на вопросы, которые у вас, вероятно, возникнут при создании и использовании API среда выполнения Windows с помощью C++/WinRT.
Это важно
Сведения о выпуске C++/WinRT см. в статье «Новости и изменения в C++/WinRT 2.0».
Note
Если ваш вопрос связан с сообщением об ошибке, которое вы видели, см. также раздел "Устранение неполадок C++/WinRT ".
Где можно найти примеры приложений C++/WinRT?
Как перенацелить проект C++/WinRT на более позднюю версию пакета SDK Windows?
Почему новый проект не компилируется, теперь, когда я перешел в C++/WinRT 2.0?
Полный набор изменений (включая критические изменения), см. в статье "Новости" и изменения в C++/WinRT 2.0. Например, если вы используете конструкцию на основе диапазона for для коллекции среда выполнения Windows, то теперь вам потребуется #include <winrt/Windows.Foundation.Collections.h>.
Почему новый проект не компилируется? Я использую Visual Studio 2017 (версия 15.8.0 или более поздней) и пакет SDK версии 17134
Если вы используете Visual Studio 2017 (версии 15.8.0 или более поздней) и Windows SDK версии 10.0.17134.0 (Windows 10, версия 1803), то вновь созданный проект C++/WinRT может не скомпилироваться с ошибкой «error C3861: 'from_abi': identifier not found» и другими ошибками, возникающими в base.h. Решение состоит в том, чтобы либо выбрать более позднюю (лучше соответствующую стандарту) версию Windows SDK, либо установить для свойства проекта C/C++>Language>Conformance mode: No (также, если /permissive- указан в свойстве проекта C/C++>Command Line в разделе Additional Options, удалите его).
Как исправить ошибку сборки: "VSIX для C++/WinRT больше не предоставляет поддержку сборки проекта. Добавьте ссылку на проект для NuGet-пакета Microsoft.Windows.CppWinRT?
Установите пакет NuGet Microsoft.Windows.CppWinRT в ваш проект. Дополнительные сведения см. в более ранних версиях расширения VSIX.
Как настроить поддержку сборки в пакете NuGet?
Поддержка сборки C++/WinRT (props/targets) описана в файле readme пакета NuGet Microsoft.Windows.CppWinRT.
Каковы требования расширения C++/WinRT Visual Studio (VSIX)?
Начиная с версии 1.0.190128.4 расширения VSIX и более поздних версий см. поддержку C++/WinRT в Visual Studio. Другие версии см. в более ранних версиях расширения VSIX.
Что такое класс среды выполнения?
Класс среды выполнения — это тип, который можно активировать и использовать с помощью современных интерфейсов COM, как правило, в пределах исполняемого файла. Однако класс среды выполнения также можно использовать в модуле компиляции, реализующей ее. Класс среды выполнения объявляется в языке определения интерфейса (IDL), и его можно реализовать в стандартном C++ с помощью C++/WinRT.
Что означает проецируемый тип и тип реализации?
Если вы используете только класс среда выполнения Windows (класс среды выполнения), вы будете иметь дело исключительно с проецируемыми типами. C++/WinRT — это проекция языка, поэтому проецируемые типы являются частью поверхности среда выполнения Windows, проецируемых в C++ с помощью C++/WinRT. Дополнительные сведения см. в статье "Использование API с помощью C++/WinRT".
Тип реализации содержит реализацию класса среды выполнения, поэтому он доступен только в проекте, который реализует класс среды выполнения. При работе с проектом, реализующим классы среды выполнения (проект компонента среда выполнения Windows или проект, использующий пользовательский интерфейс XAML), важно быть комфортно с различием между типом реализации для класса среды выполнения и проецируемым типом, который представляет класс среды выполнения, проецируемый в C++/WinRT. Дополнительные сведения см. в статье "Создание API с помощью C++/WinRT".
Нужно ли объявить конструктор в IDL класса среды выполнения?
Только если класс времени выполнения предназначен для использования за пределами содержащей его единицы компиляции (то есть это компонент среда выполнения Windows, предназначенный для широкого использования клиентскими приложениями среда выполнения Windows). Полные сведения о назначении и последствиях объявления конструкторов в IDL см. в разделе Конструкторы классов среды выполнения.
Почему компилятор выдаёт ошибку "C3779: consume_Something: функцию, возвращающую 'auto', нельзя использовать до её определения"?
Вы используете объект среда выполнения Windows, предварительно не включив заголовочный файл соответствующего пространства имён. Включите заголовок с именем пространства имен API и перестройте его. Дополнительные сведения см. в заголовках проекции C++/WinRT.
Почему компоновщик дает мне ошибку "LNK2019: неразрешенный внешний символ"?
Если неразрешённый символ является свободной функцией среда выполнения Windows, например RoInitialize, то вам потребуется явно подключить при компоновке зонтичную библиотеку WindowsApp.lib в вашем проекте. Проекция C++/WinRT зависит от некоторых таких свободных функций (не являющихся членами класса) и точек входа. Если для вашего приложения используется один из шаблонов проектов C++/WinRT Visual Studio Extension (VSIX), то WindowsApp.lib подключается автоматически. В противном случае можно использовать параметры ссылки проекта, чтобы включить его или сделать это в исходном коде.
#pragma comment(lib, "windowsapp")
Важно устранить все возможные ошибки компоновщика, подключив WindowsApp.lib вместо альтернативной библиотеки для статической компоновки; в противном случае ваше приложение не пройдёт тесты приложение для Windows Certification Kit, которые Visual Studio и Microsoft Store используют для проверки отправляемых приложений (то есть в результате ваше приложение не сможет быть успешно принято в Microsoft Store).
Если неразрешенный символ является конструктором, возможно, вы забыли включить файл заголовка пространства имен для создаваемого класса. Включите заголовок с именем пространства имен класса и перестройте. Дополнительные сведения см. в заголовках проекции C++/WinRT.
Почему я получаю исключение "класс не зарегистрирован"?
В этом случае симптом таков: при создании класса среды выполнения или при обращении к статическому члену во время выполнения возникает исключение с кодом HRESULT REGDB_E_CLASSNOTREGISTERED.
Одной из причин может быть то, что компонент среда выполнения Windows не может быть загружен. Убедитесь, что файл метаданных компонента среда выполнения Windows (.winmd) имеет то же имя, что и двоичный файл компонента (the.dll), который также является именем проекта и именем корневого пространства имен. Кроме того, убедитесь, что метаданные среда выполнения Windows и двоичные файлы были правильно скопированы процессом сборки в папку Appx используемого приложения. И убедитесь, что потребляемое приложение AppxManifest.xml (также в Appx папке) содержит <элемент InProcessServer> правильно объявляя активируемый класс и двоичное имя.
Единообразная конструкция Эта ошибка также может произойти, если вы пытаетесь создать экземпляр локально реализованного класса среды выполнения с помощью любого конструктора проецируемого типа (кроме конструктора std::nullptr_t ). Для этого вам потребуется функция C++/WinRT 2.0, которая часто называется единообразной конструкцией. Если вы хотите принять участие в этой функции, дополнительные сведения и примеры кода см. в статье "Согласие на унифицированную конструкцию" и прямой доступ к реализации.
О том, как создавать экземпляры локально реализованных классов среды выполнения, не требующий единообразного конструктора, см. Элементы управления XAML; привязка к свойству C++/WinRT.
Следует ли реализовать Windows::Foundation::IClosable и, если да, как?
Если у вас есть класс времени выполнения, который освобождает ресурсы в своём деструкторе, и этот класс времени выполнения предназначен для использования за пределами единицы компиляции, в которой он реализован (то есть это компонент среда выполнения Windows, предназначенный для общего использования клиентскими приложениями среда выполнения Windows), то мы рекомендуем также реализовать IClosable, чтобы поддержать использование вашего класса времени выполнения языками без детерминированной финализации. Убедитесь, что ресурсы освобождаются независимо от того, вызывается ли деструктор, IClosable::Close или и то и другое. IClosable::Close может вызываться произвольное количество раз.
Нужно ли вызывать IClosable::Close для классов среды выполнения, которые я использую?
IClosable существует для поддержки языков, которые не имеют детерминированной финализации. Таким образом, в целом не нужно вызывать IClosable::Close from C++/WinRT. Но рассмотрим эти исключения для этого общего правила.
- Существует очень редкие случаи, связанные с расами завершения работы или полу смертельные объятия, где вам нужно вызвать IClosable::Close. Если вы используете, например, типы Windows.UI.Composition, то можете столкнуться с ситуациями, когда потребуется освобождать объекты в определённой последовательности, а не полагаться на то, что уничтожение оболочки C++/WinRT сделает это за вас.
- Если вы не можете гарантировать, что у вас есть последняя оставшаяся ссылка на объект (так как вы передали его другим API, которые могут хранить ссылку), то вызов IClosable::Close является хорошей идеей.
- Если есть сомнения, безопаснее вызвать IClosable::Close вручную, чем ждать, пока обёртка вызовет этот метод при уничтожении.
Таким образом, если вы знаете, что у вас есть последняя ссылка, вы можете позволить деструктору оболочки выполнить эту работу. Если вам нужно закрыть до того, как последняя ссылка исчезнет, необходимо вызвать close. Чтобы обеспечить безопасность при исключениях, следует обернуть Close в тип, реализующий идиому RAII (получение ресурса есть инициализация), чтобы закрытие происходило при раскрутке стека. C++/WinRT не имеет unique_close оболочку, но вы можете сделать свою собственную.
Можно ли использовать LLVM/Clang для компиляции с помощью C++/WinRT?
Мы не поддерживаем цепочку инструментов LLVM и Clang для C++/WinRT, но мы используем ее внутренне для проверки соответствия стандартам C++/WinRT. Например, если вы хотите эмулировать то, что мы делаем внутри, можно попробовать эксперимент, например описанный ниже.
Перейдите на страницу загрузки LLVM, найдите раздел Download LLVM 6.0.0>Pre-Built Binaries и скачайте Clang для Windows (64-бит). Во время установки выберите добавить LLVM в системную переменную PATH, чтобы вы могли вызвать ее из командной строки. В целях этого эксперимента можно игнорировать любые ошибки "Не удалось найти каталог наборов инструментов MSBuild" и /или "сбой установки интеграции MSVC", если вы видите их. Существует множество способов вызова LLVM/Clang; В приведенном ниже примере показан только один способ.
C:\ExperimentWithLLVMClang>type main.cpp
// main.cpp
#pragma comment(lib, "windowsapp")
#pragma comment(lib, "ole32")
#include <winrt/Windows.Foundation.h>
#include <stdio.h>
#include <iostream>
using namespace winrt;
int main()
{
winrt::init_apartment();
Windows::Foundation::Uri rssFeedUri{ L"https://blogs.windows.com/feed" };
std::wcout << rssFeedUri.Domain().c_str() << std::endl;
}
C:\ExperimentWithLLVMClang>clang-cl main.cpp /EHsc /I ..\.. -Xclang -std=c++17 -Xclang -Wno-delete-non-virtual-dtor -o app.exe
C:\ExperimentWithLLVMClang>app
windows.com
Так как C++/WinRT использует функции из стандарта C++17, вам потребуется использовать все флаги компилятора для получения этой поддержки; такие флаги отличаются от одного компилятора к другому.
Visual Studio — это средство разработки, которое мы поддерживаем и рекомендуем для C++/WinRT. См. поддержку C++/WinRT в Visual Studio.
Почему сгенерированная функция реализации свойства, доступного только для чтения, не имеет квалификатора const?
При объявлении свойства только для чтения в MIDL 3.0 можно ожидать, что средство cppwinrt.exe создаст для вас функцию реализации с квалификатором const (функция const рассматривает указатель this как const).
Мы, безусловно, рекомендуем использовать const везде, где это возможно, но сам инструмент cppwinrt.exe не пытается определять, какие функции реализации могут быть объявлены как const, а какие — нет. Вы можете сделать любой из функций реализации констант, как в этом примере.
struct MyStringable : winrt::implements<MyStringable, winrt::Windows::Foundation::IStringable>
{
winrt::hstring ToString() const
{
return L"MyStringable";
}
};
Вы можете удалить этот const квалификатор в ToString , если вы решите, что необходимо изменить некоторое состояние объекта в его реализации. Но сделайте каждую функцию-член либо const, либо неконстантной, но не и той и другой одновременно. Другими словами, не перегружайте функцию реализации для const.
Помимо функций реализации, ещё одно место, где используется const, — это проекции функций среда выполнения Windows. Рассмотрим этот код.
int main()
{
winrt::Windows::Foundation::IStringable s{ winrt::make<MyStringable>() };
auto result{ s.ToString() };
}
Для вызова ToString выше команда Go To Declaration в Visual Studio показывает, что проекция среда выполнения Windows IStringable::ToString в C++/WinRT выглядит следующим образом.
winrt::hstring ToString() const;
Функции в проекции являются константной независимо от того, как вы решили квалифицировать их реализацию. На внутреннем уровне проекция вызывает двоичный интерфейс приложений (ABI), что по сути сводится к вызову через указатель на интерфейс COM. Единственное состояние, с которым взаимодействует спроецированный ToString, — это указатель на интерфейс COM; и ему, разумеется, нет необходимости изменять этот указатель, поэтому функция объявлена как const. Это дает вам уверенность в том, что это ничего не изменит в ссылке IStringable, через которую вы выполняете вызов, и гарантирует, что вы можете вызывать ToString даже при наличии константной ссылки на IStringable.
Поймите, что эти примеры const — это детали реализации проекций и реализаций C++/WinRT; они служат для обеспечения чистоты кода в ваших интересах. Ни в ABI COM, ни в ABI среда выполнения Windows не существует такого понятия, как const (в случае функций-членов).
У вас есть рекомендации по уменьшению размера кода для двоичных файлов C++/WinRT?
При работе с объектами среда выполнения Windows следует избегать приведенного ниже шаблона кодирования, так как это может негативно повлиять на приложение, вызвав более двоичный код, чем необходимо создать.
anobject.b().c().d();
anobject.b().c().e();
anobject.b().c().f();
В среде среда выполнения Windows компилятор не может кешировать ни значение c(), ни интерфейсы, необходимые для каждого метода, вызываемого через косвенное обращение ('.'). Если вы не вмешаетесь, это приведет к большему числу виртуальных вызовов и дополнительным накладным расходам на подсчет ссылок. Приведенный выше шаблон может легко создать в два раза больше кода, чем требуется. Вместо этого по возможности используйте шаблон, показанный ниже. Он создает гораздо меньше кода, и он также может значительно повысить производительность времени выполнения.
auto a{ anobject.b().c() };
a.d();
a.e();
a.f();
Рекомендуемый шаблон, показанный выше, применяется не только к C++/WinRT, но и ко всем среда выполнения Windows проекциям языка.
Как превратить строку в тип (например, для навигации)?
В конце примера кода представления навигации (в основном в C#) есть фрагмент кода C++/WinRT, показывающий, как это сделать.
Как устранить неоднозначность с помощью GetCurrentTime и (или) TRY?
Файл winrt/Windows.UI.Xaml.Media.Animation.h заголовка объявляет метод с именем GetCurrentTime, а windows.h (через winbase.h) определяет макрос с именем GetCurrentTime. Когда эти два конфликтуют, компилятор C++ выдает сообщение "ошибка C4002: слишком много аргументов при вызове макроса, похожего на функцию, GetCurrentTime".
Аналогичным образом winrt/Windows.Globalization.h объявляет метод с именем TRY, а afx.h определяет макрос с именем TRY. При возникновении этих ошибок компилятор C++ создает ошибку C2334: непредвиденные токены, предшествующие "{"; пропускание видимого тела функции".
Чтобы устранить одну или обе проблемы, это можно сделать.
#pragma push_macro("GetCurrentTime")
#pragma push_macro("TRY")
#undef GetCurrentTime
#undef TRY
#include <winrt/include_your_cppwinrt_headers_here.h>
#include <winrt/include_your_cppwinrt_headers_here.h>
#pragma pop_macro("TRY")
#pragma pop_macro("GetCurrentTime")
Как ускорить загрузку символов?
В Visual Studio в меню Сервис>Параметры>Отладка>Символы> установите флажок Загружать только указанные модули. Затем в списке стека можно щелкнуть библиотеки DLL правой кнопкой мыши и загрузить отдельные модули.
Note
Если этот раздел не ответил на ваш вопрос, вы можете найти помощь, перейдя в сообщество разработчиков Visual Studio C++ или используя c++-winrt тег в Stack Overflow.