複数の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を埋め込みます。
インストール手順:
- パッケージマネージャーコンソールで「Install-Package Costura.Fody」を実行
- プロジェクトにFodyWeavers.xmlファイルが追加される
- ビルドすると自動的にDLLが埋め込まれる