Home Random Page


CATEGORIES:

BiologyChemistryConstructionCultureEcologyEconomyElectronicsFinanceGeographyHistoryInformaticsLawMathematicsMechanicsMedicineOtherPedagogyPhilosophyPhysicsPolicyPsychologySociologySportTourism






Nbsp;   Combining Modules to Form an Assembly

The Program.exe file discussed in the previous section is more than just a PE file with metadata; it

is also an assembly. An assembly is a collection of one or more files containing type definitions and resource files. One of the assembly’s files is chosen to hold a manifest. The manifest is another set of metadata tables that basically contain the names of the files that are part of the assembly. They also describe the assembly’s version, culture, publisher, publicly exported types, and all of the files that comprise the assembly.

The CLR operates on assemblies; that is, the CLR always loads the file that contains the manifest metadata tables first and then uses the manifest to get the names of the other files that are in the as- sembly. Here are some characteristics of assemblies that you should remember:

■ An assembly defines the reusable types.

■ An assembly is marked with a version number.

■ An assembly can have security information associated with it.

An assembly’s individual files don’t have these attributes—except for the file that contains the

manifest metadata tables.

 

To package, version, secure, and use types, you must place them in modules that are part of

an assembly. In most cases, an assembly consists of a single file, as the preceding Program.exe example does. However, an assembly can also consist of multiple files: some PE files with metadata and some resource files such as .gif or .jpg files. It might help you to think of an assembly as a logi- cal EXE or a DLL.

I’m sure that many of you reading this are wondering why Microsoft has introduced this assembly concept. The reason is that an assembly allows you to decouple the logical and physical notions of reusable types. For example, an assembly can consist of several types. You could put the frequently used types in one file and the less frequently used types in another file. If your assembly is deployed by downloading it via the Internet, the file with the infrequently used types might not ever have to be downloaded to the client if the client never accesses the types. For example, an independent soft- ware vendor (ISV) specializing in UI controls might choose to implement Active Accessibility types in a separate module (to satisfy Microsoft’s Logo requirements). Only users who require the additional accessibility features would require this module to be downloaded.

You configure an application to download assembly files by specifying a codeBase element (discussed in Chapter 3) in the application’s configuration file. The codeBase element identifies a URL pointing to where all of an assembly’s files can be found. When attempting to load an assembly’s file, the CLR obtains the codeBase element’s URL and checks the machine’s download cache to see if the file is present. If it is, the file is loaded. If the file isn’t in the cache, the CLR downloads the file into the cache from the location the URL points to. If the file can’t be found, the CLR throws a FileNotFound­ Exception exception at run time.




I’ve identified three reasons to use multifile assemblies:

 

■ You can partition your types among separate files, allowing for files to be incrementally down- loaded as described in the Internet download scenario. Partitioning the types into separate files also allows for partial or piecemeal packaging and deployment for applications you purchase and install.

■ You can add resource or data files to your assembly. For example, you could have a type that calculates some insurance information. This type might require access to some actuarial tables to make its computations. Instead of embedding the actuarial tables in your source code, you could use a tool (such as the Assembly Linker, AL.exe, discussed later) so that the data file is considered to be part of the assembly. By the way, this data file can be in any format—a text file, a Microsoft Excel spreadsheet, a Microsoft Word table, or whatever you like—as long as your application knows how to parse the file’s contents.

■ You can create assemblies consisting of types implemented in different programming lan- guages. For example, you can implement some types in C#, some types in Microsoft Visual Basic, and other types in other languages. When you compile the types written with C# source code, the compiler produces a module. When you compile other types written with Visual Basic source code, the compiler produces a separate module. You can then use a tool to combine all of these modules into a single assembly. To developers using the assembly, the assembly appears to contain just a bunch of types; developers won’t even know that different program- ming languages were used. By the way, if you prefer, you can run ILDasm.exe on each of the modules to obtain an IL source code file. Then you can run ILAsm.exe and pass it all of the IL source code files. ILAsm.exe will produce a single file containing all of the types. This tech- nique requires your source code compiler to produce IL-only code.

       
   
 
 


