プログラムによる .resx ファイルの使用

注意

この記事は .NET Framework に適用されます。 .NET 5+ (.NET Core を含む) に適用される情報については、「.resx ファイルのリソース」を参照してください。

XML リソース (.resx) ファイルは適切に定義された XML で構成する必要があり、特定のスキーマに従ったヘッダーの後に、名前と値のペアになったデータが続きます。そのため、手動で作成するとエラーが発生しやすくなります。 代わりに、.NET クラス ライブラリの型とメンバーを使用して、.resx ファイルをプログラムで作成できます。 また、.NET クラス ライブラリを使用して、.resx ファイルに格納されているリソースを取得することもできます。 この記事では、System.Resources 名前空間の型とメンバーを使って、.resx ファイルを操作する方法を説明します。

この記事では、リソースを含む XML (.resx) ファイルの操作について説明します。 アセンブリに埋め込まれたバイナリ リソース ファイルの操作について詳しくは、「ResourceManager」をご覧ください。

警告

プログラムでの操作以外にも、.resx ファイルを操作する方法はあります。 リソース ファイルを Visual Studio プロジェクトに追加するときは、Visual Studio で .resx ファイルを作成して維持するためのインターフェイスが提供され、コンパイル時に .resx ファイルは .resources ファイルに自動的に変換されます。 .resx ファイルを直接操作するためにテキスト エディターを使うこともできます。 ただし、ファイルの破損を避けるため、ファイルに格納されているバイナリ情報を変更しないように注意してください。

.resx ファイルを作成する

System.Resources.ResXResourceWriter クラスを使い、次の手順に従って .resx ファイルをプログラムで作成できます。

  1. ResXResourceWriter メソッドを呼び出して、.resx ファイルの名前を指定することにより、 ResXResourceWriter(String) オブジェクトをインスタンス化します。 ファイル名には、.resx 拡張子を含める必要があります。 ResXResourceWriter ブロック内の using オブジェクトをインスタンス化する場合、手順 3 で ResXResourceWriter.Close メソッドを明示的に呼び出す必要はありません。

  2. ファイルに追加するリソースごとに ResXResourceWriter.AddResource メソッドを呼び出します。 このメソッドのオーバーロードを使って、文字列、オブジェクト、バイナリ (バイト配列) のデータを追加します。 リソースがオブジェクトの場合は、シリアル化可能でなければなりません。

  3. ResXResourceWriter.Close メソッドを呼び出して、リソース ファイルを生成し、すべてのリソースを解放します。 ResXResourceWriter オブジェクトが using ブロック内で作成された場合、リソースは .resx ファイルに書き込まれ、 ResXResourceWriter オブジェクトが使うリソースは using ブロックの最後に解放されます。

結果として得られる .resx ファイルには、適切なヘッダーと data メソッドによって追加された各リソースの ResXResourceWriter.AddResource タグが含まれます。

警告

パスワード、セキュリティの配慮が必要な情報、プライベートなデータなどの格納には、リソース ファイルを使用しないでください。

次の例では、6 つの文字列、1 つのアイコン、2 つのアプリケーション定義オブジェクト (2 つの Automobile オブジェクト) を格納する CarResources.resx という名前の .resx ファイルを作成します。 例で定義されてインスタンス化された Automobile クラスは、SerializableAttribute 属性でタグ付けされます。

using System;
using System.Drawing;
using System.Resources;

[Serializable()] public class Automobile
{
   private string carMake;
   private string carModel;
   private int carYear;
   private int carDoors;
   private int carCylinders;

   public Automobile(string make, string model, int year) :
                     this(make, model, year, 0, 0)
   { }

   public Automobile(string make, string model, int year,
                     int doors, int cylinders)
   {
      this.carMake = make;
      this.carModel = model;
      this.carYear = year;
      this.carDoors = doors;
      this.carCylinders = cylinders;
   }

   public string Make {
      get { return this.carMake; }
   }

   public string Model {
      get {return this.carModel; }
   }

   public int Year {
      get { return this.carYear; }
   }

   public int Doors {
      get { return this.carDoors; }
   }

   public int Cylinders {
      get { return this.carCylinders; }
   }
}

