Другое

Полное руководство: подключение C++/WinRT к приложениям C# WinUI3

Узнайте, как правильно подключать компоненты времени выполнения C++/WinRT к приложениям C# WinUI3 без предупреждений. Полное руководство со ссылками на проекты и устранением неполадок.

Как подключить компонент времени выполнения C++/WinRT к приложению C# на WinUI3

Я потратил много времени, пытаясь заставить это работать, но ничего не получилось. Прямая ссылка на файл .winmd из .NET 5+ невозможна, и я не хочу использовать старые версии .NET. Я также пробовал добавлять компонент среды выполнения Windows сам в качестве ссылки на проект, но после повторного открытия решения Visual Studio отображала значок предупреждения на этой ссылке.

Я тестировал это с каждым возможным типом компонента среды выполнения Windows — компонент среды выполнения Windows (WinUI 3), компонент среды выполнения Windows (C++/WinRT) — но значок предупреждения сохранялся во всех случаях. Я ищу правильное решение (желательно без хаков или обходных путей).

Подключение компонента C++/WinRT к приложению C# WinUI3

Подключение компонента C++/WinRT к приложению C# WinUI3 требует понимания системы проекции Windows Runtime и правильной настройки проекта. Решение включает в себя ссылку на компонент через выходные данные сборки, а не прямое добавление файлов .winmd, а также обеспечение совместимости версий Windows SDK в обоих проектах с использованием правильного метода добавления ссылки.

Содержание

Понимание архитектуры компонента WinRT

Компоненты Windows Runtime (WinRT) служат мостом между различными языками программирования в экосистеме Windows. При создании компонента C++/WinRT генерируются файлы метаданных (.winmd), содержащие информацию о типах, используемых потребляющими языками, такими как C#. Система проекции Windows Runtime автоматически обрабатывает преобразование между типами C++ и их соответствующими представлениями в C#.

Ключевая концепция архитектуры, которую необходимо понять, заключается в том, что компоненты WinRT не ссылаются напрямую на сборки .NET. Вместо этого они компилируются в метаданные Windows Runtime, которые проецируются в потребляющий язык. Это означает, что вы не можете просто добавить файл .winmd в качестве ссылки в проект .NET 5+, поскольку механизм ссылок изменился.

Важно: Файл .winmd содержит определения типов, но фактическая реализация находится в DLL C#. Проекту C# нужны и метаданные, и реализация для корректной работы.

Создание компонента C++/WinRT

Начните с создания правильного проекта компонента C++/WinRT в Visual Studio:

  1. Создайте новый проект

    • Выберите “Компонент среды выполнения Windows (C++/WinRT)” из шаблонов C++
    • Назовите ваш компонент (например, “MyCppComponent”)
    • Выберите расположение и нажмите “Создать”
  2. Реализуйте классы компонента

    cpp
    // MyComponent.h
    #pragma once
    #include "MyComponent.g.h"
    
    namespace winrt::MyComponent::implementation
    {
        struct MyComponent : MyComponentT<MyComponent>
        {
            MyComponent() = default;
    
            int32_t AddNumbers(int32_t a, int32_t b);
            hstring GetString();
        };
    }
    
    namespace winrt::MyComponent::factory_implementation
    {
        struct MyComponent : MyComponentT<MyComponent, implementation::MyComponent>
        {
        };
    }
    
  3. Реализуйте методы

    cpp
    // MyComponent.cpp
    #include "pch.h"
    #include "MyComponent.h"
    #include "MyComponent.g.cpp"
    
    namespace winrt::MyComponent::implementation
    {
        int32_t MyComponent::AddNumbers(int32_t a, int32_t b)
        {
            return a + b;
        }
    
        hstring MyComponent::GetString()
        {
            return L"Hello from C++ WinRT!";
        }
    }
    
  4. Обновите файл проекта
    Убедитесь, что ваш .vcxproj файл содержит необходимые ссылки на Windows SDK:

    xml
    <PropertyGroup>
        <TargetPlatformVersion>10.0.22621.0</TargetPlatformVersion>
        <WindowsTargetPlatformVersion>10.0.22621.0</WindowsTargetPlatformVersion>
    </PropertyGroup>
    

