Skip to content

MS_MigrationToWindowsDesktopPacks

nishi_74322014 edited this page Aug 21, 2026 · 1 revision

Windows Desktop Packsへの移行

概要

.NET Core 3.0(.NET Core を参照)から、
Windows Forms / WPF のサポートが追加された。

詳細

基本的にポーティング移行になるもよう(VS2019 ではデザイナ使用不可)。

補足(デザイナは VS2019 の途中で使えるようになった): 本ページ執筆時点の
「VS2019 ではデザイナ使用不可」は当時として正しいが、その後解消された。

【デザイナ対応の経緯】★
   ・.NET Core 3.0 リリース時(2019年9月)
       → WPF / Windows Forms とも
         【デザイナが未提供】
       → .NET Framework 側のプロジェクトで
         デザインし、ファイルをリンクする
         (本ページの手順)という回避策が必要だった
   ・WPF デザイナ
       → VS 2019 16.3 頃から【正式提供】
   ・Windows Forms デザイナ
       → プレビューを経て
         【VS 2019 16.10(2021年5月)で正式提供】★
       → 出遅れた理由は、
         デザイナがコントロールを
         【実際にインスタンス化して表示する】仕組みのため、
         .NET Framework 上の VS から
         .NET Core のコントロールを
         読み込む【プロセス分離の仕組み】を
         作り直す必要があったから
   ・現在(VS 2022 / .NET 8, 9)
       → 【両方とも普通に使える】★

   → 【今から移行するなら、本ページの
     「ファイルをリンクする」手順は不要】である。
     ただし、大規模プロジェクトを
     段階的に移行する際の手法としては
     今も有効なので、記録として価値がある ★

移行手順の概要

.NET Framework版プロジェクト

  • ...の準備(既存)
  • ...の移行性評価(後述の「アナライザー」を参照)

.NET Core版のプロジェクト

  • ...の準備(新規)

    • ...の生成
    • ...の設定
    • ...から、.NET Framework 版プロジェクトのソース・ファイルをリンクする。
  • ...のコンパイルを通す。

    • ...へ、NuGet パッケージを追加
    • ...へ、必要に応じて後述の互換機能パックを追加
    • ...その他、非互換の API などの置換などを行う。
  • ...のビルドと実行とテストの実施

支援ツール

デザイナ

  • VS2019 では、

アナライザー

.NETのクロスプラットフォーム対応 の該当節を参照。

補足(現在の移行支援ツール): 「アナライザー」として本ページが指すのは
.NET Portability Analyzer と思われる。現在は後継がある。

【移行支援ツールの変遷】★
   ・【.NET Portability Analyzer】(旧)
       → API の移植可能性をレポート
       → 現在は【非推奨】。リポジトリはアーカイブ
   ・【.NET Upgrade Assistant】★(現行)
       → CLI / Visual Studio 拡張の両方
       → プロジェクト形式の変換(SDK スタイル化)、
         TargetFramework の変更、
         NuGet 参照の付け替えまで【自動でやる】
         dotnet tool install -g upgrade-assistant
         upgrade-assistant upgrade <path>
   ・【.NET API アナライザー】
       → コンパイル時に
         非互換 API・非推奨 API を警告する
         (Microsoft.DotNet.ApiCompat 等)
   ・【try-convert】
       → 旧形式 .csproj → SDK スタイルへの変換
       → Upgrade Assistant が内部で使う

   → 【まず Upgrade Assistant を掛ける】のが
     現在の定石。本ページの手作業の大半が自動化される ★

互換機能パック

概要

  • Windows 専用 API やプラットフォーム非依存 API など、約 20,000 の API を提供
  • NuGet パッケージ Microsoft.Windows.Compatibility 経由で提供される。
  • .NET Core または .NET Standard を対象とするプロジェクトから参照できる。

領域

# 分類 提供される領域
1 .NET Framework CodeDom
2 .NET Framework System.Runtime.Caching
3 .NET Framework Windows Workflow Foundation (WF)
4 .NET Framework Windows Communication Foundation (WCF)
5 .NET Framework Managed Extensibility Framework (MEF)
6 .NET Framework 互換性(Microsoft.Windows.Compatibility.Shims)
7 Windows Codepage
8 Windows GDI+
9 Windows ODBC
10 Windows Registry
11 Windows Service
12 Windows 暗号化
13 Windows EventLog
14 Windows アクセス制御リスト (ACL)
15 Windows パフォーマンス カウンター
16 Windows WMI (Windows Management Instrumentation)
17 Windows Active Directory(X.500)

移行メモ(体裁): 2 階層の箇条書きを表に整理した。