public class Example
{
   public static void Main()
   {
      // Instantiate an Automobile object.
      Automobile car1 = new Automobile("Ford", "Model N", 1906, 0, 4);
      Automobile car2 = new Automobile("Ford", "Model T", 1909, 2, 4);
      // Define a resource file named CarResources.resx.
      using (ResXResourceWriter resx = new ResXResourceWriter(@".\CarResources.resx"))
      {
         resx.AddResource("Title", "Classic American Cars");
         resx.AddResource("HeaderString1", "Make");
         resx.AddResource("HeaderString2", "Model");
         resx.AddResource("HeaderString3", "Year");
         resx.AddResource("HeaderString4", "Doors");
         resx.AddResource("HeaderString5", "Cylinders");
         resx.AddResource("Information", SystemIcons.Information);
         resx.AddResource("EarlyAuto1", car1);
         resx.AddResource("EarlyAuto2", car2);
      }
   }
}
Imports System.Drawing
Imports System.Resources

<Serializable()> Public Class Automobile
    Private carMake As String
    Private carModel As String
    Private carYear As Integer
    Private carDoors AS Integer
    Private carCylinders As Integer

    Public Sub New(make As String, model As String, year As Integer)
        Me.New(make, model, year, 0, 0)
    End Sub

    Public Sub New(make As String, model As String, year As Integer,
                   doors As Integer, cylinders As Integer)
        Me.carMake = make
        Me.carModel = model
        Me.carYear = year
        Me.carDoors = doors
        Me.carCylinders = cylinders
    End Sub

    Public ReadOnly Property Make As String
        Get
            Return Me.carMake
        End Get
    End Property

    Public ReadOnly Property Model As String
        Get
            Return Me.carModel
        End Get
    End Property

    Public ReadOnly Property Year As Integer
        Get
            Return Me.carYear
        End Get
    End Property

    Public ReadOnly Property Doors As Integer
        Get
            Return Me.carDoors
        End Get
    End Property

    Public ReadOnly Property Cylinders As Integer
        Get
            Return Me.carCylinders
        End Get
    End Property
End Class

Module Example
    Public Sub Main()
        ' Instantiate an Automobile object.
        Dim car1 As New Automobile("Ford", "Model N", 1906, 0, 4)
        Dim car2 As New Automobile("Ford", "Model T", 1909, 2, 4)
        ' Define a resource file named CarResources.resx.
        Using resx As New ResXResourceWriter(".\CarResources.resx")
            resx.AddResource("Title", "Classic American Cars")
            resx.AddResource("HeaderString1", "Make")
            resx.AddResource("HeaderString2", "Model")
            resx.AddResource("HeaderString3", "Year")
            resx.AddResource("HeaderString4", "Doors")
            resx.AddResource("HeaderString5", "Cylinders")
            resx.AddResource("Information", SystemIcons.Information)
            resx.AddResource("EarlyAuto1", car1)
            resx.AddResource("EarlyAuto2", car2)
        End Using
    End Sub
End Module

ヒント

Visual Studio を使用して .resx ファイルを作成することもできます。 コンパイル時に、Visual Studio は リソース ファイル ジェネレーター (Resgen.exe) を使って、.resx ファイルをバイナリ リソース (.resources) ファイルに変換し、アプリケーション アセンブリかサテライト アセンブリのいずれかに埋め込みます。

.resx ファイルをランタイムの実行可能ファイルに埋め込むことや、サテライト アセンブリにコンパイルすることはできません。 リソース ファイル ジェネレーター (Resgen.exe)を使って、.resx ファイルをバイナリ リソース (.resources) ファイルに変換する必要があります。 結果として得られる .resources ファイルは、アプリケーション アセンブリやサテライト アセンブリに埋め込むことができます。 詳細については、リソース ファイルの作成に関するページを参照してください。

リソースを列挙する

場合によっては、.resx ファイルから、特定のリソースではなく、すべてのリソースを取得したいことがあります。 これを行うには、.resx ファイル内のすべてのリソースの列挙子を提供する System.Resources.ResXResourceReader クラスを使います。 System.Resources.ResXResourceReader クラスは IDictionaryEnumeratorを実装します。これは、ループの反復処理ごとに特定のリソースを示す DictionaryEntry を返します。 その DictionaryEntry.Key プロパティはリソースのキーを返し、その DictionaryEntry.Value プロパティはリソースの値を返します。

