Microsoft Visual C++ .NET Professional Projects - Premier Press
.pdf
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.
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
