Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Перед чтением этой статьи вам потребуются сведения в разделе "Переход на C++/WinRT" из C++/CX. В этом разделе представлены два основных варианта стратегии переноса проекта C++/CX в C++/WinRT.
- Перенесите весь проект за один проход. Самый простой вариант для проекта, который не слишком велик. Если у вас есть проект компонента среда выполнения Windows, эта стратегия является единственным вариантом.
- Постепенно переносите проект (размер или сложность базы кода может сделать это необходимым). Но эта стратегия призывает вас следовать процессу переноса, в котором код C++/CX и C++/WinRT существует параллельно в одном проекте. Для проекта XAML в каждый момент времени все типы XAML-страниц должны быть либо C++/WinRT, либо C++/CX.
Этот раздел о взаимодействии относится к той второй стратегии — в тех случаях, когда необходимо переносить проект постепенно. В этом разделе показаны различные формы пар вспомогательных функций, которые можно использовать для преобразования объекта C++/CX (и других типов) в объект C++/WinRT (и наоборот) в одном проекте.
Эти вспомогательные функции будут очень полезны при переносе кода с C++/CX на C++/WinRT. Или вы можете просто использовать проекции языка C++/WinRT и C++/CX в одном проекте, независимо от того, переносите ли вы или нет, и используйте эти вспомогательные функции для взаимодействия между двумя.
После ознакомления с этим разделом, чтобы получить сведения и примеры кода о том, как одновременно поддерживать задачи PPL и корутины в одном проекте (например, вызывать корутины из цепочек задач), см. более сложный раздел Асинхронность и взаимодействие между C++/WinRT и C++/CX.
Функции from_cx и to_cx
Ниже приведен листинг исходного кода файла заголовка с именем interop_helpers.h, содержащего различные вспомогательные функции преобразования. По мере постепенного переноса проекта некоторые его части по-прежнему будут оставаться на C++/CX, а некоторые вы уже перенесёте на C++/WinRT. Эти вспомогательные функции можно использовать для преобразования объектов (и других типов) в C++/CX и C++/WinRT в проекте в точках границ между этими двумя частями.
В разделах, которые следуют за описанием кода, описываются вспомогательные функции, а также как создать и использовать файл заголовка в проекте.
// interop_helpers.h
#pragma once
template <typename T>
T from_cx(Platform::Object^ from)
{
T to{ nullptr };
if (from != nullptr)
{
winrt::check_hresult(reinterpret_cast<::IUnknown*>(from)
->QueryInterface(winrt::guid_of<T>(), winrt::put_abi(to)));
}
return to;
}
template <typename T>
T^ to_cx(winrt::Windows::Foundation::IUnknown const& from)
{
return safe_cast<T^>(reinterpret_cast<Platform::Object^>(winrt::get_abi(from)));
}
inline winrt::hstring from_cx(Platform::String^ const& from)
{
return reinterpret_cast<winrt::hstring&>(const_cast<Platform::String^&>(from));
}
inline Platform::String^ to_cx(winrt::hstring const& from)
{
return reinterpret_cast<Platform::String^&>(const_cast<winrt::hstring&>(from));
}
inline winrt::guid from_cx(Platform::Guid const& from)
{
return reinterpret_cast<winrt::guid&>(const_cast<Platform::Guid&>(from));
}
inline Platform::Guid to_cx(winrt::guid const& from)
{
return reinterpret_cast<Platform::Guid&>(const_cast<winrt::guid&>(from));
}
Функция from_cx
Вспомогательный компонент from_cx преобразует объект C++/CX в эквивалентный объект C++/WinRT. Функция приводит объект C++/CX к базовому указателю интерфейса IUnknown . Затем он вызывает QueryInterface на этом указателе, чтобы запросить интерфейс по умолчанию объекта C++/WinRT.
QueryInterface — это эквивалент расширения C++/CXsafe_cast на уровне двоичного интерфейса приложения (ABI) в среда выполнения Windows. И функция winrt::put_abi получает адрес базового указателя на интерфейс IUnknown объекта C++/WinRT, чтобы ему можно было присвоить другое значение.
Функция to_cx
Функция to_cx вспомогательной функции преобразует объект C++/WinRT в эквивалентный объект C++/CX. Функция winrt::get_abi извлекает указатель на базовый интерфейс IUnknown объекта C++/WinRT. Функция выполняет приведение этого указателя к объекту C++/CX, а затем использует расширение C++/CX safe_cast, чтобы запросить требуемый тип C++/CX.
Файл заголовка interop_helpers.h
Чтобы использовать вспомогательные функции в проекте, выполните следующие действия.
- Добавьте в проект новый элемент заголовка (.h) и присвойте ему имя
interop_helpers.h. - Замените содержимое
interop_helpers.hприведённым выше листингом кода. - Добавьте эти директивы include в
pch.h.
// pch.h
...
#include <unknwn.h>
// Include C++/WinRT projected Windows API headers here.
...
#include <interop_helpers.h>
Создание проекта C++/CX и добавление поддержки C++/WinRT
В этом разделе описывается, что делать, если вы решили принять существующий проект C++/CX, добавить в него поддержку C++/WinRT и выполнить перенос. См. также поддержку C++/WinRT в Visual Studio.
Чтобы смешивать C++/CX и C++/WinRT в проекте C++/CX, включая использование вспомогательных функций from_cx и to_cx в проекте, необходимо вручную добавить в проект поддержку C++/WinRT.
Сначала откройте свой проект C++/CX в Visual Studio и убедитесь, что для свойства проекта Общие>Версия целевой платформы установлено значение 10.0.17134.0 (Windows 10, версия 1803) или выше.
Установка пакета NuGet C++/WinRT
Пакет NuGet Microsoft.Windows.CppWinRT предоставляет поддержку сборки C++/WinRT, включая свойства и целевые файлы MSBuild. Чтобы установить его, выберите пункт меню Project>Manage NuGet Packages...>Browse, введите или вставьте Microsoft.Windows.CppWinRT в поле поиска, выберите элемент в результатах поиска, а затем нажмите Install, чтобы установить пакет для этого проекта.
Это важно
Установка пакета NuGet C++/WinRT приводит к отключению поддержки C++/CX в проекте. Если вы собираетесь выполнить перенос за один проход, то лучше оставить эту поддержку отключённой, чтобы сообщения сборки помогали вам найти (и перенести) все ваши зависимости от C++/CX, тем самым со временем превратив проект, изначально полностью написанный на C++/CX, в проект, полностью написанный на C++/WinRT. Но ознакомьтесь со следующим разделом для получения сведений о том, как включить его.
Снова включите поддержку C++/CX
Если вы выполняете перенос за один проход, то делать это не нужно. Но если вам нужно выполнять перенос постепенно, то на этом этапе вам потребуется снова включить поддержку C++/CX в своем проекте. В свойствах проекта C/C++>Общие>Использовать расширение среды выполнения Windows>Да (/ZW)).
Либо (или, для проекта XAML, дополнительно) можно добавить поддержку C++/CX с помощью страницы свойств проекта C++/WinRT в Visual Studio. В свойствах проекта Общие свойства>C++/WinRT>Язык проекта>C++/CX. Это добавит в файл следующее свойство .vcxproj .
<PropertyGroup Label="Globals">
<CppWinRTProjectLanguage>C++/CX</CppWinRTProjectLanguage>
</PropertyGroup>
Это важно
Если вам нужно выполнить сборку, чтобы обработать содержимое файла Midl (.idl) в файлы-заглушки, вам потребуется изменить Язык проекта обратно на C++/WinRT. После того как сборка сгенерирует эти заглушки, снова измените Язык проекта на C++/CX.
Список аналогичных параметров настройки, которые тонко настраивают поведение средства cppwinrt.exe, см. в файле readme пакета NuGet Microsoft.Windows.CppWinRT.
Подключите файлы заголовков C++/WinRT
Как минимум вам следует включить winrt/base.h в файл предварительно компилируемого заголовка (обычно pch.h), как показано ниже.
// pch.h
...
#include <winrt/base.h>
...
Но вам почти наверняка потребуются типы из пространства имен winrt::Windows::Foundation. И, возможно, вам уже известны другие пространства имен, которые вам понадобятся. Поэтому включите проецируемые заголовки API Windows C++/WinRT, соответствующие этим пространствам имён, вот так (сейчас вам не нужно явно включать winrt/base.h, так как он будет включён автоматически).
// pch.h
...
#include <winrt/Windows.Foundation.h>
// Include any other C++/WinRT projected Windows API headers here.
...
См. также пример кода в следующем разделе (использование проекта C++/WinRT и добавление поддержки C++/CX), где показан прием использования псевдонимов пространств имен namespace cx и namespace winrt. Этот метод позволяет справиться с иначе потенциальными столкновениями пространства имен между проекцией C++/WinRT и проекцией C++/CX.
Добавьте interop_helpers.h в проект
Теперь вы сможете добавить функции from_cx и to_cx в проект C++/CX. Инструкции о том, как это сделать, см. в приведённом выше разделе from_cx и to_cx функций.
Добавление поддержки C++/CX в проект C++/WinRT
В этом разделе описывается, что делать, если вы решили создать проект C++/WinRT и выполнить перенос там.
Чтобы смешать C++/WinRT и C++/CX в проекте C++/WinRT, включая использование вспомогательных функций from_cx и to_cx в проекте, необходимо вручную добавить поддержку C++/CX в проект.
- Создайте проект C++/WinRT в Visual Studio с помощью одного из шаблонов проектов C++/WinRT (см. Visual Studio поддержку C++/WinRT).
- Включите поддержку проекта для C++/CX. В свойствах проекта C/C++>Общие>Использовать расширение среды выполнения Windows>Да (/ZW).
Пример проекта C++/WinRT с двумя вспомогательными функциями, используемыми
В этом разделе вы можете создать пример проекта C++/WinRT, демонстрирующий использование from_cx и to_cx. Также показано, как использовать псевдонимы пространства имен для различных участков кода, чтобы избежать потенциальных конфликтов имен между проекцией C++/WinRT и проекцией C++/CX.
- Создайте проект Visual C++>Windows Universal>Core App (C++/WinRT).
- В свойствах проекта: C/C++>Общие>Использовать расширение среды выполнения Windows>Да (/ZW).
- Добавьте
interop_helpers.hв проект. Инструкции о том, как это сделать, см. в приведённом выше разделе о функциях from_cx и to_cx. - Замените содержимое
App.cppкодом из листинга ниже. - Выполните сборку и запустите.
WINRT_ASSERT — это определение макроса, и оно расширяется до _ASSERTE.
// App.cpp
#include "pch.h"
#include <sstream>
namespace cx
{
using namespace Windows::Foundation;
}
namespace winrt
{
using namespace Windows;
using namespace Windows::ApplicationModel::Core;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Numerics;
using namespace Windows::UI;
using namespace Windows::UI::Core;
using namespace Windows::UI::Composition;
}
struct App : winrt::implements<App, winrt::IFrameworkViewSource, winrt::IFrameworkView>
{
winrt::CompositionTarget m_target{ nullptr };
winrt::VisualCollection m_visuals{ nullptr };
winrt::Visual m_selected{ nullptr };
winrt::float2 m_offset{};
winrt::IFrameworkView CreateView()
{
return *this;
}
void Initialize(winrt::CoreApplicationView const &)
{
}
void Load(winrt::hstring const&)
{
}
void Uninitialize()
{
}
void Run()
{
winrt::CoreWindow window = winrt::CoreWindow::GetForCurrentThread();
window.Activate();
winrt::CoreDispatcher dispatcher = window.Dispatcher();
dispatcher.ProcessEvents(winrt::CoreProcessEventsOption::ProcessUntilQuit);
}
void SetWindow(winrt::CoreWindow const & window)
{
winrt::Compositor compositor;
winrt::ContainerVisual root = compositor.CreateContainerVisual();
m_target = compositor.CreateTargetForCurrentView();
m_target.Root(root);
m_visuals = root.Children();
window.PointerPressed({ this, &App::OnPointerPressed });
window.PointerMoved({ this, &App::OnPointerMoved });
window.PointerReleased([&](auto && ...)
{
m_selected = nullptr;
});
}
void OnPointerPressed(IInspectable const &, winrt::PointerEventArgs const & args)
{
winrt::float2 const point = args.CurrentPoint().Position();
for (winrt::Visual visual : m_visuals)
{
winrt::float3 const offset = visual.Offset();
winrt::float2 const size = visual.Size();
if (point.x >= offset.x &&
point.x < offset.x + size.x &&
point.y >= offset.y &&
point.y < offset.y + size.y)
{
m_selected = visual;
m_offset.x = offset.x - point.x;
m_offset.y = offset.y - point.y;
}
}
if (m_selected)
{
m_visuals.Remove(m_selected);
m_visuals.InsertAtTop(m_selected);
}
else
{
AddVisual(point);
}
}
void OnPointerMoved(IInspectable const &, winrt::PointerEventArgs const & args)
{
if (m_selected)
{
winrt::float2 const point = args.CurrentPoint().Position();
m_selected.Offset(
{
point.x + m_offset.x,
point.y + m_offset.y,
0.0f
});
}
}
void AddVisual(winrt::float2 const point)
{
winrt::Compositor compositor = m_visuals.Compositor();
winrt::SpriteVisual visual = compositor.CreateSpriteVisual();
static winrt::Color colors[] =
{
{ 0xDC, 0x5B, 0x9B, 0xD5 },
{ 0xDC, 0xED, 0x7D, 0x31 },
{ 0xDC, 0x70, 0xAD, 0x47 },
{ 0xDC, 0xFF, 0xC0, 0x00 }
};
static unsigned last = 0;
unsigned const next = ++last % _countof(colors);
visual.Brush(compositor.CreateColorBrush(colors[next]));
float const BlockSize = 100.0f;
visual.Size(
{
BlockSize,
BlockSize
});
visual.Offset(
{
point.x - BlockSize / 2.0f,
point.y - BlockSize / 2.0f,
0.0f,
});
m_visuals.InsertAtTop(visual);
m_selected = visual;
m_offset.x = -BlockSize / 2.0f;
m_offset.y = -BlockSize / 2.0f;
}
};
int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
winrt::init_apartment();
winrt::Uri uri(L"http://aka.ms/cppwinrt");
std::wstringstream wstringstream;
wstringstream << L"C++/WinRT: " << uri.Domain().c_str() << std::endl;
// Convert from a C++/WinRT type to a C++/CX type.
cx::Uri^ cx = to_cx<cx::Uri>(uri);
wstringstream << L"C++/CX: " << cx->Domain->Data() << std::endl;
::OutputDebugString(wstringstream.str().c_str());
// Convert from a C++/CX type to a C++/WinRT type.
winrt::Uri uri_from_cx = from_cx<winrt::Uri>(cx);
WINRT_ASSERT(uri.Domain() == uri_from_cx.Domain());
WINRT_ASSERT(uri == uri_from_cx);
winrt::CoreApplication::Run(winrt::make<App>());
}
Важные API
Связанные темы
Windows developer