Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Microsoft Visual C++ .NET Professional Projects - Premier Press

.pdf
Скачиваний:
180
Добавлен:
24.05.2014
Размер:
26 Мб
Скачать
☆

760 Project 8 .NET ASSEMBLIES WITH MANAGED C++ AND .NET

wants, and the specified version is provided to the client (and not the latest one, unless that is the version requested). This ensures that an incompatible version is not provided to the client.

NOTE

Y L F M Assembly versioning thatAuses the CLR happens at the assembly level. The

Assembly versioning is used only in cases of shared assemblies in which multiple applications use a single assembly. It is not used for private assemblies because it is not logical to maintain multiple versions of an assembly if it is to be accessed by only one application.

assembly manifest storesEthe information about the version of the referred assembly and the versions of dependent assemblies. As per the default version policy for the runtime, an applicationTcan run only with the specific version on which you

built and tested it. An exception to this is a case in which you explicitly override the version policy in configuration files.

The runtime resolves an assembly-binding request by first checking the original assembly reference, which is required to determine the version of the assembly that is to be bound. It then checks for all applicable configuration files to apply version policy. After making these checks, the runtime determines the original assembly reference and checks for redirection (if any) mentioned in the configuration files. It then determines the version that should be bound to the assembly. Finally, the runtime performs a check on the global assembly cache, the codebase in the configuration files, and the folders and subfolders of the application.

Contents of an Assembly

An assembly, in its simplest form, consists of the following elements:

Type metadata

Assembly manifest

MSIL code

Resource set

Team-Fly®

OVERVIEW OF ASSEMBLIES, MANIFESTS IN .NET

Chapter 25

761

 

 

 

 

TIP

The assembly manifest contains the assembly metadata and is required. The type metadata and resource set are required to provide useful functionality to an assembly.

You can group the preceding elements in an assembly through a number of methods. You can group these elements in a single file (say MyFirstAssembly.dll), as shown in Figure 25-1.

FIGURE 25-1 The various elements of an assembly grouped in a single file

Creating Assemblies

There are various tools and compilers provided by the .NET Framework SDK to create single-file or multifile assemblies.

A single-file assembly is the simplest form of assembly. The following are the features of a single-file assembly:

It is loaded into a single application.

Assemblies that are located outside the application into which the assembly is loaded cannot reference it.

It does not go through version checking.

It can be uninstalled by simply deleting the folder in which the assembly is placed.

762 Project 8 .NET ASSEMBLIES WITH MANAGED C++ AND .NET

In fact, for most deploying requirements, the features of a single-file assembly prove to be sufficient.

A multifile assembly is created from multiple resource files and code modules. There are various options for grouping resources and code modules into assemblies. These options are based on the following factors:

Group modules that must have the same version information

Group resources and code modules that support the manner of deployment that you are using

Versioning

Reuse

Deployment

NOTE

You can create a shared assembly if you want an assembly that can be accessed by multiple applications. In this case, the assembly can be deployed in the global assembly cache.

When creating assemblies, assembly names are important in defining the use and scope of assemblies that are being accessed by multiple applications. A single-file assembly can have a simple name, whereas it is important for a shared assembly to have a strong name. In the following section, I will explain the difference between simple and strong names and the logic for placing so much importance on assembly names.

Assembly Names

Recall that an assembly that is intended for use by a single application can have a simple name that should be unique within the application. However, if an assembly is intended for use by multiple applications, the name of the assembly gains importance. Such an assembly requires a strong name with the use of standard public key cryptography.

What distinguishes a strong name from a simple name of an assembly is the fact that a strong name consists of a simple text name (just like a simple assembly

OVERVIEW OF ASSEMBLIES, MANIFESTS IN .NET

Chapter 25

763

 

 

 

 

name) as well as a digital signature and a public key. A strong name is generated over an assembly file with the use of a corresponding private key. If you want to deploy an assembly in the global assembly cache, it is essential for you to sign an assembly with a strong name.

NOTE

An assembly file contains the assembly manifest. The assembly manifest consists of the names and hashes of all the files that constitute the assembly.

It is important to note here that when the runtime is binding to an assembly, the assembly name is not treated as case-sensitive. However, the casing that you have used to name an assembly is retained. It is advisable to consider assembly names as case-sensitive, because many .NET Framework SDK tools treat assembly names as case-sensitive.

The knowledge of standard public key cryptography is hence very important in naming an assembly that needs to be deployed in the global cache for access by multiple applications. Standard key cryptography uses certain keys, which are discussed in the next section.

Public and Private Keys