To build an assembly, you must select one of your PE files to be the keeper of the manifest. Or you can create a separate PE file that contains nothing but the manifest. Table 2-3 shows the manifest metadata tables that turn a managed module into an assembly.

 

TABLE 2-3Manifest Metadata Tables

 

Manifest Metadata Table Name Description
AssemblyDef Contains a single entry if this module identifies an assembly. The entry includes the assem- bly’s name (without path and extension), version (major, minor, build, and revision), culture, flags, hash algorithm, and the publisher’s public key (which can be null).
FileDef Contains one entry for each PE and resource file that is part of the assembly (except the file containing the manifest because it appears as the single entry in the AssemblyDef table). The entry includes the file’s name and extension (without path), hash value, and flags. If this assembly consists only of its own file, the FileDef table has no entries.
ManifestResourceDef Contains one entry for each resource that is part of the assembly. The entry includes the resource’s name, flags (public if visible outside the assembly and private otherwise), and an index into the FileDef table indicating the file that contains the resource file or stream. If the resource isn’t a stand-alone file (such as a .jpg or a .gif), the resource is a stream contained within a PE file. For an embedded resource, the entry also includes an offset indicating the start of the resource stream within the PE file.
ExportedTypesDef Contains one entry for each public type exported from all of the assembly’s PE modules. The entry includes the type’s name, an index into the FileDef table (indicating which of this assembly’s files implements the type), and an index into the TypeDef table. Note: To save file space, types exported from the file containing the manifest are not repeated in this table because the type information is available using the metadata’s TypeDef table.

 

The existence of a manifest provides a level of indirection between consumers of the assembly and the partitioning details of the assembly and makes assemblies self-describing. Also, note that the file containing the manifest has metadata information that indicates which files are part of the assembly, but the individual files themselves do not have metadata information that specifies that they are part of the assembly.

       
   
 
 

 

The C# compiler produces an assembly when you specify any of the following command-line switches: /t[arget]:exe, /t[arget]:winexe, /t[arget]: appcontainerexe, /t[arget]: library, or /t[arget]:winmdobj.1 All of these switches cause the compiler to generate a sin- gle PE file that contains the manifest metadata tables. The resulting file is either a CUI executable, a GUI executable, a Windows Store executable, a class library, or a WINMD library respectively.

 

 

 
 

1 When using /t[arget]:winmdobj, the resulting .winmdobj file must be passed to the WinMDExp.exe tool, which massages the metadata a bit in order to expose the assembly’s public CLR types as Windows Runtime types. The WinMDExp.exe tool does not touch the IL code in any way.


In addition to these switches, the C# compiler supports the /t[arget]:module switch. This switch tells the compiler to produce a PE file that doesn’t contain the manifest metadata tables. The PE file produced is always a DLL PE file, and this file must be added to an assembly before the CLR can ac- cess any types within it. When you use the /t:module switch, the C# compiler, by default, names the output file with an extension of .netmodule.

       
   
 
 

 

There are many ways to add a module to an assembly. If you’re using the C# compiler to build a PE file with a manifest, you can use the /addmodule switch. To understand how to build a multifile assembly, let’s assume that we have two source code files:

■ RUT.cs, which contains rarely used types

 

■ FUT.cs, which contains frequently used types

 

Let’s compile the rarely used types into their own module so that users of the assembly won’t need to deploy this module if they never access the rarely used types.

 

csc /t:module RUT.cs

 

This line causes the C# compiler to create a RUT.netmodule file. This file is a standard DLL PE file,

but, by itself, the CLR can’t load it.

 

Next let’s compile the frequently used types into their own module. We’ll make this module the keeper of the assembly’s manifest because the types are used so often. In fact, because this module will now represent the entire assembly, I’ll change the name of the output file to MultiFileLibrary.dll instead of calling it FUT.dll.

 

csc /out:MultiFileLibrary.dll /t:library /addmodule:RUT.netmodule FUT.cs

 

