【WinForms】DLLを単一EXEファイルに統合する方法

複数のDLL依存関係を単一実行可能ファイルにパッケージ化

Windows Formsアプリケーション開発では、プロジェクトが多くのDLLに依存することがあります。ビルド後のDebugディレクトリには様々なDLLファイルが散在し、配布が煩雑になります。理想的なシナリオは、最終的に単一のEXEファイルとして配布することです。この記事では、その実現方法を説明します。

手順1: プロジェクトファイルの変更

まず、.csprojファイルを編集して、DLLファイルをリソースとして埋め込みます。プロジェクトファイルの末尾(</Project>ノードの直前)に以下のターゲットを追加します:


<Target Name="AfterResolveReferences">
  <ItemGroup>
    <EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
      <LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)</LogicalName>
    </EmbeddedResource>
  </ItemGroup>
</Target>

<Target Name="DeleteOtherFile" AfterTargets="AfterBuild">
  <ItemGroup>
    <OtherFiles Include="$(OutputPath)\*" Exclude="$(OutputPath)\$(AssemblyName).*" />
  </ItemGroup>
  <Delete Files="@(OtherFiles)" />
</Target>

手順2: アセンブリ解決のカスタマイズ

次に、プログラムのエントリーポイントを変更して、リソースからアセンブリを動的に読み込むようにします。Program.csを以下のように修正します:


using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;

namespace WinFormsSingleExeDemo
{
    static class Program
    {
        /// <summary>
        /// アプリケーションのメインエントリーポイント
        /// </summary>
        [STAThread]
        static void Main()
        {
            // アセンブリ解決イベントハンドラを登録
            AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolver.LoadFromResources;
            
            // UI設定
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MainForm());
        }
    }

    public static class AssemblyResolver
    {
        public static Assembly LoadFromResources(object sender, ResolveEventArgs args)
        {
            // 実行中のアセンブリを取得
            var executingAssembly = Assembly.GetExecutingAssembly();
            var requestedAssembly = new AssemblyName(args.Name);

            // リソースパスを構築
            var resourcePath = requestedAssembly.Name + ".dll";
            if (!requestedAssembly.CultureInfo.Equals(CultureInfo.InvariantCulture))
            {
                resourcePath = $@"{requestedAssembly.CultureInfo}\{resourcePath}";
            }

            // マニフェストストリームからアセンブリを読み込む
            using (var resourceStream = executingAssembly.GetManifestResourceStream(resourcePath))
            {
                if (resourceStream == null)
                    return null;

                // ストリームをバイト配列に変換
                var assemblyData = new byte[resourceStream.Length];
                resourceStream.Read(assemblyData, 0, assemblyData.Length);
                
                // アセンブリをロード
                return Assembly.Load(assemblyData);
            }
        }
    }
}

ビルドと確認

上記の変更を適用してプロジェクトをビルドすると、出力ディレクトリに単一のEXEファイルが生成されます。このファイルにはすべての依存DLLが含まれているため、配布が非常に簡単になります。

注意点:

  • この方法では、DLLファイルがEXEリソースとして埋め込まれます
  • 最終的なEXEファイルサイズは、含まれるDLLの合計サイズに依存します
  • 署名付きアセンブリを使用する場合は、追加の設定が必要な場合があります

代替ソリューション

より簡単な方法として、Costura.FodyというNuGetパッケージを使用することもできます。このパッケージは、上記の手順を自動化し、追加のコードなしでDLLを埋め込みます。

インストール手順:

  1. パッケージマネージャーコンソールで「Install-Package Costura.Fody」を実行
  2. プロジェクトにFodyWeavers.xmlファイルが追加される
  3. ビルドすると自動的にDLLが埋め込まれる

タグ: WinForms .NET csharp DLL Deployment

5月29日 18:57 投稿