Lesson 1: Assemblies and Resources

Lesson 1: Assemblies and Resources

Assemblies are the primary units of deployment in a Microsoft .NET Framework application. Assemblies are collections of types and resources that are bound together to create a logical unit of functionality. Assemblies are also self-describing and contain all the information required by the common language runtime to interpret their contents and configure execution. In this lesson, you will learn how to create an assembly and resource files, retrieve resources dynamically, and share an assembly between applications.

After this lesson, you will be able to

  • Describe the parts of an assembly

  • Describe how to set identity information for an assembly

  • Describe how to create a DLL assembly

  • Describe how to retrieve resources programmatically

  • Describe how to create a resource-only assembly

  • Describe how to apply a strong name to your assembly

  • Describe how to install your assembly to the Global Assembly Cache

Estimated lesson time: 45 minutes

Assemblies

Assemblies are the fundamental building blocks of a .NET Framework application. They contain the types and resources that make up an application and describe those contained types to the common language runtime. Assemblies enable code reuse, version control, security, and deployment.

Put simply, an assembly is a project that compiles to an executable file or to a DLL file. Although .NET .exe and .dll files resemble other .exe and .dll files externally, the internal structure of an assembly is quite different from that of .exe or .dll files created with earlier development tools. An assembly consists of four internal parts:

  • The assembly manifest, or metadata. This contains information about the assembly that is exposed to the common language runtime.

  • The type metadata, which exposes information about the types contained within the assembly.

  • The intermediate language code for your assembly.

  • The resource files, which are nonexecutable bits of data, such as strings or images for a specific culture.

The assembly manifest contains the metadata that describes the assembly to the common language runtime. The common language runtime then uses the information in the assembly manifest to make decisions about the assembly s execution. An assembly manifest contains the following information:

  • Identity.

    Contains the name and version number of the assembly, and can contain optional information such as locale and signature information.

  • Types and resources.

    Contains a list of all the types that will be exposed to the common language runtime, as well as information about how those types can be accessed.

  • Files.

    Contains a list of all files in the assembly, as well as dependency information for those files.

  • Security permissions.

    Describes the security permissions required by the assembly. If the required permissions conflict with the local security policy, the assembly will fail to execute.

For the most part, the developer does not have to be concerned with the contents of the assembly manifest. It is compiled and presented to the common language run time automatically. The developer does, however, need to explicitly set the metadata that describes the assembly identity.

The identity of the assembly is contained in the AssemblyInfo.vb or .cs file for your project. You can set identity information for your assembly by right-clicking the AssemblyInfo icon and choosing View Code from the drop-down menu. The code window will open to the AssemblyInfo code page, which contains default null values for several assembly identity attributes. The following code example shows an excerpt from the AssemblyInfo file. Note that Microsoft Visual Basic .NET and Microsoft Visual C# include slightly different attributes by default.

Visual Basic .NET

<Assembly: AssemblyTitle("")> <Assembly: AssemblyDescription("")> <Assembly: AssemblyCompany("")> <Assembly: AssemblyProduct("")> <Assembly: AssemblyCopyright("")> <Assembly: AssemblyTrademark("")> <Assembly: CLSCompliant(True)>

Visual C#

