Edited at

WPFで実行ファイルとDLLを一つにまとめる(C#編)

More than 3 years have passed since last update.


その1 プロジェクトファイルにコマンドを追加

C#プロジェクトファイルの

<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets"/>

のあとに以下の文字列を挿入します

<Target Name="AfterResolveReferences">

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


その2 EXEからアセンブリをロードするコードを追加

以下のコードをソースファイルにしてプロジェクトに追加してください。


startup.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;

namespace StartUpCode //名前空間は使いやすいものに変えてください
{
public class StartUp
{
[STAThread]
public static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;

App.Main(); // Run WPF startup code.
}

private static Assembly OnResolveAssembly(object sender, ResolveEventArgs e)
{
var thisAssembly = Assembly.GetExecutingAssembly();

// Get the Name of the AssemblyFile
var assemblyName = new AssemblyName(e.Name);
var dllName = assemblyName.Name + ".dll";

// Load from Embedded Resources - This function is not called if the Assembly is already
// in the same folder as the app.
var resources = thisAssembly.GetManifestResourceNames().Where(s => s.EndsWith(dllName));
if (resources.Any())
{

// 99% of cases will only have one matching item, but if you don't,
// you will have to change the logic to handle those cases.
var resourceName = resources.First();
using (var stream = thisAssembly.GetManifestResourceStream(resourceName))
{
if (stream == null) return null;
var block = new byte[stream.Length];

// Safely try to load the assembly.
try
{
stream.Read(block, 0, block.Length);
return Assembly.Load(block);
}
catch (IOException)
{
return null;
}
catch (BadImageFormatException)
{
return null;
}
}
}
// in the case the resource doesn't exist, return null.
return null;
}
}
}



その3 プロジェクト設定を変更

プロジェクト設定を変更して、スタートアップオブジェクトをその2で追加したソースに変更する。

すべて終了したらビルドすればEXE単独で起動できるようになります。

ただし、WPFToolkitのグラフのようにDLLからDLLの関数を呼んでいたりすると動かない場合があるので100%とはいきませんが99%くらいはこれでいけます。

WPFプロジェクトじゃなくても使えるので意外に便利です。

今回の記事は次のサイトの日本語訳だったりする

http://blogs.interknowlogy.com/2011/07/13/merging-a-wpf-application-into-a-single-exe/