Posted on 31. March 2020

Подключение проекта C ++ / CLI к .NET Core

 

Одной из новых функций Visual Studio 2019 (начиная с версии 16.4) и .NET Core 3.1 является возможность создавать проекты C ++ / CLI, ориентированные на .NET Core. Это можно сделать напрямую с помощью cl.exe и link.exe (с использованием новой опции  /clr:netcore) или с помощью MSBuild (с помощью <CLRSupport> NetCore </ CLRSupport>). В этой статье я расскажу о шагах, необходимых для переноса простого проекта взаимодействия C ++ / CLI в .NET Core. Более подробную информацию можно найти в документации .NET Core.

Пример проекта

Во-первых, мне нужно сделать пример решения для миграции. Я собираюсь использовать приложение с собственной точкой входа, которая отображает форму Windows Forms через C ++ / CLI. Миграция решения с управляемой точкой входа, взаимодействующей с коренными зависимостями через C ++ / CLI, была бы такой же простой. Для начала я создал решение с тремя проектами:

1. NativeApp. Приложение C ++ для Windows из шаблона Visual Studio «Настольное приложение Windows».

1. Это будет точкой входа в приложение.

2. Я обновил его, чтобы он отображал управляемую форму (через проект CppCliInterop) и вызывал для нее метод при вызове команды IDM_ABOUT.

2. ManagedLibrary. Библиотека C # Windows Forms для .NET Core.

1. Это обеспечит форму WinForms для отображения собственного приложения.

2. Я добавил текстовое поле в форму и метод для установки текста текстового поля. Я также нацелил этот проект на .NET Core и .NET Framework, чтобы его можно было использовать с любым из них. Таким образом, мы можем сосредоточиться на переносе только части образца C ++ / CLI.

3. CppCliInterop. Библиотека .NET Framework C ++ / CLI.

1. Это будет использоваться в качестве уровня взаимодействия для подключения приложения к управляемой библиотеке WinForms.

2. Он ссылается на ManagedLibrary и позволяет коренным проектам использовать его.

3. Это проект, который необходимо перенести в .NET Core.

 

Пример кода доступен на GitHub. При запуске приложения, если вы щелкнете по меню Справка -> О программе, форма WinForms будет отображаться с текстом в текстовом поле, предоставленном проектом NativeApp.

Миграция vcxproj в .NET Core

Теперь для интересной части - обновление примера приложения для запуска на .NET Core. Необходимые изменения на самом деле минимальные. Если вы ранее переносили проекты C # в .NET Core, перенос проектов C ++ / CLI еще проще, поскольку формат файла проекта не меняется. В управляемых проектах проекты .NET Core и .NET Standard используют новый формат файла проекта в стиле SDK. Однако для проектов C ++ / CLI тот же формат vcxproj используется для таргетинга на .NET Core, как и .NET Framework.

Все, что нужно, - это внести несколько изменений в файл проекта. Некоторые из них могут быть сделаны через Visual Studio IDE, но другие (такие как добавление ссылок на WinForms) еще не могут быть. Таким образом, самый простой способ обновить файл проекта - это просто выгрузить проект в VS и отредактировать vcxproj напрямую или использовать редактор, такой как VS Code или Notepad.

1. Замените <CLRSupport>true</CLRSupport> на <CLRSupport>NetCore</CLRSupport>. Это говорит компилятору использовать /clr:netcore вместо /clr при сборке.

1. Это изменение может быть сделано через интерфейс конфигурации проекта Visual Studio, если вы предпочитаете.

2. Обратите внимание, что <CLRSupport> указывается отдельно в каждой группе свойств конфигурации / платформы в файле проекта примера проекта, поэтому обновление необходимо выполнить в четырех разных местах.

2. Замените <TargetFrameworkVersion>4.7</TargetFrameworkVersion> на <TargetFramework>netcoreapp3.1</TargetFramework>.

1. Эти параметры можно изменить с помощью интерфейса конфигурации проекта Visual Studio на вкладке «Дополнительно». Однако обратите внимание, что изменение параметра поддержки CLR проекта, как описано в предыдущем шаге, не приведет к автоматическому изменению  <TargetFrameworkVersion>, поэтому обязательно очистите параметр «.NET Target Framework Version» перед выбором .NET Core Runtime Support.