Most encryption algorithms depend on keys. A key usually consists of a set of digits containing details about encoding a message. Once a message has been encrypted using a key, the original message can be retrieved by decrypting it with the same key.

However, there are certain problems associated with this technique. For instance, if you encode a message with a key, the receiver of the message requires the key to decode the message. Thus, if you intend to send a message to multiple receivers, each receiver requires the key to decode the message. This in turn means that the key has to be accessible to a large number of people, which might lead to unauthorized people having copies of the key.

To ensure security, the public key cryptography technique uses two kinds of keys, one to code the message and the other to decode it. One of the keys is published and made into a public key. For example, a public key can be made available to

764 Project 8 .NET ASSEMBLIES WITH MANAGED C++ AND .NET

anyone so that whoever wants to send you a message uses this key to encode a message for you. You can choose to not disclose the other key to anyone (your private key) and use it to decode messages. This method ensures that nobody is able to read the messages that are intended to be read only by you.

NOTE

It is important to ensure that the private key cannot simply be calculated from the public key of a person.

The encryption method can be reversed in other cases. You can use a private key to code messages and make a public key available to everyone. Anyone who has the public key can decode the messages. However, if the message has not been encoded properly using your private key, no one will be able to decode it. This concept of using public keys ensures validity of messages that are used to generate unique assembly names.

In this context, it is useful to know how shared names are generated from keys.

Generating Shared Names from Keys

Consider a situation where you have put an assembly named NewAssembly in the global assembly cache. In such a situation, the CLR requires the assembly name to match the file name of the assembly, say NewAssembly.dll.

NOTE

A private assembly, which is deployed only in the root application folder, can have an assembly name that is different from the file name.

To generate shared names by using public key cryptography, you need to perform the following steps:

1.Generate a public and a private key for the assembly.

2.Take a cryptographic hash of the names and contents of the files in the assembly.

OVERVIEW OF ASSEMBLIES, MANIFESTS IN .NET

Chapter 25

765

 

 

 

 

3.Encode the hash by using the private key.

4.Store the hash in the manifest.

5.Include the public key in the assembly name.

After you generate a name for a shared assembly, when an application calls the assembly, the runtime uses the public key of the assembly to decode the hash. Next, the runtime reads the files and calculates a cryptographic hash for itself. It loads the assembly if the two hashes are the same.

Using this method to generate shared names ensures that there are no name clashes, because the name of the assembly consists of a public key that is randomly generated as well as the assembly name specified by the developer. Thus, the chances of the algorithm generating the same public key with the same name given by the developer are very low. This ensures that unique shared names are generated.

After the installation of an assembly, there might be a situation in which some changes are made to a file contained in an assembly. In such a situation, the changes can be easily identified because the runtime does not load an assembly where the value of the calculated hash does not match the value of the hash stored in the manifest.

Generating shared names from keys also ensures that there is no unauthorized interference, because if the private key is not disclosed to everyone, only selected people will be able to correctly encrypt the hash of the modified files.

Assembly Location

The location of an assembly governs whether the CLR is able to locate the assembly when it is referenced. The location of an assembly also determines if the assembly can be shared with other assemblies.

An assembly can be deployed at the following three locations:

The global assembly cache, which is installed wherever the CLR is installed. This is the most suitable location for an assembly if you want multiple applications to access the assembly.

On an FTP server, which requires assemblies to have strong names.

In the application’s folder or subfolders, which requires that the subfolders of an application be based on culture. Culture is a pre-defined value

766 Project 8 .NET ASSEMBLIES WITH MANAGED C++ AND .NET

that denotes the language, sublanguage, country/region, calendar, and other cultural conventions. The culture values are defined in the System::Globalization namespace. If an assembly contains information in the culture attribute, the assembly must be located in a subfolder with the culture’s name. This is the most commonly used location for deploying an assembly. These assemblies are also called satellite assemblies.

NOTE

According to the runtime, a satellite assembly is any assembly that has the culture attribute set to a value other than null. A satellite assembly consists of only resources and does not contain any executable code. The resource files can be compiled into satellite assemblies by using the assembly linker utility. Locating assemblies in appropriate folders becomes important in this case, because the created assemblies are named identically and the runtime can differentiate between them according to the culture attribute specified for each assembly and its location.

Having explained the various locations where you can deploy an assembly, I will now discuss the procedure to convert a private assembly into a shared assembly.

Converting a Private Assembly into a Shared Assembly

To convert a private assembly into a shared assembly, you need to perform the following steps:

1.Create a key pair.

2.Sign the assembly.

3.Place the assembly in the cache.

The following sections discuss these steps in detail.