次の例では、前の例で作成した CarResources.resx ファイルの ResXResourceReader オブジェクトを作成して、リソース ファイルを反復処理します。 リソース ファイルに定義された 2 つの Automobile オブジェクトを System.Collections.Generic.List<T> オブジェクトに追加し、6 個中 5 個の文字列を SortedList オブジェクトに追加します。 SortedList オブジェクト内の値は、コンソールに列見出しを表示するために使われるパラメーター配列に変換されます。 Automobile プロパティ値もコンソールに表示されます。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Resources;

public class Example
{
   public static void Main()
   {
      string resxFile = @".\CarResources.resx";
      List<Automobile> autos = new List<Automobile>();
      SortedList headers = new SortedList();

      using (ResXResourceReader resxReader = new ResXResourceReader(resxFile))
      {
         foreach (DictionaryEntry entry in resxReader) {
            if (((string) entry.Key).StartsWith("EarlyAuto"))
               autos.Add((Automobile) entry.Value);
            else if (((string) entry.Key).StartsWith("Header"))
               headers.Add((string) entry.Key, (string) entry.Value);
         }
      }
      string[] headerColumns = new string[headers.Count];
      headers.GetValueList().CopyTo(headerColumns, 0);
      Console.WriteLine("{0,-8} {1,-10} {2,-4}   {3,-5}   {4,-9}\n",
                        headerColumns);
      foreach (var auto in autos)
         Console.WriteLine("{0,-8} {1,-10} {2,4}   {3,5}   {4,9}",
                           auto.Make, auto.Model, auto.Year,
                           auto.Doors, auto.Cylinders);
   }
}
// The example displays the following output:
//       Make     Model      Year   Doors   Cylinders
//
//       Ford     Model N    1906       0           4
//       Ford     Model T    1909       2           4
Imports System.Collections
Imports System.Collections.Generic
Imports System.Resources

Module Example
    Public Sub Main()
        Dim resxFile As String = ".\CarResources.resx"
        Dim autos As New List(Of Automobile)
        Dim headers As New SortedList()

        Using resxReader As New ResXResourceReader(resxFile)
            For Each entry As DictionaryEntry In resxReader
                If CType(entry.Key, String).StartsWith("EarlyAuto") Then
                    autos.Add(CType(entry.Value, Automobile))
                Else If CType(entry.Key, String).StartsWith("Header") Then
                    headers.Add(CType(entry.Key, String), CType(entry.Value, String))
                End If
            Next
        End Using
        Dim headerColumns(headers.Count - 1) As String
        headers.GetValueList().CopyTo(headerColumns, 0)
        Console.WriteLine("{0,-8} {1,-10} {2,-4}   {3,-5}   {4,-9}",
                          headerColumns)
        Console.WriteLine()
        For Each auto In autos
            Console.WriteLine("{0,-8} {1,-10} {2,4}   {3,5}   {4,9}",
                              auto.Make, auto.Model, auto.Year,
                              auto.Doors, auto.Cylinders)
        Next
    End Sub
End Module
' The example displays the following output:
'       Make     Model      Year   Doors   Cylinders
'       
'       Ford     Model N    1906       0           4
'       Ford     Model T    1909       2           4

特定のリソースを取得する