3. Замените ссылки .NET Framework (на System, System.Data, System.Windows.Forms и System.Xml) следующей ссылкой на компоненты WinForms из Windows Desktop .NET Core SDK. Этот шаг пока не поддерживает Visual Studio IDE, поэтому его необходимо выполнить, отредактировав vcxproj напрямую. Обратите внимание, что необходима только ссылка на Windows Desktop SDK, поскольку .NET Core SDK (который включает в себя такие библиотеки, как System, System.Xml и т. Д.) Включается автоматически. Существуют разные ссылки на Framework для WinForms, WPF или обоих (как описано в документах по миграции).

1.  

После внесения этих изменений проект C ++ / CLI будет успешно ориентирован на .NET Core.

Если вы используете последнюю версию Visual Studio 2019 (16.5 или 16.6 Preview 1), все должно работать и во время выполнения.

До предварительного просмотра Visual Studio 2019 16.5 2 библиотеки C ++ / CLI не создавались файл .runtimeconfig.json, необходимый для библиотек C ++ / CLI, чтобы указать, какую версию .NET Core они используют, поэтому его нужно было добавить вручную. Итак, если вы используете более старую версию Visual Studio, вам нужно будет вручную создать этот файл CppCliInterop.runtimeconfig.json и убедиться, что он скопирован в выходной каталог:

Теперь приложение может работать на .NET Core! Версия источника доступна в  the NetCore branch в репозитории GitHub. Вот форма Windows, запущенная перед загруженными модулями, показывающая выгруженный coreclr.dll.

Билд без MSBui

Миграция этого примера приложения в .NET Core была просто вопросом обновления файла проекта для целевой платформы .NET Core вместо .NET Framework. Если вам нужно собирать сборки C ++ / CLI напрямую с помощью cl.exe и link.exe. Необходимые шаги:

1. Используйте /clr:netcore вместо /clr при вызове cl.exe.

2. Справочные сборки .NET Core с использованием необходимой ссылки /FU (справочные сборки .NET Core обычно устанавливаются в папку % ProgramFiles% \ dotnet \ packs \ <SDK> \ <Version> \ ref).

3. При компоновке включайте каталог хоста приложения .NET Core как LibPath. Хост-файлы приложения .NET Core обычно устанавливаются в папку % Program Files% \ dotnet \ package \ Microsoft.NETCore.App.Host.win-x64 \ <Version> \ runtime \ win-x64 \ native).

4. Убедитесь, что файл ijwhost.dll (необходимый для запуска среды выполнения .NET Core) скопирован локально из расположения узла приложения .NET Core. MSBuild делает это автоматически при сборке проекта vcxproj.

5. Создайте файл .runtimeconfig.json, как говорилось ранее.

Несколько предостережений

1. Как видите, с Visual Studio 2019 и .NET Core 3.1 направленность  на .NET Core с проектами C ++ / CLI легко. Однако есть несколько ограничений C ++ / CLI. Поддержка C ++ / CLI возможна только в Windows, даже при работе в .NET Core. Если вам нужна межплатформенная совместимость, ссылайтесь на платформу.

2. Проекты C ++ / CLI не могут быть нацелены на .NET Standard - только .NET Core или .NET Framework - и многоцелевой таргетинг не поддерживается, поэтому для создания библиотеки, которая будет использоваться как вызывающими .NET Framework, так и .NET Core, потребуются два файла проекта.

3. Если в проекте используются API, которые недоступны в .NET Core, эти вызовы необходимо будет обновить до альтернатив .NET Core. .NET Portability Analyzer может помочь найти любые зависимости Framework, которые не будут работать в .NET Core.

Подведение итогов и ресурсы

Надеемся, что в этом примере показано, как воспользоваться преимуществами новой функциональности в Visual Studio 2019 и .NET Core 3.1 для переноса проектов C ++ / CLI в .NET Core. Следующие ссылки могут быть полезны для дальнейшего чтения.

C++/CLI .NET Core migration docs

Пример, использований в этом посте (исходный пример находится в основной ветке, а обновления .NET Core - в ветви netcore)

.NET Portability Analyzer

Источник



Exception: Object reference not set to an instance of an object.
Posted on 12. March 2019

Как переносить настольные приложения на .NET Core 3.0

В этой статье будет рассмотрен перенос настольного приложения с .NET Framework на .NET Core. В качестве примера возьмем WinForms приложение. Шаги для WPF приложения схожи, различия рассмотрим по ходу. Также увидим как использовать WinForms designer в Visual Studio, хотя он находится в стадии разработки и еще не доступен для .NET Core проектов.

