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

.NET Core で作成済みのコンソールアプリなプロジェクトでWindows Formsの機能を利用可能にするための設定方法の紹介です。以下の画像のように、プロジェクトテンプレートをからコンソールアプリを選択して作成したプロジェクトは Windows Formsにあった機能群(例えばClipbordクラス、Bitmapクラス)などが使用できません。

これは、.NET Core のコンソールアプリがマルチプラットフォームを意識しているため Windows 上でしか動作しない「System.Windows.Forms」名前空間のに含まれるライブラリ類は参照関係が設定されていないからです(Windows 向けだけの話ですが知っていれば割と便利なクラスがいくつかあります)

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

そこで、このようなコンソールアプリでも Windows Forms の機能を利用できるようにプロジェクトを設定する方法を紹介したいと思います(この作業をするとマルチプラットフォーム性が失われてしまいますが、今後 .NET Framework が EOL に向かうため Windows 専用アプリと言えども .NET Core 3, .NET 5 への移行準備的な事を意識しています)

実装・確認環境

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

  • Visual Studoo 2019(16.7.1)
  • .NET Core 3.1 + C# 8.0
  • Windows10 1904(19041.450)

設定方法

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

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

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

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

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

// .csproj ファイル
<Project Sdk="Microsoft.NET.Sdk">

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

</Project>

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

// .csproj ファイル
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> <!-- ★★★(1) -->

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.0</TargetFramework>
    <UseWindowsForms>true</UseWindowsForms> <!-- ★★★(2) -->
  </PropertyGroup>

</Project>

編集後にファイルを保存すると 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
    {
        [STAThread] // 注意:大半のクラスはこの属性(STAThread)を付けないとエラーが発生する。
        public static void Main(string[] args)
        {
            // System.Windows.Forms.Clipboard クラス
            Clipboard.SetData("クリップボードに値を貼り付けました", true);
        }
    }
}

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

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