|
Introduction to assemblies
The C# Column - Yashawant Kanetkar
The
assembly is an important element of .NET programming. On the .NET
platform, an assembly is a unit of reuse, versioning, security and
deployment. In order to package and deploy our types, they must
be placed into modules that are part of an assembly. Every managed
application in .NET is deployed as an assembly. It means that the
entire .NET code on compilation gets converted into an Intermediate
Language (IL) code and gets stored as an assembly. In addition to
the IL code, an assembly also contains Assembly metadata (Manifest),
Type metadata and Resources. Assemblies are hence self-describing.
Let us peep into the structure of an assembly.
Structure of
an assembly
If an assembly is to be made up of several programs, the programs
can be stored in separate modules. Suppose there are two source
files a.cs and b.cs of which, a.cs
is stored in a module, whereas, b.cs is stored in the
assembly itself. Both comprise an assembly named mydll.dll.
The structure of the assembly mydll.dll would look as
shown in the following figure.
The PE file a.dll is said to
be stored in a module of the mydll.dll assembly. Had
mydll.dll contained resources, they would also get added
in the structure with the type metadata and manifest. The assembly
description shown in manifest contains the identity of the
assembly, consisting of its name, version and culture. An assembly
has a four part version number e.g. 1.0.1.1. The parts are <Major>
. <Minor> . <Build> . <Revision>. Other contents
are the names of all the files in the assembly, information regarding
whether all the types defined in the assembly are visible to other
assemblies or private to one, a hash of all files in the assembly
and details of any security permissions that clients need to have
in order to be able to run the assembly.
Private and shared
assemblies
We can create two types of assembliesprivate assemblies and
shared assemblies. A private assembly is used by only one application
while a shared assembly is shared amongst different applications.
By default, when a C# program is compiled,
the assembly produced will be a private assembly. This assembly
(DLL/EXE) should be placed in the same folder as the calling application.
With a private assembly its not necessary to think about naming
conflicts with other classes or versioning problems because each
application has its own copy of the assembly. In shared assemblies
we have to ensure that the assembly is unique, and therefore, give
it a unique name (called strong name).
In this article we would see how to build
and use a shared assembly.
Let us write a class library consisting
of a function called display( ). We will call this function from
a client program, which we will create later. Here is code of the
display( ) method, which is defined in the mytext class.
public class mytext
{
public void display ( Form fr, string s, Color c, string fname,
int size, Point pt )
{
Graphics g = fr.CreateGraphics();
Font myfont = new Font(fname, size);
SolidBrush mybrush = new SolidBrush(c);
g.DrawString(s, myfont, mybrush, pt);
}
}
The display() method simply draws text
with the specified Font and Brush. On building the project a fontassembly.dll
file would get created in the fontassembly\bin\Debug
subfolder.
A shared assembly is always unique. To
be uniquely identified, a shared assembly has a strong name. The
combination of a file name, a public key, a version number and culture
(locale details) gives an assembly a strong name, which is guaranteed
to be unique. Now lets first see what are public and private
keys and then see how to build them.
Public keys,
private keys and encryption
Encryption is the act of encoding files/programs so that others
not privy to the decryption mechanism cannot understand the content
of the text. Thus encryption is the process of hiding our data from
anyone we feel should not be allowed to read it. Under the .NET
platform this has special significance since here we distribute
our IL code embedded in assemblies, instead of the executable machine
code, which can potentially be deciphered without too much difficulty.
.NET has a strong inbuilt mechanism to maintain the privacy of code.
If this had not been the case, any one with the ILDASM.EXE utility
would have an access to your code. Not only that, he or she would
also have the opportunity to distribute it under his name. .NET
achieves this security by public key encryption.
The public key and private keys are complementary
entities; one is meaningless without the other. The creation of
public and private keys is carried out by complicated mathematical
algorithms. A document encrypted with one key can be decrypted only
by using the other key. Not even the key with which the data was
encrypted can be used to decrypt it back. The public key can be
distributed freely without any fear of it being misused.
Building the
keys
To build public and private keys we have to use the sn (short for
strong name) utility. This utility generates a public/private key
pair. The public key created using this utility will be used to
define a strong name. To create the key pair we must type the following
at command prompt:
C:\CSharp\fontassembly>sn k mykey.snk
On executing the utility a file called
mykey.snk gets created. This file contains the public and private
keys.
To create a strong name, the compiler uses
the public key from the mykey.snk file, hence we must provide this
file to the compiler. To do so we would have to open the AssemblyInfo.cs
file of the project. In this file we would have to set the AssemblyKeyFile
attribute to the .snk file path. The attribute must
be either set to an absolute path to the key file or if the file
is copied to the local directory of the project, only the file name
must be mentioned. We need to add the following to the AssemblyInfo.cs
file.
[assembly: AssemblyKeyFile(mykey.snk)]
We should also change the version number
of our assembly as shown below.
[assembly: AssemblyVersion(1.0.1.1)]
On rebuilding the program, the assembly
would now use the public key from the mykey.snk file along with
the name of the assembly, the version number and the culture to
build a strong name. Culture is a combination of a two-letter word
indicating the language and a two-letter word indicating the country
code. The strong name never contains the private key. This ensures
that the private key is not written in the manifest and hence is
not made available to everybody. This strong name is written in
the manifest of the assembly.
Along with the strong name, the compiler
also generates a cryptographic hash from the names and contents
of the files in the assembly. The compiler then encrypts the cryptographic
hash for the assembly using the private key (available in mykey.snk).
It places the encrypted hash in the manifest of the assembly. This
hash would be used by the client to check the authenticity of the
assembly.
So, in all, two things are written inside
the manifestthe strong name and the encrypted hash of the
names and contents of the files. The process of writing the encrypted
hash value in the manifest is called Signing of an assembly.
Now our shared assembly is ready. To make it available to all the
clients we would have to install it in the Global Assembly Cache.
All the shared assemblies available on a system are stored in the
Global Assembly Cache. This store is located in the <drive>:\Windows\Assembly
folder.
Installing the
shared assembly
To install a shared assembly in the global cache we would have to
use the Global Assembly Cache utility tool called gacutil along
with the /i option. Here /i stands for install. This is what we
must type at the command prompt to install the
assembly.
C:\CSharp\fontassembly\bin\Debug>gacutil /i fontassembly.dll
If the assembly is successfully installed,
the following message would appear:
Assembly successfully added to the cache
Building the
client
Let us now build a client that would use the assembly that we created
above. To create the client select a Windows Application
project and name it as fontclient
When we click on the form, some text should
be displayed at the position where we click. This can be achieved
by calling the display( ) method from fontassembly.dll
in the Form1_MouseDown( ) handler. To have access to this method
we have added a reference of the mytext class as a data member of
the Form1 class. The Form1_MouseDown( ) handler is shown below.
private void myform_MouseDown ( object sender,
System.Windows.Forms.MouseEventArgs e)
{
if(e.Button == MouseButtons.Left)
{
Point pt = new Point ( e.X, e.Y ) ;
t.display ( this, Hello, Color.Red, Comic Sans MS, 30, pt );
}
}
Here we have first checked whether the
left mouse button has been clicked. If so then we have called the
display( ) method of the mytext class from the fontassembly assembly.
For the mytext class to become available
we have added the statement using fontassembly ;. However this is
not enough. We also need to add a reference to the library. To do
this we should right click on References in the Solution
Explorer window and select Add References. On doing
so the Add References window would appear.
Execute the program and click on the form,
a string Hello would appear at the point where mouse
is clicked. The entire process of creation of a shared assembly
is illustrated in the following figure.
 |
Yashavant
Kanetkar, one of the first Express Computer columnists, is an
established software expert, speaker and author with several
best-sellers to his credit, including titles like “Let Us C”
and the “Fundas” series. Contact him at kanet@nagpur.dot.net.in |
|