Jump to content

Assembly (programming)

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Yobot (talk | contribs) at 06:16, 27 August 2016 (top: WP:CHECKWIKI error fixes using AWB (12082)). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

An assembly is a runtime unit consisting of types and other resources. All types in an assembly have the same version number.

Often, one assembly has only one namespace and is used by one program. But it can span over several namespaces. Also, one namespace can spread over several assemblies. In large designs, an assembly may consist of multiple files that are held together by a manifest (i.e. a table of contents).

In C#, an assembly is the smallest deployment unit used, and is a component in .NET. In Java, it is a JAR file.[1]

What’s in an Assembly? An assembly contains four kinds of things: An assembly manifest Provides information to the .NET runtime, such as the assembly’s name, version, requested permissions, and other assemblies that it references An application manifest Provides information to the operating system, such as how the assembly should be deployed and whether administrative elevation is required Compiled types The compiled IL code and metadata of the types defined within the assembly

The Assembly Manifest The assembly manifest serves two purposes: • It describes the assembly to the managed hosting environment. • It acts as a directory to the modules, types, and resources in the assembly. Assemblies are hence self-describing. A consumer can discover all of an assembly’s data, types, and functions—without needing additional files. An assembly manifest is not something you add explicitly to an assembly—it’s automatically embedded into an assembly as part of compilation.

Here’s a summary of the functionally significant data stored in the manifest: • The simple name of the assembly • A version number (AssemblyVersion) • A public key and signed hash of the assembly, if strongly named • A list of referenced assemblies, including their version and public key • A list of modules that comprise the assembly • A list of types defined in the assembly and the module containing each type • An optional set of security permissions requested or refused by the assembly (SecurityPermission) • The culture it targets, if a satellite assembly (AssemblyCulture) The manifest can also store the following informational data: • A full title and description (AssemblyTitle and AssemblyDescription) • Company and copyright information (AssemblyCompany and AssemblyCopy right) • A display version (AssemblyInformationalVersion) • Additional attributes for custom data

        Some of this data is derived from arguments given to the compiler, such as the list of

referenced assemblies or the public key with which to sign the assembly. The rest comes from assembly attributes, indicated in parentheses. You can view the contents of an assembly’s manifest with the .NET tool ildasm.exe. In Chapter 19, we describe how to use reflection to do the same programmatically.

         Specifying assembly attributes

You can control much of the manifest’s content with assembly attributes. For example: [assembly: AssemblyCopyright ("\x00a9 Corp Ltd. All rights reserved.")] [assembly: AssemblyVersion ("2.3.2.1")] These declarations are usually all defined in one file in your project. Visual Studio automatically creates a file called AssemblyInfo.cs in the Properties folder with every new C# project for this purpose, prepopulated with a default set of assembly attributes that provide a starting point for further customization.

The Application Manifest An application manifest is an XML file that communicates information about the assembly to the operating system. An application manifest, if present, is read and processed before the .NET-managed hosting environment loads the assembly—and can influence how the operating system launches an application’s process. A .NET application manifest has a root element called assembly in the XML namespace urn:schemas-microsoft-com:asm.v1: <?xml version="1.0" encoding="utf-8"?> <assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1"> </assembly> The following manifest instructs the OS to request administrative elevation:

<?xml version="1.0" encoding="utf-8"?> <assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1"> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2"> <security> <requestedPrivileges> <requestedExecutionLevel level="requireAdministrator" /> </requestedPrivileges> </security> </trustInfo> </assembly>

We describe the consequences of requesting administrative elevation in Chapter 21. Windows Store applications have a far more elaborate manifest, described in the Package.appxmanifest file. This includes a declaration of the program’s capabilities, which determine permissions granted by the operating system. The easiest way to edit this file is with Visual Studio, which presents a UI when you double-click the manifest file.

 Deploying a .NET application manifest

You can deploy a .NET application manifest in two ways: • As a specially named file located in the same folder as the assembly • Embedded within the assembly itself

