65.9K
CodeProject is changing. Read more.
Home

Runtime Embedded Resource Manager

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (2 votes)

Jan 1, 2006

5 min read

viewsIcon

70742

downloadIcon

659

A library to help use your project's embedded resources at run-time.

Introduction

When you're organizing your development solution, you can go one of two routes with your non-source files: you can leave your resources external to your executable "loose" on the local file system, or you can compile them into the assembly. The first route allows you to modify the resources easily without having to redistribute code changes, but the second offers a small amount of protection against the modification of those resources, and leads to a tidier distribution.

This article isn't meant to convince you to embed if you don't have the need, but to offer some small help if you do. Reading embedded resources involves tedious and repetitive code, lending itself well to encapsulation in a library. I'll show you how and why I wrote a run-time embedded resource manager to give simplified access to images and sounds in my applications.

Background

Basically, I did this because I grew tired of writing this type of code over and over:

pictureBox1.Image = 
   new Icon(Assembly.GetExecutingAssembly().GetManifestResourceStream(
   "RuntimeResManTest.Icons.CDROM01.ICO")).ToBitmap();

I would inevitably get the case of a character in the name of the resource wrong and waste time troubleshooting the namespace name, class name, folder name, and resource name. To further cut down on the code to read the resource, I'd inevitably cache the current Assembly, and reference it on subsequent resource reads.

Eventually, I realized that what I wanted was a single point of contact for getting a handle to embedded resources, which in my case were always images (BMP, GIF, PNG and JPG), icons (ICO), and sound (WAV) files.

The Code

I wrote a simple class called Resources to act as that single point of contact for instantiation and resource reading. You have to pass the assembly with the embedded resources into the lone constructor, so it can enumerate and categorize them, but that's the only configuration it needs. That parameter will ordinarily be the assembly of the executable that references the library.

private RuntimeResMan.Resources _r = 
   new Resources(Assembly.GetExecutingAssembly());

The constructor of the Resources class passes the supplied Assembly into the internal constructors of the objects that hold the actual resource data:

  • Bitmaps
  • Icons
  • Sounds

These resource collection classes are structured similarly. They contain an ArrayList object that stores BitmapEx, IconEx, and Sound objects, respectively. I created BitmapEx, IconEx, and Sound objects to attach a string Name property to the resource data, for accessing the resource at run-time.

When constructed and passed an Assembly, the Bitmaps object reads the list of embedded resources:

internal Bitmaps(System.Reflection.Assembly target) {
   foreach(string resource in target.GetManifestResourceNames()) {
      string ext = Path.GetExtension(resource).ToLower();
   
      if (ext == ".bmp" || 
            ext == ".gif" ||
            ext == ".jpg" ||
            ext == ".jpeg") 
         _bitmaps.Add(new BitmapEx(resource, 
            (Bitmap)Bitmap.FromStream(
            target.GetManifestResourceStream(resource))));
   }
}

The BitmapEx constructor takes a string with the fully qualified name of the discovered resource, and the Bitmap object that it defines.

public BitmapEx(string name, Bitmap bitmap) {
   string[] tokens = name.Split('.');
   
   _name = tokens[tokens.Length - 2].ToLower();
   _bitmap = bitmap;
}

The unqualified name of the resource is calculated and forced to lowercase, so you won't be tripped up by case when trying to use the Bitmap later at runtime. The extension is chopped too, since it isn't necessary. The framework knows which "flavour" the Bitmap is (GIF, JPG, etc.), so we can drop the extra characters for brevity and simplicity. This assumption would cause a problem if you have multiple resources with the same name that differ by extension (balloon.bmp and balloon.gif in the same assembly), so feel free to append the extension to the name if it suits your purpose.

My Bitmaps class exposes the actual Bitmap object to client code via a string accessed indexer property (the simple name of the embedded resource), so the BitmapEx class is never actually required to be exposed. It stays private in the assembly.

The Icons class works the same way, except that it searches for embedded resources with the extension ".ico", of course. In all other ways, though, it is structured and behaves the same way as the Bitmaps class.

The Sounds class works slightly differently. First, since there is no Sound class in v1.1 of the framework, there's no need to use a contrived name like SoundEx to describe an object that contains both the sound data and the name of the sound. Secondly, since this class exposes a method, Play, to start playing the sound described, the class has to be public. The sound data contained in the .wav file isn't really meaningful outside of the context of the parameter for P/Invoke, so the raw data isn't exposed in the same way as the BitmapEx class exposes a Bitmap object, or the IconEx class exposes an Icon.

Knowing all this, you can now use embedded resources in your projects easily, without having to muck around with reading stream objects. You can see the simple code in the sample project.

private void cmdShowIcon_Click(object sender, System.EventArgs e) {
   pictureBox1.Image = _r.Icons["cdrom01"].ToBitmap();
}
   
private void cmdShowBitmap_Click(object sender, System.EventArgs e) {
   pictureBox1.Image = _r.Bitmaps["beany"];
}

To play a sound, add a .wav file to your project as an embedded resource and play it like this:

Sound s = _r.Sounds["burp"];
   
if (s != null)
   s.Play();

Or, if you fear no NullReferenceException, you can play it even more simply:

_r.Sounds["burp"].Play();

No muss, no fuss.

Caveat

This method is not very memory efficient if you have an awful lot of embedded resources and they're used infrequently in your solution. You wouldn't want to load them all aggressively at project startup, only to have them soaking up memory unnecessarily. You could modify this solution easily to load them only the first time they're required, thus spreading out the memory hit (and the overhead of the discovery process) over the lifetime of your application, rather than incurring the performance penalty up front, but I wanted to keep this example simple.

Obviously, since you're fetching and reading the same copy of the Bitmaps and Icons that were read at instantiation, if you fiddle with them programmatically, all subsequent uses of the embedded resources will get the modified, in-memory versions.

Conclusion

This article wasn't meant as a tutorial on the PlaySound API call, as much fun as it is to play with that particular function. There are a lot of other things you can do with it, like play synchronously, or stop the sound, but you can read more about those capabilities here, and add them to your solution if you're so inclined.

I really like the fact that managing resources this way insulates me from the annoying idiosyncrasies of the framework, like the fact that creating a Bitmap from a stream requires me to call a static method of the Bitmap class, while creating an Icon from a stream more naturally requires me to put the stream into the Icon constructor. Why the difference?

History

  • Jan 1 2006 - Initial revision (Happy New Year).