The Right to Assemble

The Right to Assemble

As I mentioned previously, assemblies are the building blocks of .NET Framework applications. Assemblies form the fundamental unit for managing deployment, version control, reuse, and security permissions. An assembly is really a collection of types and resources that are built to work together and form a logical unit of functionality. An assembly provides the common language runtime (CLR) with the information it needs to be aware of type implementations in an application. To the runtime, a type does not exist outside the context of an assembly.

We can look closely at assemblies with a technology called reflection. If you've accessed COM type libraries in a classic Visual Basic program, the concept of reflection in .NET will be familiar to you. However, reflection is now quite a bit more powerful and much easier to use. When you compile a Visual Basic source file with a .NET compiler, the compiler emits the intermediate language (IL) for the statements in the source file, along with the metadata that fully describes the types defined in the file. It is the metadata that the reflection classes in .NET use to examine the types.

An assembly contains one or more modules. How is this so? A module is a portable executable (PE) file with the extension .dll or .exe that consists of one or more classes and interfaces. A single module can contain multiple namespaces, and a namespace can span multiple modules. Therefore, an assembly is composed of one or more modules.

What makes assemblies different from classic Visual Basic EXE or DLL files is that they contain everything that would usually be found in a type library. As you know, an assembly contains a manifest, which is much like a table of contents. The manifest contains key information such as the assembly's name and version, all the files that make up the assembly (other DLLs, bitmaps, resources, readme files, and so on), and external dependencies.

Because assemblies are self-describing, Visual Basic .NET applications do not rely on registry values, which means that DLL conflicts are reduced and your applications are made more reliable and easier to deploy. To use an assembly, first a reference to the assembly is created and then the Imports statement is used to choose the namespaces of the items within the assembly you want to use.

Private Assemblies

Up to now we've been building private assemblies. Private assemblies are stored in the directory of the application and are used exclusively for a specific .NET program. You know that with the older-style DLLs, if a bug fix or enhanced functionality required a new version of the file to be installed, each program that needed this file used the new version automatically. The older version was overwritten by the new DLL. Of course, many times applications that worked just fine mysteriously crashed when another program installed a newer version of a DLL. Because we are storing our assemblies in the same directory as our application, this problem is obviated.

Private assemblies will usually be developed by a single company and will be used by a single application. Disk space is cheap, so storage is not a problem. And because we're in control of our assembly, we don't need to worry about our file being overwritten by a rogue application. If an application uses private assemblies, the application is usually packaged and deployed together with all its assemblies.

Sometimes, however, an assembly written by one company will be used by an application written by another. These companies are probably not coordinating their naming conventions, so the second company could easily develop an assembly that has the same name as the assembly from the first company. If we install two different programs that happen to have the same name, we are courting trouble. Luckily, the .NET Framework has an elegant solution to what was once a thorny problem.

Shared Assemblies

All of the development assemblies used for writing Visual Basic .NET programs are shared assemblies. Shared assemblies are similar to shared DLLs in the \Windows\System directory that are used by classic Visual Basic, Microsoft Windows, Microsoft Access, and other Windows programs. However, .NET ensures that each program uses only the assembly that it was compiled against. How is this accomplished?

Assemblies used in programs targeted for .NET are usually stored in the \Windows\Assembly folder. This folder is for global assemblies (such as Microsoft.VisualBasic) that can be used by any .NET program on your machine. When a Visual Basic .NET program is installed on a machine, the Visual Basic namespaces can be referenced automatically from this cache.

For developers using Visual Studio .NET, the public assemblies we just discussed are also stored in the Visual Studio development directories, so you actually have two copies on your machine. However, it's the \Assembly folder that's used to store assemblies on your users' machines for any .NET program you might distribute. Open the \Windows\Assembly folder on your computer—a view of mine is shown in Figure 8-1. You can right-click an assembly to take a look at its properties dialog box.

Figure 8-1

The \Windows\Assembly folder contains global assemblies.

Side-by-Side Execution

As I mentioned, the assembly cache contains the set of assemblies available to all applications targeting the .NET Framework. Multiple versions of the same assembly can be placed in the assembly cache, which allows applications that require different versions of the assembly or share the same assembly to execute correctly. The ability to run multiple versions of the same assembly simultaneously is known as side-by-side execution. Side-by-side execution puts an end to the so-called DLL Hell that Visual Basic programmers (and Windows users) have had to live with for so long.

Side-by-side execution allows you to create different versions of your assembly that do not need to be backward compatible. Of course, having multiple versions eliminates all sorts of coding and testing of a product and collapses the time you need to get your software to market. The common language runtime provides the infrastructure that allows multiple versions of the same assembly to run on the same computer, or even in the same process. Pretty cool.

To support shared assemblies, Windows has to be able to actually load DLLs that have the same name into a single address space or process. This ability is the method that .NET has for eliminating DLL conflicts. Search your machine for a file called mscorcfg.msc, which is a .NET Admin Tool. It's usually located in C:\Windows\Microsoft.Net\Framework\<Your version number>. When you locate the file, double-click mscorcfg.msc and view the assembly cache, shown in Figure 8-2.

Figure 8-2

The assembly cache.

The Global Assembly Cache

