.NET Core 3 で C++/CLI

## はじめに

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

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

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

* [The Future of C++/CLI and .NET Core 3](https://devblogs.microsoft.com/cppblog/the-future-of-cpp-cli-and-dotnet-core-3/)

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

~~この記事は VS2019 16.4 が正式版になるまで随時更新するつもりでいます。~~
12/4 に .NET Core 3.1 と VS2019 16.4 正式版がリリースされました。

* [Announcing .NET Core 3.1](https://devblogs.microsoft.com/dotnet/announcing-net-core-3-1/)

という事で一旦ここまでとします。

## 基本情報

* 正式対応は .NET Core 3.1 からで VS2019 16.4 と同時の予定
* .NET Core 3.0 の段階で実装は完了している (?)
    * [Support mixed-mode assemblies on Windows](https://github.com/dotnet/coreclr/issues/18013)
* 現状では Windows 専用でクロスプラットフォーム対応の予定はない
    * [Support mixed-mode assemblies on non-Windows platforms](https://github.com/dotnet/coreclr/issues/25082)
    * [Will CoreCLR support C++/CLI crossplat?](https://github.com/dotnet/coreclr/issues/659)
* コンパイラのオプション指定 /clr:netcore で .NET Core 向けのコンパイルをする
* [Mixed Mode Assemblies](https://github.com/dotnet/coreclr/blob/master/Documentation/botr/mixed-mode.md)

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

VS2019 では 16.0 Preview 2 から IDE 上で使用が可能になっています。詳細は [Preview 2 の解説](#vs2019-1640-preview-20--net-core-sdk-31100-preview1-014459) を参照してください。

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

(Preview 4.0 で確認)

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

* [インターフェース - C# によるプログラミング入門 | ++C++; // 未確認飛行 C](https://ufcpp.net/study/csharp/oo_interface.html?p=5#dim)

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

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

```csharp: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");
}
```

```cpp:C++/CLI
Interface1^ o = gcnew Class1();
o->Method1();
o->Method2();
```

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

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

```cpp: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 + .NET Core SDK 3.1.100 (Release)

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

SDK のパスから Preview が取れたのでコマンドラインで試す場合は下記のようにしてください。


```
set LIB=%LIB%;"C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Host.win-x86\3.1.0\runtimes\win-x86\native"
cl /AI"C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.1.0\ref\netcoreapp3.1\\" /clr:netcore /FU"System.Console.dll" ConsoleApplication1.cpp
```

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

* [Visual Studio 2019 バージョン 16.4 Preview 6 New release](https://docs.microsoft.com/ja-jp/visualstudio/releases/2019/release-notes-preview#16.4.0-pre.6.0)

C++/CLI に関する不具合修正がされた、という項目があります。

* [C++/CLR + .Net Core crashes Visual Studio](https://developercommunity.visualstudio.com/content/problem/770531/cclr-net-core-crashes-visual-studio.html)

詳細を確認したかったのですが、再現手順自体がよくわからなかったので確認できませんでした。

その他は特に変更はなさそうです。

### 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 が利用可能になっていました。

* [An Update on C++/CLI and .NET Core](https://devblogs.microsoft.com/cppblog/an-update-on-cpp-cli-and-dotnet-core/)

Visual Studio Installer で

* ワークロードで "C++ によるデスクトップ開発" にチェック
* 個別のコンポーネントで "v142 ビルドツール (14.24) の C++/CLI サポート" にチェック

で使用可能になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211064/8a6830c2-8c28-089b-9091-074fe4b398ba.png)


#### IDE 上での使用

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

* CLR クラスライブラリ (.NET Core)
* CLR 空のプロジェクト (.NET Core)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211064/15758f88-537a-c381-ef42-c0537290e949.png)

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

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211064/24739e49-2115-7e4a-07dc-a985331b8b40.png)

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211064/6bceae66-ef99-04b7-410d-1b81f4cd8843.png)

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211064/463b62f5-432b-32cf-944d-2f8c67fa0818.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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211064/a8f8fd80-5c42-3c5f-926c-b74a69c64a3f.png)

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

#### IL の違い

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211064/c72b404e-4634-c842-870b-8e0968e979c5.png)


### VS2019 16.4.0 Preview 1.0 + .NET Core 3.0.100

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

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

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

* [System.Printing.vcxproj in dotnet-wpf-int fails to build with latest Dev16 builds with ICE](https://github.com/dotnet/wpf/issues/988)
* [Add back support for private C++ frontend](https://github.com/dotnet/wpf/pull/989/files/e5c456aa7656d1a4c8ff7a86f51d2247de59d67f#diff-10edb12917f680b100ab9b8c53ebe5c9)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211064/7c127ded-567b-61b2-0ccf-332d58fab35e.png)

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

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211064/2e509f88-c819-62eb-728f-b0f0f579b03f.png)

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211064/e1562a22-8a79-4160-5a3b-ebdbe1671c5c.png)

```cpp:ConsoleApplication1.cpp
int main(array<System::String ^> ^args)
{
    System::Console::WriteLine("Hello, world!(CLR)");
    return 0;
}
```