As a separate file, its name must match that of the assembly’s, plus .manifest. So, if an assembly was named MyApp.exe, its manifest would be named MyApp.exe.manifest. To embed an application manifest file into an assembly, first build the assembly and then call the .NET mt tool as follows:mt -manifest MyApp.exe.manifest -outputresource:MyApp.exe;#1 The .NET tool ildasm.exe is blind to the presence of an embedded application manifest. Visual Studio, however, indicates whether an embedded application manifest is present if you double-click the assembly in Solution Explorer. Modules

  The contents of an assembly are actually packaged within one or more intermediate

containers, called modules. A module corresponds to a file containing the contents of an assembly. The reason for this extra layer of containership is to allow an assembly to span multiple files—a feature that’s useful when building an assembly containing code compiled in a mixture of programming languages.

Figure 18-1 shows the normal case of an assembly with a single module. Figure 18-2 shows a multifile assembly. In a multifile assembly, the “main” module always contains the manifest; additional modules can contain IL and/or resources. The manifest describes the relative location of all the other modules that make up the assembly. Multifile assemblies have to be compiled from the command line: there’s no support in Visual Studio. To do this, you invoke the csc compiler with the /t switch to create each module and then link them with the assembly linker tool, al.exe. Although the need for multifile assemblies is rare, at times you need to be aware of the extra level of containership that modules impose—even when dealing just with single-module assemblies. The main scenario is with reflection

The Assembly Class The Assembly class in System.Reflection is a gateway to accessing assembly metadata at runtime. There are a number of ways to obtain an assembly object: the simplest is via a Type’s Assembly property: Assembly a = typeof (Program).Assembly; or, in Windows Store applications: Assembly a = typeof (Program).GetTypeInfo().Assembly; In desktop apps, you can also obtain an Assembly object by calling one of Assem bly’s static methods:

GetExecutingAssembly Returns the assembly of the type that defines the currently executing function GetCallingAssembly Does the same as GetExecutingAssembly, but for the function that called the currently executing function GetEntryAssembly Returns the assembly defining the application’s original entry method Once you have an Assembly object, you can use its properties and methods to query the assembly’s metadata and reflect upon its types. Table 18-1 shows a summary of these functions.

Strong Names and Assembly Signing A strongly named assembly has a unique and untamperable identity. It works by adding two bits of metadata to the manifest: • A unique number that belongs to the authors of the assembly • A signed hash of the assembly, proving that the unique number holder produced the assembly.

This requires a public/private key pair. The public key provides the unique identifying number, and the private key facilitates signing. Strong-name-signing is not the same as Authenticode-signing. We cover Authenticode later in this chapter.

The public key is valuable in guaranteeing the uniqueness of assembly references: a strongly named assembly incorporates the public key into its identity. The signature is valuable for security—it prevents a malicious party from tampering with your assembly. Without your private key, no one can release a modified version of the assembly without the signature breaking (causing an error when loaded). Of course, someone could re-sign the assembly with a different key pair—but this would give the assembly a different identity. Any application referencing the original assembly would shun the imposter because public key tokens are written into references. Adding a strong name to a previously “weak” named assembly changes its identity. For this reason, it pays to give production assemblies strong names from the outset. A strongly named assembly can also be registered in the GAC. How to Strongly Name an Assembly To give an assembly a strong name, first generate a public/private key pair with the sn.exe utility: sn.exe -k MyKeyPair.snk This manufactures a new key pair and stores it to a file called MyApp.snk. If you subsequently lose this file, you will permanently lose the ability to recompile your assembly with the same identity. You then compile with the /keyfile switch: csc.exe /keyfile:MyKeyPair.snk Program.cs Visual Studio assists you with both steps in the Project Properties window. A strongly named assembly cannot reference a weakly named assembly. This is another compelling reason to strongly name all your production assemblies.

