You are on page 1of 66

Introducing .

NET Assemblies

The Role of .NET Assemblies.NET
Assembly is a versioned, self-describing binary file
hosted by the CLR.
.NET assemblies have file extensions (*.exe or *.dll)

Benefits of the assembly format.
Assemblies Promote Code Reuse
A code library (also termed a class library) is a *.dll
that contains types intended to be used by external
applications.
A referenced *.exe can also be considered a code
library.

Regardless of how a code library is packaged, the
.NET platform allows you to reuse types in a
language-independent manner.
For example, you could create a code library in C#
and reuse that
library in any other .NET programming language.


Assemblies Establish a Type Boundary
The assembly in which a type resides further establishes a
types identity.
For example, if you have two uniquely named assemblies
(say, MyCars.dll and YourCars.dll) that both define a
namespace (CarLibrary) containing a class named
SportsCar, they are considered unique types in the .NET
universe
Assemblies AreVersionable Units
.NET assemblies are assigned a four-part numerical
version number of the form
<major>.<minor>.<build>.<revision>
if you do not explicitly provide a version number using
[AssemblyVersion] attribute,the assembly is automatically
assigned a version of 0.0.0.0.
This number, in conjunction with an optional public key
value, allows
multiple versions of the same assembly to coexist in
harmony on a single machine.
Assemblies that provide public key information are termed
strongly named
Assemblies Are Self-Describing
Assemblies are regarded as self-describing in part
because they record every external assembly it must have
access to in order to function correctly.
Thus, if your assembly requires System.Windows.
Forms.dll and System.Drawing.dll, they will be documented
in the assemblys manifest .
An assembly contains metadata that describes the
composition (member names,implemented interfaces,base
classes ,constructors and so forth)of every contained type.
Assemblies Are Configurable
Assemblies can be deployed as private or shared.
Regardless of how you deploy your assemblies, you are
free to author XML-based configuration files.
Using these configuration files, the CLR can be instructed
to probe for assemblies under a specific location, load a
specific version of a referenced assembly for a particular
client, or consult an arbitrary directory on your local
machine, your network location, or a web-based URL

Understanding the Format of a .NET
Assembly
A .NET assembly (*.dll or *.exe) consists of the
following elements:
1. A Win32 file header
2. A CLR file header
3. CIL code
4. Type metadata
5. An assembly manifest
6. Optional embedded resources
1.A Win32 file header
The Win32 file header establishes the fact that the
assembly can be loaded and manipulated by the
Windows family of operating systems.
This header data also identifies the kind of application
(console-
based, GUI-based, or *.dll code library) to be hosted
by the Windows operating system.
open a .NET assembly using the dumpbin.exe utility
(via a Visual Studio 2008 command prompt) and
specify the /headers flag, you can view an assemblys
Win32 header information.
An assemblys Win32 file header information
2.The CLR File Header
The CLR header is a block of data that all .NET files
must support in order to be hosted by the CLR.
this header defines numerous flags that enable the
runtime to understand the layout of the managed file.
supply the /clrheader flag to dumpbin.exe, to get the
internal CLR header information for a given .NET
assembly.

An assemblys CLR file header information

3.CIL Code
CIL code is a platform- and CPU-agnostic
intermediate language.
At runtime, the internal CIL is compiled on the fly
(using a just-in-time [JIT] compiler) to platform- and
CPU-specific instructions.
4.Type Metadata
The metadata completely describes the format of the
contained
types as well as the format of external types
referenced by this assembly.
The .NET runtime uses this metadata to resolve the
location of types (and their members) within the binary,
lay out types in memory, and facilitate remote method
invocations.
5.Assembly Manifest
also referred to as assembly metadata).
The manifest documents each module within the
assembly, establishes the version of the assembly,
and also documents any external assemblies
referenced by the current assembly
6.Optional Assembly Resources
A .NET assembly may contain any number of
embedded resources such as application icons,
image files, sound clips, or string tables.
In fact, the .NET platform supports satellite
assemblies that contain nothing but localized
resources. This can be useful for the purposes of
building international software.
Single-File Assembly

