his article describes how to use .NET’s ability to embed XML files, icons and other files in an assembly so your code can access them at run-time. Embedding the files helps you avoid problems commonly associated with external files, such as missing files, difficulty locating the files at runtime, and issues with upgrading and versioning.
The ability to embed resources in an application file is not new, but the mechanism for embedding files in .NET is different then for unmanaged code, so don’t expect to be able to work with embedded resources in exactly the same way as you could in unmanaged code.
Microsoft itself frequently embeds resources in application files. For example, if you load an XML file into Internet Explorer (IE), it renders the file in a TreeView-style manner, using different colors for nodes, attributes, and text content. IE accomplishes that by using an embedded resource, an XSLT stylesheet. You can access the stylesheet using the URL res://msxml.dll/DEFAULTSS.XSL (you can type this straight into IE’s address field to see the stylesheet contents).
|Author note: You can’t use this technique to access resources embedded within .NET assemblies, because the resource files are not stored in the same way.|
Storing Files as Resources in an Assembly
To include a file as a resource inside an assembly, add the file to your project in Visual Studio by dragging it into the Solution Explorer from Windows Explorer, or right-click the project item and select “Add” and then select “Add Existing Item” from the popup menu. After adding the file in your project, select it in the Solution Explorer and then select “Embedded Resource” as the build action in the properties window. (You can also add resources to a managed assembly by using the /resource compiler option if you are compiling your assembly from the command-line).
Select a Build Action
The “Build Action” property tells the compiler what to do with a particular file. Table 1 shows the available options:
|Embed||Include the file as an embedded resource.|
|Compile||Compile the file.|
|Content||Files designated as content files can be referenced by Visual Studio setup projects.|
|None||Do nothing. Use this action to add documentation or other uncompiled files to your project for reference purposes without embedding them in an assembly.|
Accessing Resources at Runtime
To access an embedded resource at runtime, use the GetManifestResourceStream() method of the Assembly class. There are several ways to get an active assembly reference at runtime – some of the common methods are the GetEntryAssembly(), GetExecutingAssembly(), or GetCallingAssembly() methods of the System.Reflection.Assembly class. The following list shows how and when you might want to use each method:
- GetEntryAssembly: Use this method to reference the executable file that was run to start the current process. The entry assembly is set for Windows forms applications, but for most other applications (for example, web projects), GetEntryAssembly returns ‘nothing’ (or null, in C#).
- GetExecutingAssembly: Use this method to reference the same assembly that the executing code is in.
- GetCallingAssembly: Use this method to reference the assembly containing the code that called the current method. This method is useful if you write generic code to read embedded resources that is inside a shared assembly where the embedded resource is in the calling assembly.
The name argument of the GetManifestResourceStream() method identifies your resource, and should match the resource filename. The argument is case-sensitive, regardless of the .NET language you are using.
If you use a default namespace you must prefix the assembly name to the resource name. For example, to access an icon called “icon1.ico” in a project with a default namespace of “myproject”, use the name “myproject.icon1.ico”. In both Visual Basic .NET and C#, new projects have a default namespace?Visual Studio uses the project name by default. You can remove or change the default namespace by right-clicking the project item in the Solution Explorer and selecting Properties from the popup menu. The default namespace appears under the Common Properties, General item (see Figure 1). In VB.NET, it’s the “root namespace”; in C# it’s called the “default namespace”.
|Figure 1: Use the Visual Studio Project properties dialog to remove or change the default project namespace.|
Although you can use folders (directories) to organize files in your project at design-time, at runtime, all manifest resources are in a “flat” structure (no pathnames, just filenames). If you’re having trouble identifying your resource name, compile your application and use ildasm.exe to view your assembly manifest?the resources are listed in the form “.mresource public [resource name]”.
Build the Sample Application
The sample application for this article consists of a simple Windows Forms application that contains three embedded files: an icon, an XML data file and an XSL style sheet. The following code excerpts demonstrate how to access the embedded resources at runtime.
Reading an embedded icon
' The GetIcon method centralizes the process to ' retrieve icon resources ' method to retrieve and assign an icon Sub ShowIcon picIcon.Image = GetIcon("Icon1.ico").ToBitmap End Sub ' method to retrieve an embedded icon resource Private Function GetIcon(ByVal strIdentifier As String) As System.Drawing.Icon ' use the strIdentifier argument to retrieve the ' appropriate resource from the assembly With New System.IO.StreamReader( _ [Assembly].GetEntryAssembly. _ GetManifestResourceStream(strIdentifier)) ' read the resource from the returned stream GetIcon = New System.Drawing.Icon(.BaseStream) ' close the stream .Close() End With End Function
Reading an embedded XML file
Sub GetListData Dim xmlListData As Xml.XmlDocument Dim xmlItem As Xml.XmlNode ' The GetXML method centralizes the process to ' retrieve XML resources xmlListData = GetXML("listdata.xml") For Each xmlItem In xmlListData.DocumentElement.ChildNodes lstData.Items.Add(xmlItem.InnerText) Next End Sub ' retrieve an embedded XML file Private Function GetXML(ByVal strIdentifier As String) As System.Xml.XmlDocument Dim xmlDoc As New System.Xml.XmlDocument() ' use the strIdentifier argument to retrieve the ' appropriate resource from the assembly With New System.IO.StreamReader( _ [Assembly].GetEntryAssembly. _ GetManifestResourceStream (strIdentifier)) ' load the document from the returned stream xmlDoc.Load(.BaseStream) .Close() ' return the document GetXML = xmlDoc End With End Function
Reading and Parsing an Embedded XSLT Stylesheet
Sub ParseData Dim stmOutputStream As New System.IO.MemoryStream() ' The GetStylesheet method centralizes the process ' to retrieve XSLT stylesheets, returning them as an ' instance of the XslTransform class. GetStylesheet("sample.xslt").Transform( _ GetXML("listdata.xml"), Nothing, stmOutputStream) txtResults.Text = _ System.Text.Encoding.ASCII.GetString( _ stmOutputStream.ToArray) stmOutputStream.Close() End Sub Private Function GetStylesheet(ByVal strIdentifier As String) As System.Xml.Xsl.XslTransform Dim xslSheet As New System.Xml.Xsl.XslTransform() Dim xslReader = _ New Xml.XmlTextReader( _ Assembly].GetEntryAssembly. _ GetManifestResourceStream(strIdentifier)) xslSheet.Load(xslReader) xslReader.Close() GetStylesheet = xslSheet End Function
Using Visual Studio, embedding files as runtime-accessible resources is easy. Embedding resources within your assembly can help to protect your intellectual property, prevent users from altering application resources and can reduce application deployment difficulties.