Search code examples
c#resource-files

How do I create and recall class objects from a resource file that I can use at runtime?


I am trying to create a resource file of message codes. I've created a small console example that fails when I try to recall the object.

I based this on this MSDN -Creating Resource Files example, but I don't know what I've missed while I tried to simplify it.

In the code I run it once to generate the resource file and add that file to the project. Then I recompile to run the recall code.

using System;
using System.Reflection;
using System.Resources;

namespace ResourceDemo
{
  internal class Program
  {
    private static void Main(string[] args)
    {
      bool generateMode = false;

      if (generateMode) {
        // After running once with generate mode set to true, add the resulting
        // "StatusItem.resource" that was created in the .\bin\x86\Debug folder
        // to the project.
        Generate();
      }
      else {
        // When run the next line generates an exception:
        //   An unhandled exception of type 'System.Resources.MissingManifestResourceException' occurred in mscorlib.dll
        //
        //   Additional information: Could not find any resources appropriate for the specified culture
        //   or the neutral culture.  Make sure "StatusItems.resources" was correctly embedded or linked
        //   into assembly "ResourceDemo" at compile time, or that all the satellite assemblies required
        //   are loadable and fully signed.

        StatusItem statusItem = GetResource("2");
        Console.WriteLine("Id: {0}  Message: {1}", statusItem.Id.ToString(), statusItem.Message);
        Console.ReadKey();
      }
    }

    public static void Generate()
    {
      StatusItem[] statusItem = new StatusItem[4];

      // Instantiate an Status object items.
      statusItem[0] = new StatusItem(2, "File not found");
      statusItem[1] = new StatusItem(3, "Path not found");
      statusItem[2] = new StatusItem(4, "Too many open files");
      statusItem[3] = new StatusItem(5, "File access denied");

      // Define a resource file named StatusItems.resx.
      using (System.Resources.ResourceWriter rw = new ResourceWriter(@".\StatusItems.resources")) {
        for (int i = 0; i < 4; i++) {
          rw.AddResource(statusItem[i].Id.ToString(), statusItem[i]);
        }

        rw.Generate();
      }
    }

    public static StatusItem GetResource(string key)
    {
      Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
      System.Resources.ResourceManager rm = new System.Resources.ResourceManager("StatusItems", Assembly.Load("ResourceDemo"));

      return (StatusItem)rm.GetObject(key);
    }

    [Serializable()]
    public class StatusItem
    {
      public StatusItem(int id, string message)
      {
        Id = id;
        Message = message;
      }

      public int Id { get; set; }
      public string Message { get; set; }
    }
  }
}

Solution

  • The following code successfully creates a non-embedded resource using the ResourceWriter and is able to recall the data objects using a dictionary.

    It seems to work fine from the IDE. I tried to compile this at the command line but ran into some other issues that are better left to a separate question. I think it has more to do with comand-line compiling than the code.

    I wanted to post something that answered this question specifically, though I will probably take the advice of Peter Duniho and use the ResxResourceWriter family instead.

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Resources;
    
    namespace ResourceDemo
    {
      internal class Program
      {
        private const string nameSpace = "ResourceDemo";
        private const string resourceExtension = ".resources";
        private const string resourceFilename = "StatusItems";
        private static IDictionary<string, StatusItem> dictionary;
    
        private static void Main(string[] args)
        {
          bool generateMode = false;
    
          if (generateMode) {
            // Only run when a new resource is added
            Generate();
          }
          else {
            // Show the contents of the resource
            EnumerateResource();
    
            // Make a dictionary so it is usable
            BuildDictionary();
    
            Console.WriteLine("Look-up items 2, 4, 42 and 3 in dictionary");
            WriteStatusItemToConsole(GetResource("2"));
            WriteStatusItemToConsole(GetResource("4"));
            WriteStatusItemToConsole(GetResource("42"));
            WriteStatusItemToConsole(GetResource("3"));
            Console.ReadKey();
          }
        }
    
        /// <summary>
        /// Build the working dictionary from the resource file
        /// </summary>
        public static void BuildDictionary()
        {
          Console.WriteLine("Building a look-up dictionary");
          StatusItem statusItem;
          dictionary = new Dictionary<string, StatusItem>();
          ResourceReader res = new ResourceReader(@".\" + resourceFilename + resourceExtension);
    
          IDictionaryEnumerator dict = res.GetEnumerator();
          while (dict.MoveNext()) {
            statusItem = (StatusItem)dict.Value;
            dictionary.Add(dict.Key.ToString(), statusItem);
          }
          res.Close();
          Console.WriteLine("{0} items written to dictionary.", dictionary.Count.ToString());
          Console.WriteLine();
        }
    
        /// <summary>
        /// List all the items inside the resource file. Assuming that the
        /// </summary>
        public static void EnumerateResource()
        {
          StatusItem statusItem;
          Console.WriteLine("Resources in {0}", resourceFilename + resourceExtension);
          ResourceReader res = new ResourceReader(@".\" + resourceFilename + resourceExtension);
          IDictionaryEnumerator dict = res.GetEnumerator();
          Console.WriteLine("Dictionary Enumeration ready");
          while (dict.MoveNext()) {
            statusItem = (StatusItem)dict.Value;
            Console.WriteLine("   {0}: '{1}, {2}' (Type: {3})", dict.Key, statusItem.Id.ToString(), statusItem.Message, dict.Value.GetType().Name);
          }
          res.Close();
          Console.WriteLine();
        }
    
        /// <summary>
        /// Called to create the binary resource file. Needs to be called once.
        /// </summary>
        public static void Generate()
        {
          StatusItem[] statusItem = new StatusItem[4];
    
          // Instantiate some StatusItem objects.
          statusItem[0] = new StatusItem(2, "File not found");
          statusItem[1] = new StatusItem(3, "Path not found");
          statusItem[2] = new StatusItem(4, "Too many open files");
          statusItem[3] = new StatusItem(5, "File access denied");
    
          // Define a resource file named StatusItems.resx.
          using (System.Resources.ResourceWriter rw = new ResourceWriter(@".\" + resourceFilename + resourceExtension)) {
            for (int i = 0; i < 4; i++) {
              rw.AddResource(statusItem[i].Id.ToString(), statusItem[i]);
            }
            rw.Generate();
          }
        }
    
        /// <summary>
        /// Look up StatusItem in dictionary with the given key
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static StatusItem GetResource(string key)
        {
          StatusItem result = null;
          if (dictionary != null) {
            dictionary.TryGetValue(key, out result);
          }
          return result;
        }
    
        /// <summary>
        /// Write the value of the given item to the console
        /// </summary>
        /// <param name="statusItem"></param>
        public static void WriteStatusItemToConsole(StatusItem statusItem)
        {
          if (statusItem != null) {
            Console.WriteLine("   Id: {0}  Message: {1}", statusItem.Id, statusItem.Message);
          }
          else {
            Console.WriteLine("Null Item");
          }
        }
    
        /// <summary>
        /// This is our sample class
        /// </summary>
        [Serializable()]
        public class StatusItem
        {
          public StatusItem(int id, string message)
          {
            Id = id;
            Message = message;
          }
    
          public int Id { get; set; }
          public string Message { get; set; }
        }
      }
    }