О примере

Для этого поста будет использовано игровое Memory-style board приложение. Оно состоит из WinForms UI (MatchingGame.exe) и библиотеки классов с игровой логикой (MatchingGame.Logic.dll), которые предназначены для .NET Framework 4.5. Здесь можно скачать образец. Итак, рассмотрим перенос проекта приложения на .NET Core 3.0, а также библиотеки классов на .NET Standard 2.0. Использование .NET Standard вместо .NET Core позволяет повторно использовать игровую логику для размещения приложения на других платформах, таких как iOS, Android или в вебе.

 

Вы можете просмотреть видео, как выполняется конвертация либо следовать пошаговым инструкциям ниже. Конечно, можно воспользоваться двумя вариантами.

Пошаговый процесс

Перемещение может быть выполнено в отдельной ветке, если не используется контроль версий. Также можно создать копию своего проекта, к которой можно вернуться при необходимости.

Прежде чем переносить приложение на .NET Core 3.0, подготовите следующее:

  1. Установите .NET Core 3 и Visual Studio 2019 Preview версию (Visual Studio 2017 поддерживает версии только до .NET Core 2.2).
  2. Начните с рабочего проекта. Убедитесь, что проект беспроблемно открывается, собирается и запускается.
  3. Обновите NuGet пакеты. Всегда рекомендуется использовать последние версии NuGet пакетов перед любым переносом. Если Ваше приложение ссылается на какие-либо NuGet пакеты, обновите их до последней версии. Убедитесь, что Ваше приложение успешно собрано. Если NuGet выдает ошибки, понизьте версию и найдите последнюю, которая не нарушает Ваш код.
  4. Запустите .NET Portability Analyzer, чтобы определить, существуют ли какие-либо API, от которых зависит Ваше приложение, и которые отсутствуют в .NET Core. Если таковые имеются, Вам необходимо провести рефакторинг своего кода, чтобы избежать зависимостей от API, не поддерживаемых в .NET Core. Иногда можно найти альтернативный API, который обеспечивает необходимую функциональность.
  5. Замените packages.config на PackageReference. Если используются NuGet пакеты, то нужно добавить те же NuGet пакеты в новый .NET Core проект. .NET Core проекты поддерживают только PackageReference для добавления NuGet пакетов. Чтобы переместить ссылки NuGet из packages.config в файл проекта, в обозревателе решений необходимо щелкнуть правой кнопкой на packages.config -> Migrate packages.config в PackageReference… Более детально об этом типе переноса можно ознакомиться по ссылке Migrate from packages.config to PackageReference.

 

Портирование основного проекта

Создание нового проекта
  • Создайте новое приложение того же типа (Console, WinForms, WPF, Class Library), что и приложение, которое нужно перенести, для .NET Core 3. На данный момент были сделаны демонстрационные Visual Studio шаблоны для десктопных проектов, которые находились в стадии разработки, поэтому использовалась консоль.
dotnet new winforms -o \MatchingGame.Core\
  • Скопируйте в файле проекта все внешние ссылки из старого проекта, например:
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
  • Начните сборку. На этом этапе, если пакеты, на которые Вы ссылаетесь, поддерживают только .NET Framework, Вы получите NuGet предупреждение. Если Вы не обновились к последней версии NuGet пакета на шаге 3, попробуйте определить, доступна ли последняя версия, поддерживающая .NET Core (.NET Standard), и повторите обновление. Если более новой версии нет, то .NET Framework пакеты все еще можно использовать, но можно получить ошибки времени выполнения, если эти пакеты имеют зависимости от API, не поддерживаемых в .NET Core. В таком случае рекомендуется сообщить автору NuGet пакета, что Вас заинтересовало бы обновление пакета до .NET Standard. Сделать это можно через контактную форму в NuGet галерее.
Быстрый способ (заменить существующий файл проекта)

Итак, быстрый способ переноса. Убедитесь, что у Вас есть копия текущего .csproj файла, возможно, Вам придется использовать его в будущем. Замените текущий .csproj файл .csproj файлом из проекта, созданного ранее, и добавьте вверху <PropertyGroup> следующее:

<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
Соберите приложение. Если ошибок не возникло, то перенос проекта на .NET Core 3 прошел успешно. Для перемещения зависимого (UI) проекта рекомендуется ознакомиться с разделом «Перенос пользовательского интерфейса»), чтобы узнать, как использовать Designer, ознакомьтесь с разделом «Использование WinForms Designer для проектов .NET Core».