Настройка проекта C# WinUI3

Для вашего проекта C# WinUI3 выполните следующие шаги:

  1. Создайте новый проект WinUI3

    • Выберите шаблон “Пустое приложение, упакованное (WinUI 3 для настольных ПК)”
    • Укажите целевую платформу .NET 6, .NET 7 или .NET 8 (избегайте .NET Framework для WinUI3)
  2. Настройте свойства проекта

    • Убедитесь, что целевая платформа .NET 6/7/8
    • Установите целевую платформу x64 или x86 (совпадающую с вашим компонентом C++)
    • Проверьте совместимость версии Windows SDK с вашим компонентом C++
  3. Добавьте необходимые ссылки на пакеты

    xml
    <ItemGroup>
        <PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.230829000" />
        <PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.755" />
    </ItemGroup>
    

Правильная настройка ссылки на проект

Здесь большинство разработчиков сталкиваются с проблемами. Вот правильный подход:

Метод 1: Ссылка на проект (Рекомендуется)

  1. Сначала соберите компонент C++

    • Щелкните правой кнопкой мыши по проекту C++ → Сборка
    • Убедитесь, что сборка прошла успешно
  2. Правильно добавьте ссылку на проект

    • Щелкните правой кнопкой мыши по вашему проекту C# → Добавить → Ссылка на проект
    • Выберите проект компонента C++ WinRT
    • Не устанавливайте флажок “Копировать локально” для ссылки
  3. Проверьте ссылку

    • Ссылка должна отображаться без значков предупреждений
    • Теперь вы должны иметь возможность использовать классы C++ в коде C#
csharp
// MainPage.xaml.cs
using MyComponent;

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
    }

    private void OnButtonClick(object sender, RoutedEventArgs e)
    {
        var component = new MyComponent();
        int result = component.AddNumbers(5, 3);
        ResultText.Text = $"Результат: {result}";
        
        string message = component.GetString();
        MessageText.Text = message;
    }
}

Метод 2: Ручная настройка ссылки (Когда ссылка на проект не работает)

Если ссылки на проект продолжают показывать предупреждения, вы можете настроить ссылку вручную:

  1. Найдите сгенерированные файлы

    • После сборки проекта C++ посмотрите в папке x64\Debug (или вашей папке конфигурации сборки)
    • Вы должны найти MyComponent.winmd и MyComponent.dll
  2. Скопируйте файлы в проект C#

    • Создайте папку Runtime в вашем проекте C#
    • Скопируйте оба файла (.winmd и .dll) в эту папку
    • Установите для файла .dll параметр “Копировать в выходной каталог” в “Копировать, если новее”
  3. Добавьте ссылку вручную

    • Щелкните правой кнопкой мыши по проекту C# → Добавить → Ссылка
    • Перейдите к файлу .winmd и выберите его

Устранение распространенных проблем

Значок предупреждения на ссылке на проект

Если вы видите значок предупреждения на ссылке на проект:

Возможные причины:

  • Несовместимые версии Windows SDK между проектами
  • Несовпадающие целевые платформы (x86 против x64)
  • Отсутствующие инструменты сборки Windows SDK
  • Поврежденный кэш проекта

Решения:

  1. Очистите и пересоберите все проекты

    • Сборка → Очистить решение
    • Сборка → Пересобрать решение
  2. Проверьте версии SDK

    xml
    <!-- Проект C++ -->
    <TargetPlatformVersion>10.0.22621.0</TargetPlatformVersion>
    
    <!-- Проект C# -->
    <WindowsTargetPlatformVersion>10.0.22621.0</WindowsTargetPlatformVersion>
    
  3. Проверьте архитектуру сборки

    • Оба проекта должны целиться на одну и ту же платформу (рекомендуется x64)
    • Проверьте “Диспетчер конфигураций” → Активная платформа решения

Проблемы со ссылками в .NET 5+

Поскольку .NET 5+ изменил работу со ссылками, вы можете столкнуться с:

  • Отсутствующий файл .winmd: Убедитесь, что проект C++ успешно собран
  • Проблемы разрешения путей: Используйте ссылки на проекты вместо файловых ссылок
  • Совместимость SDK: Используйте совпадающие версии Windows SDK