Creating Key Pairs

You use the shared name sn.exe utility to create a key pair. Typically, you run this utility from the command prompt. Depending upon the flags that you append to the command at the command prompt, this utility carries out various tasks related to generation of keys, extraction of keys, and replacement of keys. You need to

OVERVIEW OF ASSEMBLIES, MANIFESTS IN .NET

Chapter 25

767

 

 

 

 

enter a command similar to the following command at the command prompt to run the utility:

sn -k mykey.snk

In the preceding command, the mykey.snk file represents the file in which the key will be stored. When you run the command, a file of the specified name containing the private and public keys is created.

Signing Assemblies

When a project is created, the key file name is left blank, implying that signing will not be done. After modifying the project, when you recompile it, the created assembly is signed with the private key (that is contained in the specified key file). You usually sign an assembly at compile time by specifying that the assembly should be signed with an appropriate key file by using attributes. You can do this by opening the AssemblyInfo.cs file and making appropriate changes. In the AssemblyInfo.cs file, you need to indicate the key file that you have created in the assembly:AssemblyKeyFile attribute. In case the key file is not located in the folder from which you are compiling, you need to either specify the complete path name or copy the key file to the folder.

You can now store the assembly in the global assembly cache.

Placing Assemblies in the Cache

You can place the assembly in the cache by using the GacUtil.exe utility. This utility is also run at the command prompt by entering a command similar to the following command:

gacutil /i:MyFile.dll

In the preceding command, MyFile.dll indicates the file that you want to place in the assembly. The /i flag specifies that the file is being placed in the assembly cache.

NOTE

In case the gacutil.exe utility is not located in the folder containing the file, then you need to specify the complete path name.

768 Project 8 .NET ASSEMBLIES WITH MANAGED C++ AND .NET

You now understand the general concepts of assemblies and their role in any

.NET application. You will learn how to implement these concepts (at least some of them) in the forthcoming chapters. I’ll now move on to this chapter’s second topic of discussion — .NET components and their interoperability with COMbased components.

Creating .NET Components

.NET components can be created using any of the .NET-compliant languages, such as C#, Visual Basic.NET, and Managed C++. Since an assembly is the smallest unit of code reuse, the components you create will be housed in assemblies. In order for a component that you create to be usable in any other .NET language, it has to follow certain rules:

The component should derive from System.ComponentModel.Component.

The class, which is exposing some functionality, should have public access.

The code should not be unmanaged or unsafe.

COM Interoperability

Even though the .NET platform provides numerous advantages, business corporations cannot simply stop using their existing components and controls and their codebases. In fact, COM is not going to die off as a platform for component development, for the following reasons:

Various COM components that already exist have already been tested and are functioning according to business rules. If you completely shift to .NET, these components require testing again.

Considerable investment (in terms of money and time both) has been made to develop COM components. Doing away with COM would mean reinvesting resources for the development of new components.

It is possible to port existing VB6 and VC++ applications to the .NET platform.

If .NET is to be used at a large scale, it needs to be interoperable with COM. The problem with this is that .NET components and COM components conform to

OVERVIEW OF ASSEMBLIES, MANIFESTS IN .NET

Chapter 25

769

 

 

 

 

different compiled standards. While .NET components conform to the Common Language Specification standard, the COM components conform to the COM binary standard. To solve this problem, Microsoft has developed various programming techniques and utility programs that can be used to generate proxy components that are recognized by both compiled standards.

COM Interop

COM interop refers to the part of the .NET Framework that is related to interoperability of COM applications and components with .NET applications and components.

Typically, COM components work directly only with other COM components. This involves the use of v-tables. In addition, every reference to a COM object increments a reference count. When a reference is freed, the reference count gets decremented. The object gets freed when the reference count reaches zero. Similarly, .NET components also work directly only with other .NET components. They exist in memory, which is managed by the CLR. A .NET component is garbage-collected when it is no longer referenced by variables at the root level.

COM interop makes:

A .NET component appears as a COM component to other COM objects or clients so that when a COM object requires using a .NET object, a COM callable wrapper (CCW) is created by the runtime. A CCW works like any other COM object. (The next section discusses the CCW.)

A COM component appears as a .NET component to other .NET objects so that when a .NET object requires using a COM object, a runtime callable wrapper (RCW) is created by the runtime. An RCW works like any other .NET object.

COM Callable Wrapper

As mentioned earlier, when a COM client calls a .NET object, the CLR creates a managed object. However, the COM client cannot reference the .NET object directly. To enable a COM client to reference a .NET object, a CCW is created. Exactly one CCW is created for each managed object even if there are multiple