Single-file assemblies contain all of the
necessary elements (header
information, CIL code, type metadata,
manifest, and required resources) in a
single *.exe or *.dll package. Figure
illustrates the composition of a single-
file assembly.
In this case, there is a one-to-one
correspondence between the (logical)
assembly and the underlying (physical)
binary(module) hence the term single-
file assembly
Multi file Assembly
A multifile assembly is a set of .NET *.dlls that are
deployed and versioned as a single logic unit.
one of these *.dlls is termed the primary module and
contains the assembly-level manifest (as well as
any necessary CIL code, metadata, header
information, and optional resources).
The manifest of the primary module records each of
the related *.dll files it is dependent upon .
As a naming convention, the secondary modules in a
multifile assembly take a *.netmodule file extension;
Secondary *.netmodules also contain CIL code and
type metadata, as well as a module-level manifest,
which simply records the externally required
assemblies of that specific module.
Benefits of multifile assemblies
1. They provide a very efficient way to download
content For example, assume you have a
machine that is referencing a remote multifile
assembly composed of three modules, where the
primary module is installed on the client.If the client
requires a type within a secondary remote
*.netmodule, the CLR will download the binary to
the local machine on demand to a specific location
termed the download cache
2. They enable modules to be authored using multiple
.NET programming languages (which is very helpful
in larger corporations, where individual departments
tend to favor a specific .NET language)
Building and Consuming a Single-
File Assembly.
You can use command-line compilers or Visual
Studio
2005 to create a single-file assembly. By default,
the
compiler creates an assembly file with an .exe
extension
Visual studio automatically places a copy of the
library
assembly into the directory of the client application
Building and Consuming a Single-File
Assembly.

using System;
namespace CarLibrary
{
// Represents the state of the engine.
public enum EngineState
{ engineAlive, engineDead }
// The abstract base class in the hierarchy.
public abstract class Car
{
protected string petName;
protected int currSpeed;
protected int maxSpeed;
protected EngineState egnState = EngineState.engineAlive;
public abstract void TurboBoost();
public Car(){}
public Car(string name, int max, int curr)
{
petName = name; maxSpeed = max; currSpeed = curr;
}

public string PetName
{
get { return petName; }
set { petName = value; }
}
public int CurrSpeed
{
get { return currSpeed; }
set { currSpeed = value; }
}
public int MaxSpeed
{ get { return maxSpeed; } }
public EngineState EngineState
{ get { return egnState; } }
}
}