Each computer on which the CLR is installed has a machine-wide code area called the global assembly cache. The global assembly cache, or GAC, stores assemblies specifically designated to be shared by several applications on the computer. Usually you should not place an assembly you create in the GAC but should instead keep it in the single directory associated with your program as a private assembly.

However, if you create an assembly that will be used by multiple programs from the GAC, you can no longer replicate or install the application by using the XCOPY command to copy the files to the application's directory—you must move the assembly into the global assembly cache as well. You'll sometimes create a global assembly that acts like a classic Visual Basic DLL or an in-process server. For example, you might create a global assembly that calculates taxes or handles employee information that needs to be shared with other programs that target the .NET Framework. This would be a perfect use for a shared assembly.

But don't do this lightly. You should share assemblies by installing them into the global assembly cache only when you need to. As a general guideline, keep assembly dependencies private and locate assemblies in the application directory unless sharing an assembly is explicitly required. In addition, you do not have to install assemblies into the global assembly cache to make them accessible to unmanaged code or for interoperability with COM. Also, you must have Administrator privileges on a computer to install assemblies into the global assembly cache.

Assemblies deployed in the global assembly cache must have a strong name. When an assembly is added to the global assembly cache, the cache performs integrity checks on all files that make up the assembly. The cache performs these checks to ensure that an assembly has not been tampered with (for example, when a file has changed but the manifest does not reflect the change).

Assembly Strong Names

The GAC maintains a database file that contains, among other information, a mapping of where a specific assembly is located. Each company that builds a shared application must have a means to uniquely identify its specific assembly. As I mentioned, two companies naming their production assemblies something like EmployeeTracker is not inconceivable. If we have two assemblies, each from a different software company but both with the name EmployeeTracker, we must have a way to differentiate one from the other. This is where the strong name comes in.

A strong name includes the assembly's identity—its simple text name, version number, and culture information (if provided)—plus a public key and a digital signature. A strong name is generated from an assembly file, which contains the assembly manifest, which, in turn, contains the names and hashes (numeric values derived by applying an algorithm to data) of all the files that make up the assembly. Assemblies with the same strong name are expected to be identical. When you build a Visual Basic .NET application, you can ensure that an assembly name is globally unique by signing an assembly with a strong name. I'll show you how to do that later in the chapter.

Generally, strong names satisfy several requirements of programs that target the .NET platform. For example, strong names guarantee name uniqueness by relying on unique key pairs. No one else can generate the same assembly name that you can because an assembly name generated with one private key is different from an assembly name generated with another private key.

Strong names also protect the version lineage of an assembly. In other words, a strong name can ensure that no one can produce a subsequent version of your assembly. Users can be sure that a version of the assembly that they are loading comes from the same publisher that created the version of the assembly that the application was built with.

Finally, strong names provide a solid integrity check. When a strong-named assembly passes the .NET Framework security checks, the contents of the assembly are guaranteed not to have changed since it was built. Providing a digital signature and supporting certificate adds another level of trust. The signature and certificate are used when you sell or distribute your programs or components over the Internet.

To successfully deploy your Visual Basic .NET application, you must understand how the .NET runtime locates and binds to the assemblies that make up your application. By default, the runtime attempts to bind with the exact version of the assembly that the application was built with.

The common language runtime performs a number of steps when attempting to locate an assembly and resolve an assembly reference. Probing is the term used to describe how the runtime locates assemblies. Essentially, the CLR refers to a built-in set of heuristics that is used to locate the assembly on the basis of its name, version, and culture.

As you can see, an assembly contains quite a bit of information. First and foremost the assembly contains metadata—binary information that describes your program whether it is stored in an executable or in memory. When you compile your program, this metadata is stuffed in the file when it is converted to the Microsoft Intermediate Language (MSIL). Essentially, every type and member defined and referenced in a module or assembly is described by the metadata. When your code is executed, the runtime loads metadata into memory and references it to discover information about your code's classes, members, inheritance, and so on. Your program is completely self-describing, and the need to use the registry is unnecessary.

Metadata describes every type and member defined in your code. It contains identity information such as the name, version, culture, and public key to ensure that the assembly has not been modified or corrupted. Also, all of the types that are available and any other dependent assemblies are included in the metadata. Any security permissions are also contained. The assembly is really a self-contained unit that has everything it needs to know to run your program. The metadata is a key part for writing more robust and easily maintainable Visual Basic .NET programs.

The Other Parts of an Assembly

At the top of the diagram shown in Figure 8-3 is the assembly object. As I mentioned, one or more DLLs might make up an assembly, so references to those DLLs are stored in the assembly object. Also, any resources, such as bitmaps, that are used in your program are referenced in this object. Next is the module class. A module references a single DLL, of which there might be more than one for an assembly. Everything the runtime needs to know about that particular DLL module is stored here.

In addition, all type information about the module is stored. It's easy to think of a .NET type as a class. As we will see in our upcoming program, the Type class can be used to retrieve all metadata for an object. Each of the type classes has a collection of Type objects members. Each type contains members, such as fields, properties, events, methods, constructors, and parameters.

Figure 8-3

The Assembly object.



Coding Techniques for Microsoft Visual Basic. NET
Coding Techniques for Microsoft Visual Basic .NET
ISBN: 0735612544
EAN: 2147483647
Year: 2002
Pages: 123
Authors: John Connell

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