.NETでは、実行可能ファイルやライブラリ(アセンブリ)に任意のファイルをリソース(resource)として格納することができます。

リソースには、テキストあるいはバイナリのファイル、画像や音声などのマルチメディアファイル、そのほか任意のファイルを埋め込むことができます。 リソースとして埋め込むことにより、コードと密接に関連する読み取り専用の外部ファイルをアセンブリとしてひとまとめにしたうえで公開・配布することができます。

埋め込んだリソースはアセンブリ自身が実行時に読み込むことができるほか、外部のアセンブリからも読み込むことができます。

ここではリソースを埋め込む方法と、埋め込んだリソースを読み込む方法について解説します。

.NETにおけるアセンブリとリソース

.NETにおける実行可能ファイル(.exeファイル)やライブラリ(.dllファイル)は、ともにアセンブリ(Assembly)という同一の構成単位で扱われます。 アセンブリには、コンパイルされた実行可能なコード(型やメソッド)だけでなく、リソースを格納することができます。

リソースは、実行時にコードから任意のタイミングで読み込んで使用することができます。 また、リソースはストリーム(System.IO.Stream)として取得できるため、ストリームからの読み込みをサポートをするStreamReaderなどのクラスを使うことができ、外部ファイルを読み込む場合と全く同じ方法で読み込むことができます。

.NETにおけるアセンブリと、それに格納される内容等についての詳細はクラスライブラリの作成 §..NETにおけるライブラリを参照してください。

リソースの埋め込み

ここからはアセンブリへのリソースの埋め込み方について見ていきます。 ここではIDEを使った場合と、コマンドラインを使った場合の2つのの方法での埋め込み方について紹介します。 また、プロジェクトファイルを直接編集してリソースを埋め込む場合についても紹介しています。

ここでは、リソースを埋め込むための実行可能ファイルまたはライブラリのプロジェクトが既に存在するものとして解説します。 プロジェクトの作成についてはクラスライブラリの作成等を参照してください。

また、ここでは埋め込むためのリソースとして、次のような内容のテキストファイルを埋め込むことにします。 また、プロジェクトファイルからは相対パスresources\hello.txtで参照される場所に配置するものとします。

resources\hello.txt
Hello, world!

IDEでのリソースの埋め込み

ここではファイルをリソースとして埋め込むための手順をIDEごとに解説します。 手順はIDEごとに異なりますが、共通することは以下の3点です。

  1. 埋め込みたいファイルをプロジェクトに追加する (あるいはプロジェクトファイルがあるディレクトリ配下に配置する)
  2. 埋め込みたいファイルのビルドアクションEmbeddedResource(埋め込みリソース)に変更する
  3. 必要に応じてリソースに与える論理名を指定する

Visual Studio 2019での例

.NET Coreや.NET Standardをターゲットとするプロジェクトの場合、プロジェクトファイルのディレクトリ配下にあるファイルはプロジェクトに関連するファイルとして自動的に一覧に表示されます。

プロジェクトファイルに表示されるファイル一覧

それ以外の場所にあるファイルや、.NET Framework形式のプロジェクトの場合は、プロジェクトのコンテキストメニューから[追加]→[既存の項目]を選択します。

リソースとして埋め込むファイルを追加する

ファイルダイアログで、リソースとして埋め込みたいファイルを選択し、追加します。

リソースとして埋め込むファイルを選択する

次に、ファイルのビルドアクションを変更します。 リソースとして埋め込みたいファイルのプロパティを開き、[ビルドアクション]を[埋め込みリソース]に変更します。

ファイルのビルドアクションを「埋め込みリソース」に変更する

ここまでの設定で、ファイルはリソースとして扱われ、ビルド時にアセンブリへと埋め込まれるようになります。


.NET Framework形式のプロジェクトの場合、プロパティでファイル名を変更することもできます。 この名前は、リソースを読み込むときに使用される論理名の一部として使われます。

