Click here to Skip to main content
Click here to Skip to main content

Runtime Embedded Resource Manager

By , 1 Jan 2006
 

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).

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Sean Michael Murphy
Product Manager
Canada Canada
Member
I'm a graduate of the University of Toronto with a degree in zoology. I'm currently a software development manager with a large Canadian financial institution, and a passionate squash player.
 
I live with the the three loves of my life; my wife Kim, son Alex and daughter Sarah.
 
I remain fond of my car.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralThis rocks!memberjfamme22 Mar '07 - 13:33 
A big Thank You for doing this!!!
 
Cool | :cool:
 
-JoeFlyer
Need an RC Plane? Go to www.JoeFlyer.com
GeneralRe: This rocks!memberRobbSadler1 Sep '07 - 7:58 
Yes - double rocks! I have been thinking about this and the pain level was just getting high enough that I was going to have to do something.
 
Many thanks!
 

GeneralYou wrote how to retrieve Resource... but how do i add them the the assemblymemberAssaf Koren5 Feb '06 - 14:18 
How do I add those resources to the Assembly? Confused | :confused:
 
don't worry, be happy!
GeneralRe: You wrote how to retrieve Resource... but how do i add them the the assemblymemberSean Michael Murphy6 Feb '06 - 2:46 
Assaf Koren wrote:
How do I add those resources to the Assembly?

 
Hey
Right click on your project in the solution explorer, select "Add -> Add Existing Item..." and browse to your picture, wav file, or whatever resource you want to add. Once it's in the project, press F4 to get it's properties and change the "Build Action" from "Content" to "Embedded Resource".
 
When you compile, the resource will be in the assembly.
 
Share and enjoy.
Sean
GeneralRe: You wrote how to retrieve Resource... but how do i add them the the assemblymemberAssaf Koren6 Feb '06 - 11:11 
Thanks for the fast response...
But that I know... the question is how would I keep the resources in an external file and load them from it?
 
I find a way to do it (using the ResGen utility) It works though it still doesn't support .wav files and stuff like that other then strings and images.
 
If you have any solution for storing the resources in an external resource file, please suggest.
 
Thanks for the help,
Assaf
 
don't worry, be happy!
GeneralRe: You wrote how to retrieve Resource... but how do i add them the the assemblymemberSean Michael Murphy8 Feb '06 - 16:42 
Assaf Koren wrote:
If you have any solution for storing the resources in an external resource file, please suggest.

 
Once you've got the resources compiled into an assembly, you can reference it (the resource assembly) statically by adding it as a reference in your main project, or dynamically by loading it at run time using reflection.
 
Share and enjoy.
Sean
GeneralRe: You wrote how to retrieve Resource... but how do i add them the the assemblymemberAssaf Koren11 Feb '06 - 19:24 
Thanks very much for the help...
 
Its working great.
AssafSmile | :)
 
don't worry, be happy!
GeneralInteresting but...memberRobert Kozak3 Jan '06 - 9:00 
Visual Studio 2005 now does this already out of the box. Go to the project properties and select Resources. Smile | :)
 

 
-- Robert Kozak
GeneralRe: Interesting but...memberSean Michael Murphy3 Jan '06 - 10:08 
Robert Kozak wrote:
Visual Studio 2005 now does this already out of the box. Go to the project properties and select Resources

 
Too funny; I haven't upgraded yet, but I guess I should to see what else I may be writing about that's already obsolete. 2003 does it too, of course, but in a very ham-handed, culture-aware sort of way that was overkill for what I wanted.
 
Thanks.
Sean
GeneralRe: Interesting but...memberi.tec1 Aug '06 - 19:02 
i am still using VS.NET 2002 :lol
 
.NET 2005 does not deserve a move up to the moment. Lightweight controls, winform multitasting and few other new feathers shine. however i manage them all in 2002.
 
pls forget about my opinion in case new version manages to block source code from .net decompiler

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 1 Jan 2006
Article Copyright 2006 by Sean Michael Murphy
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid