.NET Coreと.NET 5以降のコンソールアプリでWindows Formsの機能を利用する

.NET Core および .NET 5以降のコンソールアプリ上で Windows Forms の機能を利用可能にするための設定方法の紹介です。

以下の画像のように、プロジェクトテンプレートをからコンソールアプリを選択して作成したプロジェクトは Windows Formsにあった機能群(例えばClipbordクラス、Bitmapクラス)などがデフォルトでは使用できません。

.NET Coreでコンソールアプリを作成するときに指定するテンプレートの種類
コンソールアプリのテンプレート

そこで、このようなコンソールアプリでも Windows Forms の機能を利用できるようにプロジェクトを設定する方法を紹介したいと思います(この作業をするとマルチプラットフォーム性が失われてしまいますが、今後 .NET Framework も徐々に終了していくので移行するときに微妙に困ったときのメモになりmさ宇。

実装・確認環境

この記事は以下の環境で動作確認を行っています。

  • ViauslStudio 2019(16.11.4)
  • .NET Core 3.1 + C#8.0
  • .NET 5 + C# 9.0

設定方法

作成済みのコンソールアプリのソリューションエクスプローラの状態が以下のような形になっていると思います。フレームワークに「Microsoft.NETCore.App」がぶら下がっている状態です。このツリーのノードを更に展開すると中身の名前空間が見れますが「System.Windows.Forms」名前空間は含まれていません。

ソリューションエクスプローラ上で確認したコンソールアプリのファイルの状態
ソリューションエクスプローラーの状態

ここから、プロジェクトの設定を変更します。ソリューションエクスプローラのプロジェクトを選択し、コンテキストメニューから「プロジェクト ファイルの編集」を選択してください。

プロジェクト ファイルの編集のメニューを選択
プロジェクト ファイルの編集

エディタ画面に以下のようなXMLが表示されます。

// .csproj ファイル

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

</Project>

これを以下のように書き換えます。

// .csproj ファイル

// .NET Core3.1 の場合
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> <!-- ★★★(1) -->

  <PropertyGroup>
    <OutputType>WinExe</OutputType> <!-- ★★★(2) -->
    <TargetFramework>net5.0</TargetFramework>
    <UseWindowsForms>true</UseWindowsForms> <!-- ★★★(3) -->
  </PropertyGroup>

</Project>

// .NET 5 以降の場合
<Project Sdk="Microsoft.NET.Sdk"> <!-- 変更しない -->

  <PropertyGroup>
    <OutputType>WinExe</OutputType> <!-- ★★★(1) -->
    <TargetFramework>net5.0-windows</TargetFramework> <!-- ★★★(2) -->
    <UseWindowsForms>true</UseWindowsForms> <!-- ★★★(3) -->
  </PropertyGroup>

</Project>

.NET 5 以降は以下注意する必要があります。

「net5.0」を「net5.0-windows」に変更しないと以下のようなエラーが発生してコンパイルエラーになります。この設定は IDE 上からは現在設定ができないためプロジェクトファイルをテキストとして開いて編集するしかりません。そしてこの設定を行うとこのプロジェクトのプロパティの「対象のフレームワーク」が空欄になりますがこれは不具合ではありません。(=空欄だからともコンボボックスから選んで設定するとまたコンパイルエラーになります)

エラー NETSDK1136 Windows フォームまたは WPF を使用しているとき、またはそのようなプロジェクトまたはパッケージを参照しているときには、ターゲット プラットフォームを Windows に設定する必要があります (通常は TargetFramework プロパティに '-windows' を含めることによる)。

また「出力の種類」は「コンソール アプリケーション」を指定できない(というか無理やり指定しても効かなくなります)ので無理に変更しないようにしましょう。

f:id:Takachan:20211007235325p:plain

編集後にファイルを保存すると VisualStudio がプロジェクトの設定を自動でリロードします。そうすると以下のようにフレームワークの部分に「Microsoft.WindowsDesktop.App.WindowsForms」が追加されています。

Windows Forms 用の設定をプロジェクトファイルに書き込んだ場合
Windows Forms の設定を追加した場合

これで、「System.Windows.Forms」名前空間のクラスが使用できるようになります。

こんな感じです。

// Program.cs
using System;
using System.Windows.Forms; // ★★★ これが使える

namespace ConsoleApp17
{
    internal class Program
    {
        // 注意:Forms の大半のクラスはでこの属性(STAThread)を付けることが要求される
        [STAThread] 
        public static void Main(string[] args)
        {
            // System.Windows.Forms.Clipboard クラス
            Clipboard.SetData("クリップボードに値を貼り付けました", true);
        }
    }
}

コメントにも書きましたが1点だけ注意があって、メイン関数にSTAThreadAttributeを付与しないと実行時にエラーで怒られます。コンパイル時ではないのでご注意ください。

また、この指定の場合(当方コンソールプログラムは使い捨ての簡単な内容でした扱ってませんがもしかすると)マルチスレッドなプログラムを書いた場合 Windows Forms のようなメインスレッド上での動作の扱いを要求される可能性があります。

コンソールウインドウが開かなくなる対応

.NET 5 以降、この設定を行うとコンソールウインドウが開かなくなったようで、その対応として以下のコードを追加します。

public static class Kernel32
{
    [System.Runtime.InteropServices.DllImport("kernel32.dll")]
    public static extern bool AllocConsole();
}

static void Main(string[] args)
{
    Kernel32.AllocConsole(); // ★Mainの一番先頭に追記する

こうすると今まで通りコンソールウインドウが表示されます。

関連記事

takap-tech.com