.NET Core/.NET Standardのプロジェクトでは、この名前は変更できません。 変更したい場合は§.埋め込むリソースの論理名を定義するを参照して直接プロジェクトファイルを編集してください。

埋め込むリソースのファイル名
.NET Core/.NET Standardの場合・変更できない
.NET Frameworkの場合・変更できる

MonoDevelop 7.8での例

ここではMonoDevelopを使った場合について解説しますが、UIの差異はあるもののVisual Studio for Macでも概ね同様の手順が適用できると思われます。


.NET Coreや.NET Standardをターゲットとするプロジェクトの場合、プロジェクトファイルのディレクトリ配下にあるファイルはプロジェクトに関連するファイルとして自動的に一覧に表示されます。

プロジェクトファイルに表示されるファイル一覧

それ以外の場所にあるファイルや、.NET Framework形式のプロジェクトの場合は、プロジェクトのコンテキストメニューから[追加]→[ファイルの追加]を選択します。

リソースとして埋め込むファイルを追加する

ファイルダイアログで、リソースとして埋め込みたいファイルを選択し、追加します。 このとき、[規定のビルドアクションのオーバーライド]のチェックボックスにチェック☑を入れ、アクションを[EmbeddedResource]に変更します。

リソースとして埋め込むファイルを選択する

追加しようとしているファイルの場所によっては下記のようなダイアログが表示されます。 ファイルをどのようにしてプロジェクトに追加するか選択してください。

プロジェクトに追加するファイルの扱いを選択する

すでにプロジェクトに追加されているファイルをリソースとして埋め込む場合は、ファイルのコンテキストメニューから[ビルドアクション]→[EmbeddedResource]を選択します。

ファイルのビルドアクションを「EmbeddedResource」に変更する

ここまでの設定で、ファイルがリソースとして扱われ、ビルド時にアセンブリへと埋め込まれるようになります。


ファイルのプロパティでは、リソースIDを変更することもできます。 このIDは、リソースを読み込むときに使用される論理名として割り当てられます。 リソースID(論理名)はプロパティで変更できるほか、直接プロジェクトファイルを編集して指定することもできます。

埋め込むリソースのリソースID

プロジェクトファイルでのリソースの埋め込み

ここではプロジェクトファイルを直接編集することにより、特定のファイルをリソースとして埋め込むための方法を解説します。

ファイルをリソースとして埋め込むには<EmbeddedResource>要素を使います。 <EmbeddedResource>要素では、埋め込みたいファイルへのパス(絶対パス、またはプロジェクトファイルからの相対パス)をInclude属性で指定し、<ItemGroup>の子要素として次のように定義します。

プロジェクトファイルにリソースとして埋め込むファイルを記述する(EmbeddedResource要素)
<Project ...>
  (途中略)
  <ItemGroup>
    <!-- プロジェクトファイルからの相対パスresources\hello.txtのファイルをリソースとして埋め込む -->
    <EmbeddedResource Include="resources\hello.txt" />
  </ItemGroup>
</Project>

埋め込みたいファイルが複数ある場合は、その数だけ<EmbeddedResource>要素を定義します。

.NET Core形式のプロジェクトファイルでは、プロジェクトファイルのディレクトリ以下にある拡張子.resxのファイルは、<EmbeddedResource>要素を定義しなくても自動的に埋め込まれます。

<EmbeddedResource>要素でのファイルの指定・除外方法についてはプロジェクトファイル §.ビルド対象のファイルを参照してください。 その他、プロジェクトファイルの記述方法などについてはプロジェクトファイルを参照してください。

埋め込むリソースの論理名を定義する

リソースを埋め込む際、各リソースには論理名(LogicalName)が与えられます。 コード上からは、この論理名を指定することによって読み込む対象のリソースを指定します。

デフォルトでは、リソースの論理名はアセンブリ名.ディレクトリ区切り文字を . に置き換えたファイルパスになります。 例えば、アセンブリ名(あるいはプロジェクト名)がSampleで、ファイルパスがresources\hello.txtの埋め込みリソースには、Sample.resources.hello.txtの論理名がデフォルトで割り当てられます。