xml
<!-- В файле .csproj убедитесь, что эти свойства установлены -->
<PropertyGroup>
    <TargetFramework>net8.0-windows10.0.22621.0</TargetFramework>
    <TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
    <UseWinUI>true</UseWinUI>
</PropertyGroup>

Проблемы с разрешением типов

Если вы не можете разрешить типы из компонента C++:

  1. Добавьте директивы using

    csharp
    using MyComponent;
    
  2. Проверьте имена пространств имен

    • Убедитесь, что пространство имен совпадает с именем вашего проекта C++
    • Проверьте правильность регистра имен классов
  3. Пересоберите решение

    • Иногда Visual Studio требуется полная пересборка для обновления кэша типов

Расширенные шаблоны реализации

Реализация асинхронного шаблона

Для асинхронных операций в вашем компоненте C++:

cpp
// Асинхронная операция в C++
IAsync<int32_t> LongOperationAsync()
{
    co_await winrt::resume_after(std::chrono::seconds(2));
    co_return 42;
}
csharp
// Использование в C#
private async void OnAsyncClick(object sender, RoutedEventArgs e)
{
    var component = new MyComponent();
    int result = await component.LongOperationAsyncAsync();
    ResultText.Text = $"Асинхронный результат: {result}";
}

Обработка событий

Реализуйте события в C++ и используйте их в C#:

cpp
// Компонент C++ с событиями
struct MyComponent : MyComponentT<MyComponent>
{
    MyComponent() = default;

    winrt::event_token ValueChanged(winrt::Windows::Foundation::TypedEventHandler<MyComponent, int32_t> const& handler)
    {
        return m_valueChanged.add(handler);
    }

    void ValueChanged(winrt::event_token const& token)
    {
        m_valueChanged.remove(token);
    }

    void SetValue(int32_t value)
    {
        m_value = value;
        m_valueChanged(*this, value);
    }

private:
    winrt::event<winrt::Windows::Foundation::TypedEventHandler<MyComponent, int32_t>> m_valueChanged;
    int32_t m_value{0};
};
csharp
// Использование событий в C#
var component = new MyComponent();
component.ValueChanged += (sender, value) => 
{
    Debug.WriteLine($"Значение изменено на: {value}");
};
component.SetValue(100);

Внедрение зависимостей

Для более сложных приложений рассмотрите внедрение зависимостей:

csharp
// В запуске C#
services.AddSingleton<MyComponent>();

// В конструкторе страницы
public MainPage(MyComponent component)
{
    this.InitializeComponent();
    this.component = component;
}

Заключение

Подключение компонентов C++/WinRT к приложениям C# WinUI3 требует тщательного внимания к настройке проекта и пониманию системы проекции Windows Runtime. Ключевые выводы:

  1. Используйте ссылки на проекты вместо прямых ссылок на файлы .winmd для проектов .NET 5+
  2. Обеспечивайте совместимость между версиями Windows SDK и целевыми платформами
  3. Сначала соберите компонент C++ перед добавлением ссылок в проект C#
  4. Очищайте и пересобирайте все решение при возникновении проблем со ссылками
  5. Избегайте ручного копирования файлов как основного решения - используйте правильные ссылки на проекты

Правильное решение включает создание компонента C++/WinRT с правильным типом проекта, настройку вашего проекта C# WinUI3 с совместимыми версиями SDK и использование диалога “Добавить ссылку на проект” вместо ручного добавления файлов .winmd. Этот подход устраняет значки предупреждений и обеспечивает правильное разрешение типов между двумя языками.

Для сложных сценариев рассмотрите реализацию асинхронных шаблонов, обработки событий и внедрения зависимостей для создания надежных, поддерживаемых приложений, использующих преимущества как C++, так и C#.

Источники

  1. Документация Microsoft - Создание компонента среды выполнения Windows на C++/WinRT
  2. Документация Microsoft - Использование компонентов среды выполнения Windows в C#
  3. Документация Microsoft - Шаблоны проектов WinUI 3
  4. Документация Microsoft - Справочник по среде выполнения Windows
  5. GitHub - Примеры Windows App SDK
Авторы
Проверено модерацией
Модерация