Медленный путь (управляемый перенос)

Если система выдает ошибки, то нужно внести дополнительные корректировки. Здесь предоставлена информация о внесении изменений в код с возможными исправлениями для каждого вопроса. Шаги ниже также помогут лучше понять процесс переноса, поэтому, если быстрый способ помог, но Вам интересно узнать «почему и как», продолжайте читать.
  • Перейдите в.csproj файл в SDK-style. Чтобы переместить приложение в .NET Core, сначала нужно изменить файл проекта в SDK формате, поскольку старый формат не поддерживает .NET Core. Кроме того, SDK-style формат намного проще и с ним легче работать. Убедитесь, что копия текущего .csproj файла создана. Замените содержимое .csproj файла следующим. Для WinForms приложения:
  
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net472</TargetFramework>
    <UseWindowsForms>true</UseWindowsForms>
    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
  </PropertyGroup>
</Project></pre>
Для WPF приложения:

  
    <Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net472</TargetFramework>
    <UseWPF>true</UseWPF>
    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
  </PropertyGroup>
</Project>
Обратите внимание, что значение <GenerateAssemblyInfo> было установлено как false. В проектах нового стиля AssemblyInfo.cs генерируется автоматически по умолчанию. Так что, если проект уже содержит файл AssemblyInfo.cs файл (а он содержит), то необходимо отключить авто-генерацию или удалить файл.

Теперь скопируйте и вставьте все ссылки из старой версии .csproj файла в новую. Например:

Ссылка на NuGet пакет
 
<PackageReference Include="Microsoft.Windows.Compatibility" Version="2.0.1" />
Ссылка на проект
 
<ProjectReference Include="..\MatchingGame.Core\MatchingGame.Core.csproj" />

Проект должен успешно сформироваться, так как это просто новый способ написать то же самое. Если возникли какие-либо ошибки, то перепроверьте сделанные шаги.

Существует также сторонний инструмент CsprojToVs2017, который может выполнить преобразование. Но после его использования все равно может понадобиться удалить некоторые ссылки вручную, например:
<Reference Include="System.Data.DataSetExtensions"></Reference>
<Reference Include="Microsoft.CSharp"></Reference>
<Reference Include="System.Net.Http"></Reference>

  • Перейдите от .NET Framework к .NET Standard или .NET Core. После успешного преобразования библиотеки в SDK формат, ее можно перенастроить. В данном случае, необходимо, чтобы библиотека классов предназначалась для .NET Standard вместо .NET Core. Таким образом, она будет доступна из любой .NET реализации, если нужно отправить игру на другие платформы (например, iOS, Android или Web Assembly). Для этого замените данную строку
<TargetFramework>net472</TargetFramework>
на следующую

<TargetFramework>netstandard2.0</TargetFramework>
Создайте Ваше приложение. Если Вы используете API, которые не включены в .NET Standard, то могут возникнуть ошибки. Если ошибок нет, то следующие два шага можно пропустить.

  • Добавьте Windows Compatibility Pack для совместимости, если необходимо.Некоторые API, которые не включены в .NET Standard, доступны в Windows пакете совместимости. Если на предыдущем шаге были получены ошибки, то можно проверить, поможет ли Windows пакет совместимости. В рассматриваемом приложении была получена следующая ошибка «Имя реестра не существует в текущем контексте», поэтому в проект был добавлен Microsoft.Windows.Compatibility NuGet пакет. После установки ошибка исчезла.
  • Установите API Analyzer. API Analyzer, доступный в виде NuGet пакета Microsoft.DotNet.Analyzers.Compatibility, предупредит об использовании устаревших API или API, которые не поддерживаются на всех платформах (Windows, Linux, macOS). Если был добавлен пакет совместимости, то рекомендуется добавить и API Analyzer для отслеживания случаев использования API, которые не будут работать на всех платформах. На этом рассмотрение процесса переноса библиотеки классов на .NET Standard заканчивается. Если у Вас есть несколько проектов, которые ссылаются друг на друга, переносите их «снизу вверх», начиная с проекта, который не зависит от других. В данном примере также есть WinForms MatchingGame.exe проект, поэтому теперь необходимо выполнить аналогичные шаги для его переноса в .NET Core.