The same key pair can sign multiple assemblies—they’ll still have distinct identities if their simple names differ. The choice as to how many key pair files to use within an organization depends on a number of factors. Having a separate key pair for every assembly is advantageous should you later transfer ownership of a particular application (along with its referenced assemblies), in terms of minimum disclosure. But it makes it harder for you to create a security policy that recognizes all of your assemblies. It also makes it harder to validate dynamically loaded assemblies. Prior to C# 2.0, the compiler did not support the /keyfile switch, and you would specify a key file with the AssemblyKey File attribute instead. This presented a security risk, because the path to the key file would remain embedded in the assembly’s metadata. For instance, with ildasm, you can see quite easily that the path to the key file used to sign mscorlib in CLR 1.1 was as follows: F:\qfe\Tools\devdiv\EcmaPublicKey.snk

Obviously, you need access to that folder on Microsoft’s .NET Framework build machine to take advantage of that information! Delay Signing In an organization with hundreds of developers, you might want to restrict access to the key pairs used for signing assemblies, for a couple of reasons: • If a key pair gets leaked, your assemblies are no longer untamperable. • A test assembly, if signed and leaked, could be maliciously propagated as the real assembly.

Withholding key pairs from developers, though, means they cannot compile and test assemblies with their correct identity. Delay signing is a system for working around this problem. A delay-signed assembly is flagged with the correct public key, but not signed with the private key. A delay-signed assembly is equivalent to a tampered assembly and would normally be rejected by the CLR. The developer, however, instructs the CLR to bypass validation for the delay-sign assemblies on that computer, allowing the unsigned assemblies to run. When it comes time for final deployment, the private key holder re-signs the assembly with the real key pair. To delay-sign, you need a file containing just the public key. You can extract this from a key pair by calling sn with the -p switch: sn -k KeyPair.snk sn -p KeyPair.snk PublicKeyOnly.pk KeyPair.snk is kept secure and PublicKeyOnly.pk is freely distributed. You can also obtain PublicKeyOnly.pk from an existing signed assembly with the -e switch: sn -e YourLibrary.dll PublicKeyOnly.pk You then delay-sign with PublicKeyOnly.pk by calling csc with the /delaysign+ switch:csc /delaysign+ /keyfile: PublicKeyOnly.pk /target:library YourLibrary.cs Visual Studio does the same if you tick the “Delay sign” checkbox in Project Properties. The next step is to instruct the .NET runtime to skip assembly identity verification on the development computers running the delay-signed assemblies. This can be done on either a per-assembly or a per-public key basis, by calling the sn tool with the Vr switch: sn -Vr YourLibrary.dll Visual Studio does not perform this step automatically. You must disable assembly verification manually from the command line. Otherwise, your assembly will not execute. The final step is to fully sign the assembly prior to deployment. This is when you replace the null signature with a real signature that can be generated only with access to the private key. To do this, you call sn with the R switch: sn -R YourLibrary.dll KeyPair.snk You can then reinstate assembly verification on development machines as follows: sn -Vu YourLibrary.dll You won’t need to recompile any applications that reference the delay-signed assembly, because you’ve changed only the assembly’s signature, not its identity. Assembly Names An assembly’s “identity” comprises four pieces of metadata from its manifest: • Its simple name • Its version (“0.0.0.0” if not present) • Its culture (“neutral” if not a satellite) • Its public key token (“null” if not strongly named) The simple name comes not from any attribute, but from the name of the file to which it was originally compiled (less any extension). So, the simple name of the System.Xml.dll assembly is “System.Xml.” Renaming a file doesn’t change the assembly’s simple name.

The version number comes from the AssemblyVersion attribute. It’s a string divided into four parts as follows: major.minor.build.revision You can specify a version number as follows: [assembly: AssemblyVersion ("2.5.6.7")] AssembliesThe culture comes from the AssemblyCulture attribute and applies to satellite assemblies, described later in the section “Resources and Satellite Assemblies” The public key token comes from a key pair supplied at compile time via the /keyfile switch, as we saw earlier, in the section “How to Strongly Name an Assembly”. Fully Qualified Names A fully qualified assembly name is a string that includes all four identifying components, in this format: simple-name, Version=version, Culture=culture, PublicKeyToken=public-key For example, the fully qualified name of System.Xml.dll is: "System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" If the assembly has no AssemblyVersion attribute, the version appears as “0.0.0.0”. If it is unsigned, its public key token appears as “null”. An Assembly object’s FullName property returns its fully qualified name. The compiler always uses fully qualified names when recording assembly references in the manifest.