<EmbeddedResource>要素では、埋め込むリソースの論理名を定義することができます。 具体的には、次の例のようにLogicalName属性で論理名を定義します。

リソースとして埋め込むファイルの論理名を定義する(LogicalName属性)
<Project ...>
  (途中略)
  <ItemGroup>
    <!-- プロジェクトファイルからの相対パスresources\hello.txtのファイルをリソースとして埋め込む -->
    <!-- リソースには論理名として"hello.txt"を与える -->
    <EmbeddedResource Include="resources\hello.txt" LogicalName="hello.txt" />
  </ItemGroup>
</Project>

コマンドラインでのリソースの埋め込み

ここではコマンドラインコンパイラを使う場合に、特定のファイルをリソースとして埋め込むための方法を解説します。

MicrosoftのC#コンパイラであるcsc.exeとVisual Basicコンパイラvbc.exe、およびMonoのC#コンパイラmcs、同Visual Basicコンパイラvbncはいずれも同じ方法でリソースを埋め込むことが出来ます。

リソースを埋め込むためにはコンパイルオプション/resource、あるいはその省略形である/resを使います。 /resourceオプションは以下の形式で指定します。

/resource:path,name
pathで指定されたファイルに論理名nameを与えた上でリソースとして埋め込む。
/resource:path
pathで指定されたファイルをリソースとして埋め込む。 論理名にはpathのファイル名部分が用いられる。

埋め込みたいファイルが複数ある場合は、その数だけ/resourceオプションを指定します。 論理名は省略することができますが、その場合のデフォルト動作はプロジェクトファイルのEmbeddedResource要素で指定する場合とは異なり、アセンブリ名などのプレフィックスは前置されません。


例として、コンパイル時にパスresources\hello.txtのファイルをリソースとして埋め込むには次のようにします。 この例では、リソースの論理名としてhello.txtを指定しています。

cscでresources\hello.txtをリソースとして埋め込む
csc sample.cs /resource:resources\hello.txt,hello.txt
vbcでresources\hello.txtをリソースとして埋め込む
vbc sample.vb /resource:resources\hello.txt,hello.txt
mcsでresources/hello.txtをリソースとして埋め込む
mcs sample.cs /resource:resources/hello.txt,hello.txt
vbncでresources/hello.txtをリソースとして埋め込む
vbnc sample.vb /resource:resources/hello.txt,hello.txt

リソースの読み込み

リソースの取得と読み込み (Assembly.GetManifestResourceStream)

実行時にアセンブリに埋め込まれたリソースを読み込むには、Assembly.GetManifestResourceStreamメソッドを使います。

このメソッドでは、引数として埋め込まれたリソースの論理名を指定します。 リソースが存在する場合はそのリソースがStreamとして返されます。 存在しない場合はnull/Nothingが返されます。 System.IO.FileNotFoundExceptionのような、リソースが存在しないことを示す例外はスローされません。

リソースの論理名は、大文字小文字の違いが区別されます。 大文字小文字の違いを含めて論理名が一致しない場合は、リソースが存在しないものとしてnull/Nothingが返されます。


より具体的には、以下の手順でリソースを読み込みこみます。

  1. Assembly.GetExecutingAssemblyなど、Assemblyクラスのインスタンスを取得するメソッドを使って目的のリソースが埋め込まれているアセンブリを取得する。
  2. 読み込みたいリソースの論理名を指定してAssembly.GetManifestResourceStream()を呼び出し、リソースのStreamを取得する
  3. 取得したStreamからデータを読み込む

例として、リソース埋め込みの手順で埋め込んだテキストファイルを読み込む場合は次のようになります。 ここでは、リソースの論理名がSample.resources.hello.txtとなっていると仮定します。 プロジェクトファイルで明示的に論理名を指定していない場合、デフォルトではプロジェクト名(アセンブリ名).ディレクトリ区切り文字を . に置き換えたファイルパスが論理名になります。