using System;
using System.Windows.Forms;
namespace CarLibrary
{
public class SportsCar : Car
{
public SportsCar(){ }
public SportsCar(string name, int
max, int curr)
: base (name, max, curr){ }
public override void TurboBoost()
{
MessageBox.Show("Ramming
speed!", "Faster is better...");
}
}
public class MiniVan : Car
{
public MiniVan(){ }
public MiniVan(string name,
int max, int curr)
: base (name, max, curr){ }
public override void
TurboBoost()
{
// Minivans have poor
turbo capabilities!
egnState =
EngineState.engineDead;
MessageBox.Show("Time to
call AAA", "Your car is
dead");
}
}
}

Notice how each subclass implements
TurboBoost() using the Windows Forms
MessageBox
class, which is defined in the
System.Windows.Forms.dll assembly. For your
assembly to make use of
the types defined within this external assembly,
the CarLibrary project must set a reference to this
binary via the Add Reference dialog box (see
Figure 15-7), which you can access through the
Visual
Studio Project -Add Reference menu selection.
Building and Consuming a Multifile
Assembly
Build a mutifile assembly
1. Create secondary modules (*.netmodules)
csc.exe /t:module *.cs
2. Create primary module (*.dll)
csc.exe /t:library /addmodule:*.netmodule /out:*.dll *.cs
Consume a mutifile assemble
Supply onle the primary module to the compiler
csc /r: primary.dll client.cs
Building and Consuming a Multifile Assembly
a multifile assembly is simply a collection of related modules that is
deployed and versioned as a single logical unit
need to make use of the command-line compiler (csc.exe)to build
assembly
To begin, open a simple text editor (such as Notepad) and create
the following Ufo class definition
saved to a file named ufo.cs:
using System;
namespace AirVehicles
{
public class Ufo
{
public void AbductHuman()
{
Console.WriteLine("Resistance is futile");
}
}
}
To compile this class into a .NET module,
csc.exe /t:module ufo.cs
to produce a *.netmodule as opposed to a *.dll or an *.exe file)
Next, create a new file named helicopter.cs that contains the following
class definition:
using System;
namespace AirVehicles
{
public class Helicopter
{
public void TakeOff()
{
Console.WriteLine("Helicopter taking off!");
}
}
}
Given that airvehicles.dll is the intended name of the primary module of
this multifile
assembly, you will need to compile helicopter.cs using the /t:library and
/out: options

csc /t:library /addmodule:ufo.netmodule /out:airvehicles.dll
helicopter.cs
Consuming a Multifile
Assembly
Imports AirVehicles
Module Module1
Sub Main()
Dim h As New AirVehicles.Helicopter()
h.TakeOff()

' This will load the *.netmodule on demand.
Dim u As New UFO()
u.AbductHuman()
Console.ReadLine()
End Sub
End Module

To compile
vbc /r:airvehicles.dll *.vb
Understanding Shared
Assemblies
a private assembly is deployed within the same
directory as the application making use of it.
shared assemblies are installed into the GAC
The GAC is located under a subdirectory of your
Windows directory named Assembly
(e.g.,C:\Windows\Assembly), as shown in Figure

Understanding Strong Names
Before you can deploy an assembly to the GAC,
you must assign it a strong name, which is used
to uniquely identify the publisher of a given .NET
binary.
Understand that a publisher could be an
individual programmer, a department within a
given company, or an entire company at large.
a strong name is the modern day .NET equivalent
of the COM globally unique identifier (GUID)
identification scheme
Formally, a strong name is composed of a set of
related data, much of which is specified using
assembly-level attributes:
The friendly name of the assembly
The version number of the assembly (assigned
using the [AssemblyVersion] attribute)
The public key value (assigned using the
[AssemblyKeyFile] attribute)
An optional culture identity value for localization
purposes (assigned using the[AssemblyCulture]
attribute)
An embedded digital signature created using a
hash of the assemblys contents and the private
key value
Generate digital signature at
compile-time
To provide a strong name for an assembly, your
first step is to generate public/private key data
using the .NET Framework 3.5 SDKs sn.exe
utility
The sn.exe utility responds by generating a file
(typically ending with the *.snk [Strong Name
Key] file extension) that
contains data for two distinct but mathematically
related keys, the public key and the privatekey.
Strongly naming an assembly
1. Create the required key data using sn.exe
sn k MyTestKey.snk
2. Inform the C# compiler where the MyTestKey.snk
isLocated
[Assembly:
AssemblyKeyFile(@C:\MyTestKey\MyTestKey.snk
)]
3. Specify the version of a shared assembly
A .NET version is composed of the four parts:
<major>.<minor>.<build>.<version>
[Assembly: AssemblyVersion(1.0.*)]
* indicates visual studio automatically increments
the build and version number
Install a shared assembly to the GAC
Global Assembly Cache (GAC) is located under the
Assembly subdirectory (C:\Windows\Assembly)
Install a shared assembly to the GAC by dragging or
dropping the assembly to C:\Windows\Assembly
gacutil.exe allows to examine and modify the contents
ofGAC
Option Meaning
/i Install a strongly name assembly into the GAC
/u Uninstall an assembly from the GAC
/l Displays the assemblies (or a specific assembly)
inthe GAC
Delayed signing
The administrator or some trusted individual that holds
the *.snk file extracts the public key value from the *.snk
File sn.exe p myKey.snk testPublicKey.snk
Distribute the extracted testPublicKey.snk to developers
The developer specifies the delayed signing attribute in
the assembly
[Assembly: AssemblyDelaySign(true)]
[Assembly:
AssemblyKeyFile(@C:\MyKey\testPublicKey.snk)]
Disable the signature verification process when
deploying an assembly to GAC
sn.exe Vr MyAssembly.d
Exploring the System.IO
Namespace
In the framework of .NET, the System.IO
namespace is the region of the base class
libraries devoted to file-based (and memory-
based) input and output (I/O) services.
System.IO defines a set of classes, interfaces,
enumerations, structures, and delegates, most of
which are contained
in mscorlib.dll.
In addition to the types contained within
mscorlib.dll, the System.dllassembly defines
additional members of the System.IO namespace
The Directory(Info) and File(Info)
Types
System.IO provides four types that allow you to
manipulate individual files, as well as interact with
a machines directory structure
The Abstract FileSystemInfo Base
Class
The DirectoryInfo and FileInfo types receive many
behaviors from the abstract FileSystemInfo base
class.

Working with the DirectoryInfo
Type
This class contains a set of members used for creating,
moving, deleting, and enumerating over directories and
subdirectories

class Program
{
static void Main(String[] args)
{

Console.WriteLine("***** Fun with Directory(Info)
*****\n");
DirectoryInfo dir = new DirectoryInfo(@"C:\Windows");

// Dump directory information.
Console.WriteLine("***** Directory Info *****");
Console.WriteLine("FullName: {0}", dir.FullName);
Console.WriteLine("Name: {0}", dir.Name);
Console.WriteLine("Parent: {0}", dir.Parent);
Console.WriteLine("Creation: {0}", dir.CreationTime);
Console.WriteLine("Attributes: {0}", dir.Attributes);
Console.WriteLine("Root: {0}", dir.Root);
}
}
Enumerating Files with the DirectoryInfo
Type
Console.WriteLine("***** Fun with Directory(Info) *****\n");
DirectoryInfo dir = new DirectoryInfo(@"C:\Windows");
FileInfo[] bitmapFiles = dir.GetFiles("*.jpg");
Console.WriteLine("Found {0} *.bmp files\n",
bitmapFiles.Length);
foreach (FileInfo f in bitmapFiles)
{
Console.WriteLine("***************************\n");
Console.WriteLine("File name: {0}", f.Name);
Console.WriteLine("File size: {0}", f.Length);
Console.WriteLine("Creation: {0}", f.CreationTime);
Console.WriteLine("Attributes: {0}", f.Attributes);
Console.WriteLine("***************************\n");
}
Creating Subdirectories with the
DirectoryInfo Type

You can programmatically extend a directory
structure using the
DirectoryInfo.CreateSubdirectory() method.
This method can create a single subdirectory, as
well as multiple nested subdirectories
// Now make a new directory on the root:
// Create \MyFoo
DirectoryInfo d = dir.CreateSubdirectory("MyFoo");
Console.WriteLine("Created: {0}", d.FullName);
// Create \MyBar\MyQaaz
d = dir.CreateSubdirectory(@"MyBar\MyQaaz");
Console.WriteLine("Created: {0}", d.FullName);
Working with the Directory Type
// Get all drives.
string[] drives = Directory.GetLogicalDrives();
Console.WriteLine("Here are your drives:");
foreach (string s in drives)
Console.WriteLine("--> {0}", s);
// Now delete what we made.
Console.WriteLine("Press Enter to delete directories");
Console.ReadLine();
try
{
Directory.Delete(@"C:\Windows\MyFoo");
Directory.Delete(@"C:\Windows\MyBar", true);
}
catch (IOException e)
{
Console.WriteLine(e.Message);
}
Working with the FileInfo Class
The FileInfo class allows you to obtain details
regarding existing files on your hard drive (time
created, size, file attributes, and so forth)
and aids in the creation, copying, moving, and
destruction of files.
In addition to the set of functionality inherited by
FileSystemInfo are some core members unique to
the FileInfo class, which are described in Table
The FileInfo.Create() Method
static void Main(string[] args)
{
// Make a new file on the C drive.
FileInfo f = new FileInfo(@"C:\Test.dat");
FileStream fs = f.Create();
// Use the FileStream object...
// Close down file stream.
fs.Close();
}
Notice that the FileInfo.Create() method returns a FileStream
type, which exposes synchronous and asynchronous write/read
operations to/from the underlying file
The FileInfo.Open() Method
FileInfo.Open() method to open existing files as well as
create new files with far more precision than
FileInfo.Create(),
Open() typically takes several parameters to qualify the
overall structure of the file you are manipulating.
static void Main(string[] args)
{
// Make a new file via FileInfo.Open().
FileInfo f2 = new FileInfo(@"C:\Test2.dat");
using(FileStream fs2 = f2.Open(FileMode.OpenOrCreate,
FileAccess.ReadWrite, FileShare.None))
{
// Use the FileStream object...
}
}
This version of the overloaded Open() method requires three parameters.
The first parameter specifies the general flavor of the I/O request

public enum FileMode
{
//Informs the OS to make a new file. If it already exists, an IOException is
thrown.
CreateNew,
//Informs the OS to make a new file. If it already exists, it will be overwritten
Create,
//Opens an existing file. If the file does not exist, a FileNotFoundException
is thrown.
Open,
//,Opens the file if it exists; otherwise, a new file is created.
OpenOrCreate,
//Opens a file and truncates the file to 0 bytes in size.
Truncate ,
//Opens a file, moves to the end of the file, and begins write operations. If
the file does not exist, a new file is created.
Append
}
The second parameter, a value from the FileAccess
enumeration, is used to determine the read/write
behavior of the underlying stream




Finally, you have the third parameter, FileShare, which
specifies how the file is to be shared among other file
handlers.
public enum FileShare
{
None,
Read,
Write,
ReadWrite
}
public enum
FileAccess
{
Read,
Write,
ReadWrite
}
The FileInfo.OpenRead() and FileInfo.OpenWrite()
Methods
These methods return a properly configured read-only or write-only
FileStream type, without the need to supply various enumeration
values return a FileStream object.
static void Main(string[] args)
{
// Get a FileStream object with read-only permissions.
FileInfo f3 = new FileInfo(@"C:\Test3.dat");
using(FileStream readOnlyStream = f3.OpenRead())
// Use the FileStream object...
readOnlyStream.Close();
// Now get a FileStream object with write-only permissions.
FileInfo f4 = new FileInfo(@"C:\Test4.dat");
using(FileStream writeOnlyStream = f4.OpenWrite())
// Use the FileStream object...
writeOnlyStream.Close();
}
The FileInfo.OpenText() Method
the OpenText() method returns an instance of the
StreamReader type, rather than a FileStream
type.
static void Main(string[] args)
{
// Get a StreamReader object.
FileInfo f5 = new FileInfo(@"C:\boot.ini");
using(StreamReader sreader = f5.OpenText())

// Use the StreamReader object...
Sreader.Close();
}
The FileInfo.CreateText() and FileInfo.AppendText()
Methods
The final two methods are CreateText() and AppendText(),
both of which return a StreamWriter reference, as shown
here:

static void Main(string[] args)
{
FileInfo f6 = new FileInfo(@"C:\Test5.txt");
using(StreamWriter swriter = f6.CreateText())
// Use the StreamWriter object...
Swriter.Close()
FileInfo f7 = new FileInfo(@"C:\FinalTest.txt");
using(StreamWriter swriterAppend = f7.AppendText())
// Use the StreamWriter object...
swriterAppend.Close();

}
Working with the File Type
File supplies AppendText(), Create(), CreateText(), Open(),OpenRead(),
OpenWrite(), and OpenText()methods.
static void Main(string[] args)
{
// Obtain FileStream object via File.Create().
FileStream fs = File.Create(@"C:\Test.dat")
// Obtain FileStream object via File.Open().
FileStream fs2 = File.Open(@"C:\Test2.dat", FileMode.OpenOrCreate,
FileAccess.ReadWrite, FileShare.None))
// Get a FileStream object with read-only permissions.
FileStream readOnlyStream = File.OpenRead(@"Test3.dat")

// Get a FileStream object with write-only permissions.
FileStream writeOnlyStream = File.OpenWrite(@"Test4.dat")
// Get a StreamReader object.
StreamReader sreader = File.OpenText(@"C:\boot.ini")
// Get some StreamWriters.
StreamWriter swriter = File.CreateText(@"C:\Test3.txt")
StreamWriter swriterAppend = File.AppendText(@"C:\FinalTest.txt")
}
New .NET 2.0 File Members
The Abstract Stream Class
A stream represents a chunk of data flowing
between a source and a destination.
Streams provide a common way to interact with a
sequence of bytes, regardless of what kind of
device (file, network connection, printer, etc.) is
storing or displaying the bytes.
The abstract System.IO.Stream class defines a
number of members that provide support for
synchronous and asynchronous interactions with
the storage medium (e.g., an underlying file or
memory location).
Working with FileStreams
The FileStream class provides an implementation
for the abstract Stream members in a manner
appropriate for file-based streaming.
It is a primitive stream; it can read or write only a
single
byte or an array of bytes.
make use of various stream wrappers, to directly
interact with the members of the FileStream type
which make it easier to work with textual data or
.NET types.
System.Text namespace defines a type named
Encoding, which provides members that encode
and decode strings to (or from) an array of bytes
// Obtain a FileStream object.
FileStream fStream = File.Open(@"C:\myMessage.dat,
FileMode.Create);
// Encode a string as an array of bytes.
string msg = "Hello!";
byte[] msgAsByteArray = Encoding.Default.GetBytes(msg);
// Write byte[] to file.
fStream.Write(msgAsByteArray, 0, msgAsByteArray.Length);
// Reset internal position of stream.
fStream.Position = 0;
// Read the types from file and display to console.
Console.Write("Your message as an array of bytes: ");
byte[] bytesFromFile = new byte[msgAsByteArray.Length];
for (int i = 0; i < msgAsByteArray.Length; i++) {
bytesFromFile[i] = (byte)fStream.ReadByte();
Console.Write(bytesFromFile[i]); }
Console.Write("\nDecoded Message: ");
Console.WriteLine(Encoding.Default.GetString(bytesFromFile));
// Close stream and delete file.
fStream.Close();
}
Working with StreamWriters and StreamReaders
These classes are useful whenever you need to
read or write character-based data (e.g., strings).
Both of these types work by default with Unicode
characters; however,you can change this by
supplying a properly configured
System.Text.Encoding object reference
StreamReader derives from an abstract type
named TextReader.this base class provides a
very limited
set of functionality to each of these
descendents, specifically the ability to read and
peek into a character stream.
The StreamWriter type derives from an abstract
base class named TextWriter. This class defines
members that allow derived types to write textual
data to a given character stream
BinaryWriters and
BinaryReaders
BinaryReader and BinaryWriter, derive directly
from System.Object. These types allow you to
read and write discrete data types to an
underlying stream
BinaryWriters and
BinaryReaders

You might also like