[assembly: AssemblyTitle("")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("")] [assembly: AssemblyCopyright("")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")]

You can set assembly identity information by setting the value of these attributes in the AssemblyInfo file. The following code example demonstrates how to set the AssemblyTitle attribute:

Visual Basic .NET

<Assembly: AssemblyTitle("The Best Assembly Ever!")> 

Visual C#

[assembly: AssemblyTitle("The Best Assembly Ever!")]

Creating Class Library Assemblies

You will want to create class library assemblies frequently. Class library assemblies represent sets of types that can be referenced and used in other assemblies. For example, you might have a custom control that you want to use in several applications or a component that exposes higher math functions. Such an assembly is not executable itself, but rather must be referenced by an executable application.

You can create class library assemblies and control library assemblies by using the templates provided by Visual Studio .NET. The class library template is designed to help you create an assembly of types that can be exposed to other applications, and the Microsoft Windows control library template is provided to assist you in building assemblies of custom controls.

To create a class library assembly

  1. From the File menu, choose New and then choose Project. The New Project window opens.

  2. In the New Project window, select Visual Basic Projects or Visual C# Projects, as appropriate, and then choose Class Library or Windows Control Library, as appropriate.

  3. Write the code for your class library or Windows control library.

  4. Set any required identity information in the AssemblyInfo file of your assembly.

  5. From the Build menu, choose Build to build your assembly.

You can switch between a Release version and a Debug version of your assembly by choosing Configuration Manager from the Build menu and setting the appropriate build version. Configuring your assembly is discussed in greater detail in Lesson 2 of this chapter.

Resources and Resource Assemblies

Most production-ready applications use resources. Resources are nonexecutable data that is embedded in an application. Examples of resources include strings displayed in the user interface based on the culture settings of the system or a set of images. Packaging such data into resource files allows you to change the data required by a program without recompiling the entire application. In Chapter 8, you learned how to create resource files and assemblies by localizing your forms. In this section, you will learn how to create resource files manually, how to embed these resource files into assemblies or create resource-only assemblies, and how to use code to retrieve code and objects stored in resource assemblies.

Creating Resource Files

The .NET Framework includes a sample application called ResEditor that can be used for creating text and image resource files. The ResEditor application is not integrated with Visual Studio .NET it must be run separately. In fact, it is supplied as source code files and must be compiled before it can be used. The ResEditor source files are located in the FrameworkSDK\Samples\Tutorials\resourcesand localization\reseditor folder, found in the folder where Visual Studio .NET is installed. You can build the application using either the batch file supplied in that folder or by adding the source files to an empty Visual Studio project, and then configuring and building them.

NOTE
If you use the batch file to build the ResEditor, you should run it from the Visual Studio .NET command prompt to ensure that the system path variables include the Visual C# compiler. To open the Visual Studio .NET command prompt in Windows XP, click Start, and then point to All Programs, Visual Studio .NET, Visual Studio .NET Tools.

The ResEditor tool allows you to create .resources or .resx files that contain strings and images. The ResEditor user interface is shown in Figure 9.1.

ResEditor allows you to specify the type and name of the resources in your file. Once entries have been added to the file, you can supply values for the string resources or specify the images to be added for image resources. Once you have created your resource set, you can save it as either a .resources binary file or a .resx XML file.

figure 9-1 the reseditor user interface.

Figure 9-1. The ResEditor user interface.

To create a resource file with ResEditor

  1. If necessary, compile the ResEditor source files. They can be found in the FrameworkSDK\Samples\Tutorials\resourcesandlocalization\reseditor folder, found in the folder in which Visual Studio .NET is installed.

  2. Open ResEditor. If you want to edit a preexisting .resources or .resx file, you can open it by choosing File, Open and then browsing to the appropriate file.

  3. From the Add drop-down menu, choose the type of resource you want to add. In the Add text box, type the name that the resource will use. Click the Add button to add the resource to the file.

  4. In the main pane, click the cell next to the resource name to specify the value. If the resource is a string, type the string into the box. If it is an image, browse to the correct file.

  5. If you want to rename any of the resources, highlight the resource you want to rename by clicking it, type the new name in the Rename text box and click the Rename button to apply the new name. If you want to delete a resource, highlight the resource by clicking it and choose Delete from the Resources menu.

  6. When you have finished editing your resource file, choose File and then Save As to save your file. You can save it as either a .resources file or a .resx file.

Once created, you can add .resources or .resx files to your project by selecting Project, Add Existing Item and then browsing to the appropriate resource file.

Embedding Resources

Once you have created resource files, you can embed them in your assembly. This allows you to package resources into the same assembly as the code files, thus increasing the portability of your code and reducing its dependence on additional files. To embed an externally created resource into your assembly, all you have to do is add the file to your project. When the project is built, the resource file will be compiled into the assembly.

To embed resources in your assembly

  1. Create the resource file. This can be a .resources file or a .resx file. You might find it useful to use a tool such as ResEditor.

  2. From the Project menu, choose Add Existing Item. The Add Existing Item window opens.

  3. Browse to the correct resource file, and click Open to select it. The file is added to your project.

  4. From the Build menu, choose Build to build your solution. The assembly is built with the resource file embedded.

Creating Resource Assemblies

You can create assemblies that only contain resources. You might find this useful in situations where you expect to have to update the data contained in resource files, but do not want to have to recompile your application to update it.

You can create resource assemblies by adding resource files to an empty project. When the project is built, the resources will be compiled into their own assembly that can then be referenced and accessed. Accessing the resources contained in such assemblies is discussed later in this lesson.

To create a resource assembly

  1. From the File menu, choose New and then choose Project. The New Project window opens.

  2. In the New Project window, choose Visual Basic Projects or Visual C# Projects, as appropriate, and then choose Empty Project. Click OK to create the project. A new empty project is created.

  3. From the Project menu, use Add Existing Item to add your resource files to your project.

  4. In Solution Explorer, right-click your project and choose Properties. The Project Property Pages open.

  5. In the Output Type drop-down menu, change the output type of your project to Class Library.

  6. From the Build menu, choose Build <your project>.

  7. Your resources are compiled in the assembly.

Creating Satellite Assemblies

When creating international applications, you might want to provide different sets of resources for different cultures. Satellite assemblies allow different sets of resources to load automatically based on the CurrentUICulture setting of the thread. You learned how to generate applications for localization in Chapter 8. In this section, you will learn how to create satellite assemblies and incorporate them into your application.

Visual Studio .NET allows you to effortlessly create satellite assemblies by incorporating alternate sets of appropriately named resource files into your application. Visual Studio .NET does the rest upon compilation.

To be incorporated into a satellite assembly, a resource file must follow a specific naming scheme based on the culture for which it is designed. The name of a resource file for a specific culture is the same as the name of the resource file for the invariant culture, and the culture code is inserted between the base name and the extension. Thus, if you have a resource file named MyResources.resx, a resource file containing alternate resources for neutral German UIs would be named MyResources.de.resx. And a version of the file containing German resources specific to Luxembourg would be named MyResources.de-LU.resx.

Once these alternate versions of the file are added to your solution, Visual Studio .NET will compile them into satellite assemblies, and a directory structure for them will be created. At run time, the culture-specific resources contained in these files will be located automatically by the common language runtime.

To create satellite assemblies

  1. Create alternate versions of your resource files specific to locales where your application will be run.

  2. Name your resource files correctly for their specific culture. Names for resource files for satellite assemblies must contain the culture code separated by periods between the base name and the extension. The base name and extension must be the same as the resource file for the invariant culture.

  3. From the Project menu, use Add Exiting Item to add these files to your application.

  4. From the Build menu, choose Build Solution to build your solution.

  5. Culture-specific resource files are automatically compiled into satellite assemblies and placed in an appropriate directory structure.

Retrieving Resources at Run Time

At run time, you can use the ResourceManager class to retrieve embedded resources. A ResourceManager, as the name implies, manages access and retrieval of resources embedded in assemblies. Each instance of a ResourceManager is associated with an assembly that contains resources.

You can create a ResourceManager by specifying two parameters: the base name of the embedded resource file and the assembly in which that file is found. The new ResourceManager will be dedicated to the embedded resource file that you specify. The base name is the name of the namespace that contains the file and the file without any extensions. For example, a resource file named myResources.de-DE.resx in a namespace named Namespace1 would have a base name of Namespace1.myResources.

The assembly parameter refers to the assembly in which the resource file is located. If the assembly that contains the resources is the same assembly that contains the object that is creating the ResourceManager, you can get a reference to the assembly from the type object of your object. For example:

Visual Basic .NET

' This example demonstrates how to create a ResourceManager to ' access resource files in an embedded file named myResources.resx ' in a namespace called myNamespace and the same assembly as the ' current object. This example assumes Imports System.Resources Dim myManager As New ResourceManager("myNamespace.myResources", _ Me.GetType.Assembly)

Visual C#

// This example demonstrates how to create a ResourceManager to // access resource files in an embedded file named myResources.resx // in a namespace called myNamespace and the same assembly as the // current object. This example assumes using System.Resources ResourceManager myManager = new ResourceManager ("myNamespace.myResources", this.GetType().Assembly);

If the resources you are accessing are in a different assembly, such as a resource-only assembly, you must load that assembly before accessing the resources contained there. You can load the assembly by using the Assembly object in the System.Reflection namespace. For example:

Visual Basic. NET

' This example assumes Imports System.Resources Dim myResources As System.Reflection.Assembly ' The Assembly.Load method requires the name of the assembly as a ' parameter myResources = System.Reflection.Assembly.Load("ResourceAssembly") Dim myManager As New ResourceManager("ResourceAssembly.Resources", _ myResources)

Visual C#

// This example assumes using System.Resources System.Reflection.Assembly myResources; // The Assembly.Load method requires the name of the assembly as a // parameter myResources = System.Reflection.Assembly.Load("ResourceAssembly"); ResourceManager myManager = new ResourceManager("ResourceAssembly.Resources", myResources);

NOTE
Your project must contain a reference to the assembly you want to access.

Once you have created your resource manager, you can use it to retrieve strings and objects contained in the resource file. To retrieve a string, use the ResourceManager.GetString method, specifying the name of the string resource you are retrieving.

You can retrieve images and other objects from a resource file by using the ResourceManager.GetObject method. This method returns an object corresponding to the name provided. You must perform an explicit conversion to convert the returned object to the correct type. The following code example demonstrates how to retrieve an image using the ResourceManager:

Visual Basic .NET

' Assumes the existence of a ResourceManager named myManager Dim myImage As System.Drawing.Image myImage = CType(myManager.GetObject("ImageResource"), _ System.Drawing.Image)

Visual C#

// Assumes the existence of a ResourceManager named myManager System.Drawing.Image myImage; myImage = (System.Drawing.Image)myManager.GetObject("ImageResource");

Retrieving Culture-Specific Resources from Satellite Assemblies

When culture-specific satellite assemblies exist, a ResourceManager will load the correct resources based on the CurrentUICulture setting. Thus, if you have an embedded resource file named myResources.resx and a culture-specific file named myResources.de.resx, the ResourceManager will load resources from myResources.resx under most conditions, but will load from myResources.de.resx when the CurrentUICulture is set to de.

To access embedded resources

  1. Create an instance of a ResourceManager that specifies the namespace and root name of the embedded file that contains the resources, and the assembly in which the embedded file is found.

  2. Use the ResourceManager.GetString method to retrieve string resources, and the ResourceManager.GetObject method to retrieve object resources. You must explicitly convert retrieved objects to the correct type.

Shared Assemblies

Assemblies can be either private or shared. A private assembly is an assembly that is used by one application only. A shared assembly can be used by multiple applications.

NOTE
The terms private and shared have a different meaning for shared assemblies than they do for the Private (private) and Shared access modifiers used with types and members. In the context of shared assemblies, these terms describe whether more than one application can use a particular assembly.

Understanding Private and Shared Assemblies

Most of the assemblies you create will be private assemblies. Private assemblies are the most trouble free for developers and are the assembly created by default. A private assembly is an assembly that can be used by one application only. It is integral to the application, packaged with the application, and available to only that application. Because private assemblies are used by one application only, they do not have version or identity issues. Up to this point, you have created only private assemblies as you worked through the lessons in this guide.

You might disbelieve that last sentence. After all, you learned how to create custom controls in Chapter 7, and you can use the DLL that contains your custom controls in any number of projects. Yet, these controls are described as private assemblies. How can this be?

When you add a reference to a private assembly to your project, Visual Studio .NET creates a copy of the DLL containing that assembly and writes it to your project folder. Thus, multiple projects can reference the same DLL and use the types it contains, but each project has its own copy of the DLL and, therefore, has its own private assembly.

Only one copy of shared assemblies is present per machine, however. Multiple applications can reference and use a shared assembly. You can share an assembly by installing it to the Global Assembly Cache, and there are several reasons why you might want to do so:

  • Shared location.

    If multiple applications need to access the same copy of an assembly, it should be shared.

  • Security.

    The Global Assembly Cache is located in the C:\WINNT (Microsoft Windows 2000) or the WINDOWS (Microsoft Windows XP) folder, which are given the highest level of security by default.

  • Side-by-side versioning.

    Multiple versions of the same assembly can be installed to the Global Assembly Cache, and applications can locate and use the appropriate version.

For the most part, however, assemblies should be private. You should only share an assembly when there is a valid reason to do so. Sharing an assembly and installing it to the Global Assembly Cache further requires that your assembly be signed with a strong name.

Strong Naming

A strong name is a name that guarantees an assembly identity. It consists of information about the assembly, such as its name, version number, any culture information, and the public key of a public/private key pair. This information is encrypted with the private key of the key pair and can be decrypted with the public key of the key pair. Because no one but the developer has access to the private key, the strong name cannot be replicated by anyone but the developer, thereby ensuring the assembly identity.

To sign an assembly with a strong name, you must have access to a public/private key pair. If you do not have a key pair, you can generate one with the strong name utility (sn.exe).

To generate a key pair

  1. Open the Visual Studio .NET Command Prompt. In Windows XP, this can be opened from Start, All Programs, Microsoft Visual Studio .NET, Visual Studio .NET Tools.

  2. At the prompt, use the -k flag with sn.exe to specify an output file for your key pair. Key pair files usually have a .snk extension. An example follows:

    sn k myKey.snk

    A public/private key pair is generated. You can use this key file to sign your assemblies.

To sign your assemblies with a strong name

  1. In Solution Explorer, open the AssemblyInfo file for your project.

  2. Verify that the version number has been set for your assembly. This is specified in the AssemblyVersion attribute as shown in the following example:

    Visual Basic .NET

    <Assembly: AssemblyVersion("1.0.1.1")>

    Visual C#

    [assembly: AssemblyVersion("1.0.1.1")]

    The AssemblyVersion attribute is set to 1.0.* by default. The asterisk means that the common language runtime will automatically provide default values for the last two numbers.

  3. Use the AssemblyKeyFileAttribute to specify the path to the key file for your project. The path can be either an absolute path or a relative path. An example is shown here:

    Visual Basic .NET

    <Assembly: AssemblyKeyFile("..\..\myKey.snk")>

    Visual C#

    [assembly: AssemblyKeyFile("..\\..\\myKey.snk")]

    This attribute appears in Visual C# AssemblyInfo files by default, but must be added manually to Visual Basic .NET AssemblyInfo files.

  4. Build your assembly. The strong name will be generated and signed to the assembly.

Installing to the Global Assembly Cache

Once you have signed your assembly with a strong name, it is a small matter to install it to the Global Assembly Cache. You can install your assembly to the Global Assembly Cache by using the Global Assembly Cache utility (gacutil.exe).

To install your assembly to the Global Assembly Cache

  1. Sign your assembly with a strong name. (See the previous section for details.)

  2. Open the Visual Studio .NET Command Prompt. In Windows XP, this can be opened from Start, All Programs, Microsoft Visual Studio .NET, Visual Studio .NET Tools.

  3. Run gacutil.exe with the /i option to specify the assembly to be installed. The following code example demonstrates how to install an assembly named myAssembly.dll to the Global Assembly Cache:

    gacutil /i mypath\myAssembly.dll

Lesson Summary

  • Assemblies are the building blocks of applications. An assembly contains assembly metadata in the assembly manifest, type metadata, code files, and resources. Simply put, any project that compiles to an EXE or DLL file produces an assembly.

  • You can create class library assemblies by using the class library or Windows control library templates in Visual Studio .NET.

  • Resources are nonexecutable data that is logically deployed in an application. You can add resources to your assembly as .resources files or .resx files. When compiled, the resource files will be embedded in your assembly.

  • You can create resource-only assemblies by opening an empty project and only adding resource files to it. When built, the resulting DLL contains resources that can be accessed from other assemblies.

  • You can supply alternate versions of resource files for different cultures. These files must be marked with the culture code of the culture. When compiled, these alternate versions will be compiled into satellite assemblies.

  • You can retrieve resources at run time using the ResourceManager class. Each instance of the ResourceManager is associated with a particular resource file embedded within a particular assembly, both of which must be specified when the ResourceManager is instantiated. If satellite assemblies exist for a particular culture, the ResourceManager will retrieve the appropriate resource when the CurrentUICulture is set to that culture.

  • Most assemblies are private. A private assembly can be accessed only by the application with which it is associated. When a reference is made in a project to a nonshared assembly, a copy of that assembly is made and added to the project folder.

  • A shared assembly can be accessed by multiple programs at once. A shared assembly must be installed to the Global Assembly Cache. To install an assembly to the Global Assembly Cache, you must first sign it with a strong name. You can then use the Global Assembly Cache utility to install the assembly to the Global Assembly Cache.



MCAD(s)MCSD Self-Paced Training Kit(c) Developing Windows-Based Applications With Microsoft Visual Basic. Net a[.  .. ]0-316
MCAD(s)MCSD Self-Paced Training Kit(c) Developing Windows-Based Applications With Microsoft Visual Basic. Net a[. .. ]0-316
ISBN: 735619263
EAN: N/A
Year: 2003
Pages: 110

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net