A fully qualified assembly name does not include a directory path to assist in locating it on disk. Locating an assembly residing in another directory is an entirely separate matter that we pick up in “Resolving and Loading Assemblies” on

The AssemblyName Class AssemblyName is a class with a typed property for each of the four components of a fully qualified assembly name. AssemblyName has two purposes: • It parses or builds a fully qualified assembly name. • It stores some extra data to assist in resolving (finding) the assembly. You can obtain an AssemblyName object in any of the following ways: • Instantiate an AssemblyName, providing a fully qualified name. • Call GetName on an existing Assembly. • Call AssemblyName.GetAssemblyName, providing the path to an assembly file on disk (desktop apps only).

You can also instantiate an AssemblyName object without any arguments and then set each of its properties to build a fully qualified name. An AssemblyName is mutable when constructed in this manner. Here are its essential properties and methods: string FullName { get; } // Fully qualified name string Name { get; set; } // Simple name Version Version { get; set; } // Assembly version CultureInfo CultureInfo { get; set; } // For satellite assemblies string CodeBase { get; set; } // Location byte[] GetPublicKey(); // 160 bytes void SetPublicKey (byte[] key); byte[] GetPublicKeyToken(); // 8-byte version void SetPublicKeyToken (byte[] publicKeyToken); Version is itself a strongly typed representation, with properties for Major, Minor, Build, and Revision numbers. GetPublicKey returns the full cryptographic public key; GetPublicKeyToken returns the last eight bytes used in establishing identity. To use AssemblyName to obtain the simple name of an assembly: Console.WriteLine (typeof (string).Assembly.GetName().Name); // mscorlib To get an assembly version: string v = myAssembly.GetName().Version.ToString(); We’ll examine the CodeBase property in the later section “Resolving and Loading Assemblies” Assembly Informational and File Versions Because an integral part of an assembly name is its version, changing the Assembly Version attribute changes the assembly’s identity. This affects compatibility with referencing assemblies, which can be undesirable when making nonbreaking updates. To address this, there are two other independent assembly-level attributes for expressing version-related information, both of which are ignored by the CLR: AssemblyInformationalVersion The version as displayed to the end user. This is visible in the Windows File Properties dialog box as “Product Version.” Any string can go here, such as “5.1 Beta 2.” Typically, all the assemblies in an application would be assigned the same informational version number. AssemblyFileVersion This is intended to refer to the build number for that assembly. This is visible in the Windows File Properties dialog box as “File Version.” As with AssemblyVersion, it must contain a string consisting of up to four numbers separated by periods.

Creation of an assembly

Creation of assemblies and modules in C#

Every compilation creates either an assembly or a module in C#. It is possible to add other modules with the assembly linker (al). A speciality of Java is to create a *.class file for each class, which is not the case in C#. The creation can be activated by compiler switches, like csc /addmodule:Y.netmodule A.cs that yields a new A.exe with Y added to this assembly.[2]

References

  1. ^ Mössenböck, Hanspeter (2002-03-25). "Advanced C#: Variable Number of Parameters" (PDF). http://ssw.jku.at/Teaching/Lectures/CSharp/Tutorial/: Institut für Systemsoftware, Johannes Kepler Universität Linz, Fachbereich Informatik. p. 37. Retrieved 2011-08-05. {{cite web}}: External link in |location= (help)
  2. ^ Mössenböck, Hanspeter (2002-03-25). "Advanced C#: Variable Number of Parameters" (PDF). http://ssw.jku.at/Teaching/Lectures/CSharp/Tutorial/: Institut für Systemsoftware, Johannes Kepler Universität Linz, Fachbereich Informatik. pp. 38–40. Retrieved 2011-08-05. {{cite web}}: External link in |location= (help)