This line tells the C# compiler to compile the FUT.cs file to produce the MultiFileLibrary.dll file. Be- cause /t:library is specified, a DLL PE file containing the manifest metadata tables is emitted into the MultiFileLibrary.dll file. The /addmodule:RUT.netmodule switch tells the compiler that RUT.netmodule is a file that should be considered part of the assembly. Specifically, the /addmodule switch tells the compiler to add the file to the FileDef manifest metadata table and to add RUT.netmodule’s publicly exported types to the ExportedTypesDef manifest metadata table.

After the compiler has finished all of its processing, the two files shown in Figure 2-1 are created.

The module on the right contains the manifest.


       
   
 

RUT.netmodule MultiFileLibrary.dll

FIGURE 2-1A multifile assembly consisting of two managed modules, one with a manifest.

 

The RUT.netmodule file contains the IL code generated by compiling RUT.cs. This file also contains metadata tables that describe the types, methods, fields, properties, events, and so on that are de- fined by RUT.cs. The metadata tables also describe the types, methods, and so on that are referenced by RUT.cs. The MultiFileLibrary.dll is a separate file. Like RUT.netmodule, this file includes the IL code generated by compiling FUT.cs and also includes similar definition and reference metadata tables. However, MultiFileLibrary.dll contains the additional manifest metadata tables, making MultiFile- Library.dll an assembly. The additional manifest metadata tables describe all of the files that make up the assembly (the MultiFileLibrary.dll file itself and the RUT.netmodule file). The manifest metadata tables also include all of the public types exported from MultiFileLibrary.dll and RUT.netmodule.

       
   
 
 

 

After the MultiFileLibrary.dll assembly is built, you can use ILDasm.exe to examine the metadata’s manifest tables to verify that the assembly file does in fact have references to the RUT.netmodule file’s types. Here is what the FileDef and ExportedTypesDef metadata tables look like.

 

File #1 (26000001)

­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­

Token: 0x26000001 Name : RUT.netmodule

HashValue Blob : e6 e6 df 62 2c a1 2c 59 97 65 0f 21 44 10 15 96 f2 7e db c2 Flags : [ContainsMetaData] (00000000)


ExportedType #1 (27000001)

­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­

Token: 0x27000001 Name: ARarelyUsedType

Implementation token: 0x26000001 TypeDef token: 0x02000002

Flags : [Public] [AutoLayout] [Class] [Sealed] [AnsiClass] [BeforeFieldInit](00100101)

 

From this, you can see that RUT.netmodule is a file considered to be part of the assembly with the token 0x26000001. From the ExportedTypesDef table, you can see that there is a publicly exported type, ARarelyUsedType. The implementation token for this type is 0x26000001, which indicates that the type’s IL code is contained in the RUT.netmodule file.

       
   
 
 

 

Any client code that consumes the MultiFileLibrary.dll assembly’s types must be built using the

/r[eference]: MultiFileLibrary.dll compiler switch. This switch tells the compiler to load the Multi- FileLibrary.dll assembly and all of the files listed in its FileDef table when searching for an external type. The compiler requires all of the assembly’s files to be installed and accessible. If you were to delete the RUT.netmodule file, the C# compiler would produce the following error: fatal error CS0009: Metadata file 'C:\ MultiFileLibrary.dll' could not be opened—'Error importing module 'RUT.netmodule' of assembly 'C:\ MultiFileLibrary.dll'—The system cannot find the file specified'. This means that to build a new assembly, all of the files from a referenced assembly must be present.

 

As the client code executes, it calls methods. When a method is called for the first time, the CLR detects the types that the method references as a parameter, a return type, or as a local variable. The CLR then attempts to load the referenced assembly’s file that contains the manifest. If the type being accessed is in this file, the CLR performs its internal bookkeeping, allowing the type to be used. If the manifest indicates that the referenced type is in a different file, the CLR attempts to load the neces- sary file, performs its internal bookkeeping, and allows the type to be accessed. The CLR loads assem- bly files only when a method referencing a type in an unloaded assembly is called. This means that to run an application, all of the files from a referenced assembly do not need to be present.



Date: 2016-03-03; view: 710


<== previous page | next page ==>
Nbsp;   A Brief Look at Metadata | Adding Assemblies to a Project by Using the Visual Studio IDE
doclecture.net - lectures - 2014-2024 year. Copyright infringement or personal data (0.01 sec.)