Thursday, February 10, 2005

Embedded Resources

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/embedded_resources.htm]

Many non-trivial programming tasks require using data files such as xml, images, bat files, or sql scripts. There are common problems associated with using these files:

  1. They may not be included in the MSI during deployment. Merely adding a batch script to a project will not make it included in the MSI's files, even if you add it in the file editor.
  2. They often need to be referenced by a physical file path, which may be difficult to get or unintuitive if the base calling assembly is shadow-copied (this can happen if you test with NUnit).
  3. It can be hard to manage tons of individual files and keep their path straight - especially if you're sending your code to someone else's machine.

What we'd really like is a way to package those files in the DLL. Fortunately .Net lets us do exactly that – make them embedded resources. This will embed the file within the DLL during compilation, ensuring that it is both included in the MSI and accessible wherever the DLL is copied to. Embedded resources do not appear as their own separate files. Therefore 100 images embedded into a single DLL shows up as only one convenient file.

The steps to embed a resource are simple: In Solution Explorer, set it's Build Action to "Embedded Resource". This alone will solve the first problem of not being included in the MSI.

Once embedded, there are two ways that we'd like to be able to access the content: (1) A string of the direct content (no physical file needed). (2) A physical file path. We can handle both of these using the System.IO and System.Reflection namespaces.

Getting the direct content
The code below is a method that takes an assembly and the fully qualified resource name (with namespace), and returns a string of the content.

private static string GetEmbeddedResourceContent(Assembly asm, string strResourceName)
{       
    string strContent = "";
    Stream strm = null;
    StreamReader reader = null;
    try
    {
       
//get resource:
        string strName = asm.GetName().Name + "." + strResourceName;
        strm = asm.GetManifestResourceStream(strName);
        if (strm == null)
            strContent = null;
        else
        {
           
//read contents of embedded file:
            reader = new StreamReader(strm);

            if (reader == null)
                strContent = null;
           
else
                strContent = reader.ReadToEnd();
        }
//end of if
    }
   
catch
    {
        throw;
    }
    finally
    {
        if (strm != null)
            strm.Close();
        if (reader != null)
            reader.Close();
    }
//end of finally

    return strContent;

} //end of method

So within the assembly "MyAssembly", you could access the sql script "AddData.sql"  in folder "Scripts" like so:

string strActual = GetEmbeddedResourceContent(typeof(MyAssembly.MyClass),
"MyAssembly.Scripts.AddData.sql");

Accessing via a physical file
This may be useful if you needed the file for a batch script – for example if you wanted osql.exe to run a sql script. Because an embedded resource is not its own physical file, we need to first get the content (like above) and then copy this somewhere.

Embedded Resources are just one more useful technique. You'll find that when you need them, you really need them.

No comments:

Post a Comment