補足(互換機能パックの注意点): 便利だが、入れれば済むものではない。

【① 参照した時点で Windows 依存になる】★
   Microsoft.Windows.Compatibility は
   【Windows 専用 API を含む】
     → 参照すると
       「クロスプラットフォーム化」の目的からは遠ざかる
     → CA1416(プラットフォーム互換性アナライザー)が
       警告を出すようになった(.NET 5 以降)★
     → 「まず動かす」ための足場と割り切り、
       【段階的に外していく】のが正しい使い方

【② 入っていても "動かない" ものがある】★
   ・【WCF はサーバー側が入っていない】
       → クライアント(System.ServiceModel.*)のみ
       → サーバーは【CoreWCF】(コミュニティ移植)を使う
   ・【WF も同様】
       → CoreWF を使う
   ・System.Drawing.Common(GDI+)は
       → .NET 6 で【Windows 以外がサポート外】に
       → .NET 7 で【非 Windows は例外を投げる】★
       → クロスプラットフォームなら
         ImageSharp / SkiaSharp へ移行する

【③ 個別パッケージで足りることが多い】
   Microsoft.Windows.Compatibility は
   【巨大なメタパッケージ】
     → Registry だけ要るなら
       Microsoft.Win32.Registry だけ入れる方がよい ★

事例

Open棟梁Project での事例

手順

プロジェクト・ファイルの雛形の取得

新規作成 → プロジェクトで取得できる。

  • Windows Forms の場合
    ...のプロジェクト・ファイルの雛形
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>netcoreapp3.0</TargetFramework>
    <UseWindowsForms>true</UseWindowsForms>
    <ApplicationIcon />
    <StartupObject />
  </PropertyGroup>

</Project>
  • WPF の場合
    ...のプロジェクト・ファイルの雛形
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>netcoreapp3.0</TargetFramework>
    <UseWPF>true</UseWPF>
  </PropertyGroup>

</Project>

補足(現在の書き方:WindowsDesktop SDK は不要): .NET 5 以降、
プロジェクト ファイルの書き方が簡素化された

<!-- .NET 5 以降(現在の書き方)★ -->
<Project Sdk="Microsoft.NET.Sdk">        <!-- ← 通常の SDK でよい -->
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net8.0-windows</TargetFramework>  <!---->
    <UseWindowsForms>true</UseWindowsForms>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>
</Project>
【変わった点】★
 ① SDK は【Microsoft.NET.Sdk】に統一
     → Microsoft.NET.Sdk.WindowsDesktop は
       【互換のため残っているが、使う必要はない】
 ② TargetFramework に【-windows】を付ける
     → net8.0-windows
     → さらに OS バージョンも指定できる
       net8.0-windows10.0.19041.0
       (WinRT API を使う場合に必要)★
 ③ UseWindowsForms / UseWPF は【そのまま】
     → 両方 true にすれば併用も可能

ファイルのリンク

  • 上記のプロジェクト・ファイルにファイルをリンクする。

  • .NET Framework のプロジェクト・ファイルと同じフォルダ階層に、
    .NET Core のプロジェクト・ファイルを配置してしまっても行ける。

補足(なぜ「同じ階層に置けば行ける」のか): SDK スタイルの
プロジェクトの既定の挙動による。

【SDK スタイル プロジェクトの既定】★
   ・プロジェクト ファイルのあるフォルダ配下の
     【*.cs を自動的に全部含める】
     (旧形式は 1 つずつ <Compile Include> が必要だった)
   → だから「同じ階層に .csproj を置くだけ」で
     ソースが拾われる ★

【明示的にリンクする場合】
   <ItemGroup>
     <Compile Include="..\OldProj\**\*.cs"
              Exclude="..\OldProj\obj\**;..\OldProj\bin\**"
              Link="%(RecursiveDir)%(Filename)%(Extension)" />
   </ItemGroup>
   ※ obj / bin の除外を忘れると
     【自動生成ファイルまで拾って壊れる】★

【注意】
   ・同じ階層に置くと
     【obj / bin が衝突する】
     → BaseIntermediateOutputPath / BaseOutputPath を
       分けておくと安全 ★
   ・両方のプロジェクトを同時にビルドすると
     ファイル ロックが起きることがある

ポーティング移行

以下のポイントに注意しながらポーティング移行を行う。

ポイント

共通

  • AssemblyInfo.cs は除外(削除)する。

  • プロジェクト・ファイルを修正する。

    • ルート名前空間

    • アセンブリ名

    • プロジェクト出力

    • 参照設定や NuGet 参照

    • ファイル

      • ビルド アクションの設定
      • Resource ファイルの再構成
  • NuGet 周り

    • 参照アセンブリ(DLL)を吸わなくなったので、個別に NuGet 参照。
    • 必要に応じて、NuGet パッケージの依存関係の設定を適正化する。
  • #IF 用に NETCOREAPP を追加

    • 既定で、NETCOREAPP_3_0 があるのでこちらを利用してもイイ。
    • 必要に応じて、#IF - #ELSE の条件付きコンパイルディレクティブを実装する。

移行メモ(正誤): 移行元では条件付きコンパイル シンボルが
NETCOREAPP_3_0」と記載されていたが、
正しくは NETCOREAPP3_0NETCOREAPP の直後にアンダースコアは入らない)である。
なお #IF は VB の記法で、C# では #if である。

補足(AssemblyInfo.cs を削除する理由と、残したい場合):
単に「不要になった」のではなく、衝突するから削除するのである。

【SDK スタイルでは属性が自動生成される】★
   obj\Debug\net8.0-windows\
     XXX.AssemblyInfo.cs        ← 【ビルド時に自動生成される】
   → AssemblyVersion / AssemblyTitle / AssemblyCompany 等
   → 手書きの AssemblyInfo.cs を残すと
     【CS0579 "重複する属性" エラー】になる ★

【値はプロジェクト ファイルで指定する】
   <PropertyGroup>
     <AssemblyVersion>1.0.0.0</AssemblyVersion>
     <FileVersion>1.0.0.0</FileVersion>
     <Version>1.0.0</Version>
     <Company>...</Company>
     <Product>...</Product>
   </PropertyGroup>

【どうしても手書きを残したい場合】
   <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
   → 自動生成を止める
   → InternalsVisibleTo など
     属性を細かく制御したい場合に使う ★
【条件付きコンパイル シンボルの既定値】★
   net8.0-windows なら自動的に定義される
     NET / NET8_0 / NET8_0_OR_GREATER
     NET5_0_OR_GREATER … NET7_0_OR_GREATER
     NETCOREAPP / NETCOREAPP3_1_OR_GREATER
     WINDOWS / WINDOWS8_0_OR_GREATER …

   → 【_OR_GREATER 系を使う】のが定石 ★
       #if NET5_0_OR_GREATER
       // 新しい実装
       #else
       // .NET Framework 向け
       #endif
     → バージョンを上げるたびに
       条件を書き換えなくて済む

ライブラリの場合

  • OutputType を WinExe から Library へ変更する。
<OutputType>Library</OutputType>
  • Windows Forms / WPF のサポートの両方に対応する場合、以下を併記
<UseWindowsForms>true</UseWindowsForms>
<UseWPF>true</UseWPF>
  • Sgen.exe(XmlSerializer 専用コンパイラ)の問題で、
    GenerateSerializationAssemblies : Off を追記した。
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>

補足(Sgen.exe の問題とは何だったか): この対処の背景を補っておく。

【Sgen.exe(XML シリアライザー ジェネレーター)】★
   XmlSerializer は既定で
   【実行時に動的にアセンブリを生成する】
     → 初回の生成コストが大きい
     → これを【ビルド時に事前生成】するのが Sgen.exe
       (XXX.XmlSerializers.dll ができる)

   ・.NET Framework 時代のツールであり、
     【.NET Core では動かない / 不要】★
     → OutputType が Library だと
       既定で走ろうとしてエラーになるケースがあった
     → Off にして回避する(本ページの対処)

【現在】
   ・.NET Core / .NET 5+ では
     Microsoft.XmlSerializer.Generator という
     別ツールが用意されたが、
     【必要なケースは限られる】
   ・そもそも
     → 新規なら【System.Text.Json】★
     → XML が必須なら XmlSerializer をそのまま使い、
       インスタンスを【静的にキャッシュする】
       (毎回 new すると都度アセンブリ生成が走る)★

画面の場合

設定ファイル

  • app.config を除外(削除)し、appsettings.json を追加。
  • Microsoft.Extensions.Configuration.XXX 周辺の参照を追加
  • 冒頭で、appsettings.json の初期化コードを実行

補足(app.config は「使えなくはない」): 本ページの手順は
推奨される移行先だが、経緯を補っておく。

【app.config の扱い】★
   ・.NET Core でも
     【System.Configuration.ConfigurationManager】を
     NuGet で入れれば app.config は読める
     → 【段階移行の足場としては有効】
   ・ただし
     - configSections を使った独自セクションは
       動かないことがある
     - 暗号化(aspnet_regiis)は使えない
     - ConnectionStrings 以外は
       新しい仕組みと二重管理になる

