Edited at

.NET Core 3 で C++/CLI


はじめに

ついに .NET Core 3.0 がリリースされました。

.NET Framework はフェードアウトで今後は .NET Core に移行、集約する方針なのはご存じかと思います。

さて、 Windows アプリ開発者 (の一部) に強い関心があると思われる C++/CLI の行方ですが、一応 .NET Core のランタイムと VS2019 を含む開発環境もサポートを継続するとのアナウンスが出ています。

C++/CLI は個人的にはあまり推奨できないですが、これからどうなっていくのはとても気にはなりますので状況を追っていこうと思います。

この記事は VS2019 16.4 が正式版になるまで随時更新するつもりでいます。


基本情報

基本的には Windows の過去資産の継承が主目的で積極的な使用が目的ではないと思います。


インターフェースのデフォルト実装

(Preview 4.0 で確認)

インターフェースのデフォルト実装は IL / ランタイム拡張による新機能で .NET Core 3 以降で利用可能です。


C# で定義したデフォルト実装込みのインターフェース、実装クラスを C++/CLI から使う

定義されたものを使う事については特に問題がないようです (ランタイム側の対応だから当然とは思います) 。下記は動作します。


C#

public interface Interface1

{
public void Method1() => Console.WriteLine("Interface1.Method1");
public void Method2();
}

public class Class1 : Interface1
{
public void Method2() => Console.WriteLine("Class1.Method2");
}



C++/CLI

Interface1^ o = gcnew Class1();

o->Method1();
o->Method2();


C# で定義したデフォルト実装込みのインターフェースを C++/CLI で実装する

これはダメでした。デフォルト実装済みのメソッドも実装が要求されます。


C++/CLI

public ref class Class1 : public Interface1

{
public:
virtual void Method2()
{
}
};

下記エラーになります。

error C3766: 'CppCLILibrary::Class1' インターフェイス メソッド 'void CsClassLibrary::Interface1::Method1(void)' の実装を提供しなければなりません


C++/CLI でデフォルト実装込みインターフェースを定義する

C# で定義したものが正しく扱えないので当然ですが、定義できませんでした。


Visual Studio 2019 の動作状況


VS2019 16.4.0 Preview 5.0 + .NET Core SDK 3.1.100-preview3-014645

Preview 4.0 からの変更はおそらくありません。

SDK のパスが変わっているのでコマンドラインで試す場合は下記のようにしてください。

set LIB=%LIB%;"C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Host.win-x86\3.1.0-preview3.19553.2\runtimes\win-x86\native"

cl /AI"C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.1.0-preview3.19553.2\ref\netcoreapp3.1\\" /clr:netcore /FU"System.Console.dll" ConsoleApplication1.cpp


VS2019 16.4.0 Preview 4.0 + .NET Core SDK 3.1.100-preview2-014569

Preview 3.0 からの変更はおそらくありません。 (インターフェースのデフォルト実装まわりは Preview 4.0 から調べたのでその点については不明)


VS2019 16.4.0 Preview 3.0 + .NET Core SDK 3.1.100-preview2-014569

基本的には Preview 2.0 と同じです。出来ること、ビルドされるバイナリの挙動なども変わっていなさそうです。

SDK のパスが変わっているのでコマンドラインで試す場合は下記のようにしてください。

set LIB=%LIB%;"C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Host.win-x86\3.1.0-preview2.19525.6\runtimes\win-x86\native"

cl /AI"C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.1.0-preview2.19525.6\ref\netcoreapp3.1\\" /clr:netcore /FU"System.Console.dll" ConsoleApplication1.cpp

若干変わったなと思うところはコンパイル時に下記 Warning が出なくなっています (非表示なだけなのか根本的に出ないようになったのかは未確認) 。

warning C4679: 'System::String::GetPinnableReference": メンバーをインポートできませんでした。


VS2019 16.4.0 Preview 2.0 + .NET Core SDK 3.1.100-preview1-014459