現在実行しているアセンブリに埋め込まれているリソースを読み込む
using System;
using System.IO;
using System.Reflection;

class Sample {
  static void Main()
  {
    // 現在実行しているアセンブリを取得する
    var assm = Assembly.GetExecutingAssembly();

    // アセンブリに埋め込まれているリソース"Sample.resources.hello.txt"のStreamを取得する
    using (var stream = assm.GetManifestResourceStream("Sample.resources.hello.txt")) {

      // Streamの内容をすべて読み込んで標準出力に表示する
      var reader = new StreamReader(stream);

      Console.WriteLine(reader.ReadToEnd());

    }
  }
}
現在実行しているアセンブリに埋め込まれているリソースを読み込む
Imports System
Imports System.IO
Imports System.Reflection

Class Sample
  Shared Sub Main()
    ' 現在実行しているアセンブリを取得する
    Dim assm As Assembly = Assembly.GetExecutingAssembly()

    ' アセンブリに埋め込まれているリソース"Sample.resources.hello.txt"のStreamを取得する
    Using stream As Stream = assm.GetManifestResourceStream("Sample.resources.hello.txt")

      ' Streamの内容をすべて読み込んで標準出力に表示する
      Dim reader As New StreamReader(stream)

      Console.WriteLine(reader.ReadToEnd())

    End Using
  End Sub
End Class
実行結果(標準出力)
Hello, world!

この例ではプロジェクトの種類としてコンソールアプリケーションを使用していますが、ライブラリのプロジェクトやその他の種類のプロジェクトでも同様の手法で埋め込まれたリソースを読み込むことができます。

また、ここではStreamReaderを使ってテキストファイルのリソースを読み込みましたが、画像や音声ファイルなども同様に読み込むことが出来ます。 画像ならImage.FromStreamメソッド、音声ファイルならSoundPlayerなど、Streamからの読み込みに対応しているクラス・メソッドと組み合わせて使うことで読み込むことが出来ます。


次の例では、.NET CoreのWindows Formsアプリケーションのプロジェクトで、Image.FromStreamメソッドを使ってリソースとして埋め込まれている画像ファイルresources\sample.pngを読み込み、ウィンドウの中央に表示しています。

現在実行しているアセンブリに埋め込まれているリソースから画像を読み込み、Formの背景として設定する
using System;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Windows.Forms;

namespace SampleWinForms {
  public partial class Form1 : Form {
    public Form1()
    {
      InitializeComponent();
    }

    protected override void OnLoad(EventArgs e)
    {
      base.OnLoad(e);

      // 現在実行中のアセンブリを取得
      var assm = Assembly.GetExecutingAssembly();

      // リソースとして埋め込んだ画像ファイルのストリームを取得
      using (var stream = assm.GetManifestResourceStream("SampleWinForms.resources.sample.png")) {
        // 取得したストリームからImageを作成し、背景に設定する
        this.BackgroundImage = Image.FromStream(stream);
        this.BackgroundImageLayout = ImageLayout.Center;

        // 過去のバージョンの.NET Frameworkでは、Imageの作成元のStreamを閉じてしまうと例外がスローされた
        // こういった場合は、次のように一旦ストリームの内容をMemoryStream等にコピーする必要がある
        /*
        var memoryStream = new MemoryStream((int)stream.Length);

        stream.CopyTo(memoryStream);

        this.BackgroundImage = Image.FromStream(memoryStream);
        this.BackgroundImageLayout = ImageLayout.Center;
        */
      }
    }
  }
}
実行結果

すべてのリソース名の取得 (Assembly.GetManifestResourceNames)

Assembly.GetManifestResourceNamesメソッドを使うことにより、アセンブリに含まれているすべてのリソースの論理名を取得することができます。 このメソッドは、例えばアセンブリに埋め込まれるリソースの名前が具体的に定義されていない場合・予測できない場合や、名前からリソースを検索したい場合などに使うことができます。