【推奨される移行先】
   appsettings.json +
   【Microsoft.Extensions.Configuration】★
     var config = new ConfigurationBuilder()
         .SetBasePath(AppContext.BaseDirectory)   // ★
         .AddJsonFile("appsettings.json", optional: false)
         .AddJsonFile($"appsettings.{env}.json", optional: true)
         .AddEnvironmentVariables()
         .AddUserSecrets<Program>()               // 開発時のみ
         .Build();

   → 環境ごとの上書き、環境変数、
     シークレット管理が【階層的に合成される】★
【SetBasePath に AppContext.BaseDirectory を使う理由】★
   後述の「実行ファイルのパスに注意」(しばやん氏の記事)と
   同じ話。
     ・Directory.GetCurrentDirectory() は
       【カレント ディレクトリ】であり、
       exe の場所とは限らない
       (ショートカット起動、タスク スケジューラ起動で変わる)
     ・Assembly.Location は
       【単一ファイル発行だと空文字】になる ★
     → 【AppContext.BaseDirectory】が最も安全

サポート対象外

ClickOnce

補足(ClickOnce は .NET 5 以降で正式にサポートされた): 本ページの
「→ .NET 5 でサポートされる模様」はそのとおりになった

【現在の状況】★
   ・【.NET 5 以降の Windows デスクトップ アプリで
     ClickOnce 発行が可能】
   ・Visual Studio の [発行] ウィザードから選べる
   ・CLI からも可能
       dotnet publish -p:PublishProfile=ClickOnceProfile

【制約】
   ・【Windows 専用】(当然)
   ・self-contained 発行との組み合わせに制限がある
   ・単一ファイル発行とは併用しにくい

【配布方式の選択肢(現在)】★
   ・【ClickOnce】     … 自動更新が要る社内アプリ。実績豊富
   ・【MSIX】★         … Microsoft の推奨。
                         クリーンなインストール/アンインストール、
                         Store 配布、増分更新
                         → ただし【署名証明書が必須】で
                           社内配布のハードルが高い
   ・【自己完結型発行 + zip 配布】
                       … .NET ランタイムを同梱するので
                         「入っていない」問題が起きない ★
                       → 更新は自前 or Squirrel / Velopack
   ・MSI(WiX 等)     … 従来型。GPO 配布と相性がよい

VB版

現状では、VB 版テンプレート(dotnetコマンド を参照)が存在しない。

補足(VB のテンプレートは提供された): これもその後解消された

【現在】★
   dotnet new winforms -lang VB
   dotnet new wpf      -lang VB
   → 【VB でも Windows Forms / WPF の
     .NET プロジェクトを作れる】

【VB の現在の立ち位置】
   ・2020年、Microsoft は
     「VB に【新しい言語機能は追加しない】」と表明 ★
     → ただし【サポートは継続】する
     → .NET 8 / 9 でも VB の Windows Forms は動く
   ・対象ワークロード
     → Windows Forms / WPF / コンソール /
       クラス ライブラリは【対応】
     → ASP.NET Core は【非対応】★
   → 既存 VB 資産の維持には十分だが、
     新規は C# を選ぶのが順当

参考

.NET Blog

やってみた系

しばやん雑記

rksoftware

  • .NET Core 3.0 でデスクトップアプリを作る (目次)
    https://rksoftware.hatenablog.com/entry/2019/01/10/203234
    • .NET Core 3.0 でデスクトップアプリを作る(VS プレビュー版を使わない)

    • .NET Core 3.0 プロジェクトの発行でエラーになる

    • .NET Core 3.0 デスクトップアプリプロジェクトが

      • Visual Studio 2019 で開けない
      • ビルドできない
    • デザイナがなくても問題なし

      • .NET Core 3.0 で Windows フォームアプリケーションを作る
      • Windows フォーム手書き時の注意 AutoScaleDimensions 設定
    • 帳票出力

      • .NET Core デスクトップアプリケーションから DioDocs を使って帳票を PDF 出力する
      • .NET Core デスクトップアプリケーションで PDF 帳票を画面表示する
      • .NET Core デスクトップアプリケーションで DioDocs で作った PDF 帳票を印刷する

Microsoft Docs

移植

互換機能パック

移行メモ(リンクの誤り): 移行元では
「designs/compat-pack.md at master · dotnet/designs」の URL が、
直前の項目(Microsoft Docs の互換機能パック)と同一になっていた。
リンク テキストが指す GitHub 上の設計文書(dotnet/designs)の URL に置き換えた。


Tags: 移行, .NET開発, .NET Core

NetDevInfraWiki

マイクロソフト系技術情報 Wiki
Open 棟梁 Wiki

(未着手)

開発基盤部会 Wiki

移行管理: DONETODO

Clone this wiki locally