Preview 1.0 と状況的には変わっていないようです。

(2019/11/3 追記) IDE 上から .NET Core の C++/CLI が利用可能になっていました。

Visual Studio Installer で


  • ワークロードで "C++ によるデスクトップ開発" にチェック

  • 個別のコンポーネントで "v142 ビルドツール (14.24) の C++/CLI サポート" にチェック

で使用可能になります。

image.png


IDE 上での使用

セットアップ後は新規プロジェクトに新しいテンプレートが追加されています


  • CLR クラスライブラリ (.NET Core)

  • CLR 空のプロジェクト (.NET Core)

image.png

コンソール構成もなくなり、クラスライブラリ作成のみとなりました。空のプロジェクトは本当になにもない状態です。

プロジェクトのプロパティを見ると .NET Core が選択できるようになっています。

image.png

ビルドした C++/CLI アセンブリからの起動は runtimeconfig.json を書いても起動できませんでした。調べ方が不十分かもしれませんができなくなっているのかもしれません。

image.png

.vcxproj を見ると興味深い変更として Keyword タグが "NetCoreCProj" になっています。従来の .NET Framework では "ManagedCProj" でした。

image.png

表面的な動作は Preview 1.0 で試した .NET Core C# + .NET Frmaework C++/CLI と特に違いはない感じです。ネイティブ混在デバッグも同じようにできています。


コマンドラインでのビルド

cl.exe に /clr:netcore だけを指定した場合は Preview 1.0 で調べた時とエラーは変わりませんでしたが、 IDE 上のビルドログから cl.exe のコンパイルオプションを確認したところ、 /AI オプションで .NET Core のメタデータディレクトリを指定するのがポイントだったようです。その他、


  • 必要な参照アセンブリを /FU オプションで追加指定

  • リンク時に "ijwhost.lib" というファイルが要求されたので、存在する "C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Host.win-x86\3.1.0-preview1.19506.1\runtimes\win-x86\native" に LIB パスを通す

以上でコンパイル、リンクは成功しました。

set LIB=%LIB%;"C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Host.win-x86\3.1.0-preview1.19506.1\runtimes\win-x86\native"

cl /AI"C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.1.0-preview1.19506.1\ref\netcoreapp3.1\\" /clr:netcore /FU"System.Console.dll" ConsoleApplication1.cpp

(LIB のパスは x86 用で x64 用のものは別途存在します)

image.png

これできたバイナリも起動できませんでした。


IL の違い

.NET Framework ビルドしたものと IL 比較をしてみました。ぱっと見た感じではかなり似通って見えますが、アセンブリ参照先が当然ではありますが .NET Core らしくなっていますね。 C++/CLI で特徴的だなあと思ったのが C++ 固有の属性が "System.Runtime.CompilerSevices.VisualC" というものに移動しているところです。

image.png


VS2019 16.4.0 Preview 1.0 + .NET Core 3.0.100

IDE 、 MSBuild は未対応です。プロジェクト設定で /clr:netcore の指定をすることはできません。

コマンドラインで /clr:netcore 付きでコンパイルすることが今のところできていません (調査不足の可能性あり) 。

↓この辺が参考になりそう

image.png

C# (.NET Core) プロジェクトから C++/CLI (.NET Framework) のライブラリプロジェクトを参照する形では動作します。ネイティブ混在で問題ありません。

ネイティブ混在デバッグも可能です。

image.png

エントリーポイントがある C++/CLI の .exe ファイルを .NET Core からの実行を試行したところ、 /clr (ネイティブ混在) は起動できませんでしたが /clr:safe (マネージドのみかつ安全なコード) は実行できました (/clr:pure はリンクエラーになったので未確認。理屈的には動くはず) 。 /clr と /clr:netcore の差分がありそうです。

image.png


ConsoleApplication1.cpp

int main(array<System::String ^> ^args)

{
System::Console::WriteLine("Hello, world!(CLR)");
return 0;
}