現在実行しているアセンブリに埋め込まれているすべてのリソースを論理名を取得する
using System;
using System.Reflection;

class Program {
  static void Main(string[] args)
  {
    // 現在実行しているアセンブリを取得する
    var assm = Assembly.GetExecutingAssembly();

    // アセンブリに埋め込まれているすべてのリソースの論理名を取得して表示する
    foreach (var name in assm.GetManifestResourceNames()) {
      Console.WriteLine(name);
    }
  }
}
現在実行しているアセンブリに埋め込まれているすべてのリソースを論理名を取得する
Imports System
Imports System.Reflection

Class Sample
  Shared Sub Main()
    ' 現在実行しているアセンブリを取得する
    Dim assm As Assembly = Assembly.GetExecutingAssembly()

    ' アセンブリに埋め込まれているすべてのリソースの論理名を取得して表示する
    For Each name In assm.GetManifestResourceNames()
      Console.WriteLine(name)
    Next
  End Sub
End Class

このコードを§.リソースの埋め込みの手順で作成したプロジェクトで実行すると、次のように表示されます。

実行結果例
Sample.resources.hello.txt

リソースに関する情報の取得 (Assembly.GetManifestResourceInfo)

Assembly.GetManifestResourceInfoメソッドを使うと、リソースに関する追加的な情報を取得することができます。

リソースの情報はManifestResourceInfoクラスで参照することができ、このクラスを通してリソースが格納されている場所(ResourceLocationプロパティ)・アセンブリ(ReferencedAssemblyプロパティ・リソースが他のアセンブリに含まれている場合)やファイル名(FileNameプロパティ・リソースがマニフェストファイル以外に格納されている場合)などを知ることができます。

次の例では、Assembly.GetManifestResourceNamesメソッドと組み合わせて、アセンブリに含まれているすべてのリソースについて、その情報を取得・表示しています。

sample.cs (アセンブリに含まれるすべてのリソースの名前を取得して表示するプログラム)
using System;
using System.Reflection;

class Program {
  static void Main(string[] args)
  {
    // 現在実行しているアセンブリを取得する
    var assm = Assembly.GetExecutingAssembly();

    // アセンブリに埋め込まれているすべてのリソースの論理名を取得する
    foreach (var name in assm.GetManifestResourceNames()) {
      // リソースの情報を取得して表示する
      var info = assm.GetManifestResourceInfo(name);

      Console.WriteLine($"{name}:");
      Console.WriteLine($"  ResourceLocation={info.ResourceLocation}");
      Console.WriteLine($"  ReferencedAssembly={info.ReferencedAssembly}");
      Console.WriteLine($"  FileName={info.FileName}");
    }
  }
}
sample.vb (アセンブリに含まれるすべてのリソースの名前を取得して表示するプログラム)
Imports System
Imports System.Reflection

Class Sample
  Shared Sub Main()
    ' 現在実行しているアセンブリを取得する
    Dim assm As Assembly = Assembly.GetExecutingAssembly()

    ' アセンブリに埋め込まれているすべてのリソースの論理名を取得する
    For Each name In assm.GetManifestResourceNames()
      ' リソースの情報を取得して表示する
      Dim info = assm.GetManifestResourceInfo(name)

      Console.WriteLine($"{name}:")
      Console.WriteLine($"  ResourceLocation={info.ResourceLocation}")
      Console.WriteLine($"  ReferencedAssembly={info.ReferencedAssembly}")
      Console.WriteLine($"  FileName={info.FileName}")
    Next
  End Sub
End Class

このコードを§.リソースの埋め込みの手順で作成したプロジェクトで実行すると、次のように表示されます。

実行結果例
Sample.resources.hello.txt:
  ResourceLocation=Embedded, ContainedInManifestFile
  ReferencedAssembly=
  FileName=