Перенос пользовательского интерфейса
  • Добавьте .NET Core UI проект. Внедрите в решение новый .NET Core 3.0 UI проект. На данный момент Visual Studio шаблоны для настольных приложений находятся в стадии разработки, поэтому можно просто использовать dotnet CLI.
dotnet new winforms -o \MatchingGame.Core\
Для WPF проектов можно использовать следующее:

dotnet new wpf -o \MatchingGame.Core\
После того, как был создан новый WinForms .NET Core проект, его необходимо добавить в Ваше решение.
  • Свяжите проекты. Сначала удалите все файлы из нового проекта (прямо сейчас он содержит общий Hello World код). Затем свяжите все файлы из существующего .NET Framework UI проекта с .NET Core 3.0 UI проектом, добавив в .csprojfile файл следующую команду.
    
<ItemGroup>
    <Compile Include="..\\**\*.cs" />
    <EmbeddedResource Include="..\\**\*.resx" />
</ItemGroup>
Если у Вас WPF приложение, также необходимо включить .xaml файлы:

  
<ItemGroup>
  <ApplicationDefinition Include="..\WpfApp1\App.xaml" Link="App.xaml">
    <Generator>MSBuild:Compile</Generator>
</ApplicationDefinition>
<Compile Include="..\WpfApp1\App.xaml.cs" Link="App.xaml.cs"></Compile>
</ItemGroup>

<ItemGroup>
  <Page Include="..\WpfApp1\MainWindow.xaml" Link="MainWindow.xaml">
    <Generator>MSBuild:Compile</Generator>
  </Page>
  <Compile Include="..\WpfApp1\MainWindow.xaml.cs" Link="MainWindow.xaml.cs" />
</ItemGroup>
  • Совместите пространство имен по умолчанию и имя сборки. Поскольку Вы ссылаетесь на файлы, созданные дизайнером (например, Resources.Designer.cs), то нужно убедиться, что версия Вашего .NET Core приложения использует то же пространство имен и то же имя сборки. Скопируйте следующие параметры из Вашего .NET Framework проекта:
<PropertyGroup>
    <RootNamespace><!-- (Your default namespace) --></RootNamespace>
    <AssemblyName><!-- (Your assembly name) --></AssemblyName>
</PropertyGroup>
  • Отключите AssemblyInfo.cs файл. Как было отмечено ранее, в проектах нового стиля AssemblyInfo.cs генерируется автоматически по умолчанию. В то же время AssemblyInfo.cs файл из старого WinForms проекта будет скопирован и в новый проект, так как все ** \ *.cs файлы были связаны на предыдущем шаге. Это приведет к дублированию AssemblyInfo.cs. Чтобы избежать этого в файле MatchingGame.Core проекта, для GenerateAssemblyInfo было установлено значение false.
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
  • Запустите новый проект. Установите новый .NET Core проект в качестве StartUp проекта и запустите его. Убедитесь, что все работает.
  • Скопируйте или оставьте ссылку. Теперь вместо связывания файлов можно скопировать их из старого .NET Framework UI проекта в новый .NET Core 3.0 UI проект. После этого предыдущий можно удалить.
Использование WinForms Designer для проектов .NET Core

Как было упомянуто выше, WinForms Designer для .NET Core проектов еще не доступен в Visual Studio. Однако есть два способа обойти данный вопрос: 

  1. Сохраните файлы связанными (просто не выполните предыдущий шаг) и скопируйте их, когда будет доступна designer поддержка. Таким образом, можно изменить файлы в старом .NET Framework WinForms проекте, используя designer. И изменения будут автоматически отражены в новом .NET Core WinForms проекте, поскольку они связаны между собой.
  2. Храните два файла проекта в той же директории, где и WinForms проект: старый .csproj файл из существующего .NET Framework проекта и новый .csproj файл в SDK-style нового .NET Core WinForms проекта. Нужно выгрузить и перезапустить проект с соответствующим файлом проекта в зависимости от того, хотите ли Вы использовать designer или нет.
Подведение итогов

В этой статье было показано, как перенести десктопное приложение, содержащее несколько проектов, из .NET Framework на NET Core. Обычно недостаточно просто перенастроить свои проекты на .NET Core. Также были описаны потенциальные проблемы, с которыми Вы можете столкнуться, и способы их решения. Кроме того, было рассмотрено, как можно использовать WinForms designer для портированных приложений, пока он еще не доступен для .NET Core проектов.



Exception: Object reference not set to an instance of an object.