.resx ファイル内の項目を列挙することに加えて、 System.Resources.ResXResourceSet クラスを使って特定のリソースを名前によって取得できます。 ResourceSet.GetString(String) メソッドは、名前付きの文字列リソースの値を取得します。 ResourceSet.GetObject(String) メソッドは、名前付きオブジェクトの値やバイナリ データを取得します。 メソッドはオブジェクトを返します。そのオブジェクトはその後適切な型のオブジェクトにキャスト (C#) するか、変換 (Visual Basic) する必要があります。

次の例では、そのリソース名を使って、フォームのキャプション文字列とアイコンを取得します。 また、前の例で使ったアプリケーション定義の Automobile オブジェクトを取得して、DataGridView コントロールに表示します。

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Resources;
using System.Windows.Forms;

public class CarDisplayApp : Form
{
   private const string resxFile = @".\CarResources.resx";
   Automobile[] cars;

   public static void Main()
   {
      CarDisplayApp app = new CarDisplayApp();
      Application.Run(app);
   }

   public CarDisplayApp()
   {
      // Instantiate controls.
      PictureBox pictureBox = new PictureBox();
      pictureBox.Location = new Point(10, 10);
      this.Controls.Add(pictureBox);
      DataGridView grid = new DataGridView();
      grid.Location = new Point(10, 60);
      this.Controls.Add(grid);

      // Get resources from .resx file.
      using (ResXResourceSet resxSet = new ResXResourceSet(resxFile))
      {
         // Retrieve the string resource for the title.
         this.Text = resxSet.GetString("Title");
         // Retrieve the image.
         Icon image = (Icon) resxSet.GetObject("Information", true);
         if (image != null)
            pictureBox.Image = image.ToBitmap();

         // Retrieve Automobile objects.
         List<Automobile> carList = new List<Automobile>();
         string resName = "EarlyAuto";
         Automobile auto;
         int ctr = 1;
         do {
            auto = (Automobile) resxSet.GetObject(resName + ctr.ToString());
            ctr++;
            if (auto != null)
               carList.Add(auto);
         } while (auto != null);
         cars = carList.ToArray();
         grid.DataSource = cars;
      }
   }
}
Imports System.Collections.Generic
Imports System.Drawing
Imports System.Resources
Imports System.Windows.Forms

Public Class CarDisplayApp : Inherits Form
    Private Const resxFile As String = ".\CarResources.resx"
    Dim cars() As Automobile

    Public Shared Sub Main()
        Dim app As New CarDisplayApp()
        Application.Run(app)
    End Sub

    Public Sub New()
        ' Instantiate controls.
        Dim pictureBox As New PictureBox()
        pictureBox.Location = New Point(10, 10)
        Me.Controls.Add(pictureBox)
        Dim grid As New DataGridView()
        grid.Location = New Point(10, 60)
        Me.Controls.Add(grid)

        ' Get resources from .resx file.
        Using resxSet As New ResXResourceSet(resxFile)
            ' Retrieve the string resource for the title.
            Me.Text = resxSet.GetString("Title")
            ' Retrieve the image.
            Dim image As Icon = CType(resxSet.GetObject("Information", True), Icon)
            If image IsNot Nothing Then
                pictureBox.Image = image.ToBitmap()
            End If

            ' Retrieve Automobile objects.  
            Dim carList As New List(Of Automobile)
            Dim resName As String = "EarlyAuto"
            Dim auto As Automobile
            Dim ctr As Integer = 1
            Do
                auto = CType(resxSet.GetObject(resName + ctr.ToString()), Automobile)
                ctr += 1
                If auto IsNot Nothing Then carList.Add(auto)
            Loop While auto IsNot Nothing
            cars = carList.ToArray()
            grid.DataSource = cars
        End Using
    End Sub
End Class

.resx ファイルをバイナリの .resources ファイルに変換する

.resx ファイルを埋め込みバイナリ リソース ( .resources) ファイルに変換することには、大きな利点があります。 .resx ファイルは、アプリケーション開発中の読み取りや保守を容易に行うことができますが、完成したアプリケーションに含まれることはほとんどありません。 .resx ファイルがアプリケーションと共に配布される場合、アプリケーションの実行可能ファイルとそれに付随するライブラリとは異なる独立したファイルとして存在します。 これに対し、 .resources ファイルは、アプリケーションの実行可能ファイルやそれに付随するアセンブリに埋め込まれます。 また、ローカライズされたアプリケーションの場合、実行時に .resx ファイルに依存すると、開発者がリソース フォールバックを処理することになります。 これに対し、埋め込みの .resources ファイルを含むサテライト アセンブリのセットが作成された場合、共通言語ランタイムがリソース フォールバック プロセスを処理します。

.resx ファイルを .resources ファイルに変換するには、 リソース ファイル ジェネレーター (resgen.exe)を使います。リソース ファイル ジェネレーターの基本的な構文は次のとおりです。

 resgen.exe .resxFilename

結果は、.resx ファイルと同じルート ファイル名と .resources ファイル拡張子を持つバイナリ リソース ファイルになります。 その後、コンパイル時に、このファイルを実行可能ファイルやライブラリにコンパイルできます。 Visual Basic コンパイラを使っている場合、次の構文を使って、.resources ファイルをアプリケーションの実行可能ファイルに埋め込みます。

vbc filename .vb -resource: .resourcesFilename

C# を使っている場合、構文は次のとおりです。

 csc filename .cs -resource: .resourcesFilename

アセンブリ リンカー (al.exe) を使って、 .resources ファイルをサテライト アセンブリに埋め込むこともできます。アセンブリ リンカーの基本的な構文は次のとおりです。

al resourcesFilename -out: assemblyFilename

こちらもご覧ください