Professional Documents
Culture Documents
What is an assembly?
An assembly is the primary building block of a .NET Framework application. It is a collection of
functionality that is built, versioned, and deployed as a single implementation unit (as one or
more files). All managed types and resources are marked either as accessible only within their
implementation unit or as accessible by code outside that unit.
Assemblies are self-describing by means of their manifest, which is an integral part of every
assembly. The manifest:
• Establishes the assembly identity (in the form of a text name), version, culture, and digital
signature (if the assembly is to be shared across applications).
• Defines what files (by name and file hash) make up the assembly implementation.
• Specifies the types and resources that make up the assembly, including which are exported
from the assembly.
• Itemizes the compile-time dependencies on other assemblies.
• Specifies the set of permissions required for the assembly to run properly.
This information is used at run time to resolve references, enforce version binding policy, and
validate the integrity of loaded assemblies. The runtime can determine and locate the assembly
for any running object, since every type is loaded in the context of an assembly. Assemblies are
also the unit at which code access security permissions are applied. The identity evidence for each
assembly is considered separately when determining what permissions to grant the code it
contains. The self-describing nature of assemblies also helps makes zero-impact install and
XCOPY deployment feasible.
Creating multi file assemblies reduces the working set of the application when it is being called
which helps in performance improvement because the .NET runtime loads only the required
modules.
With Shared Assemblies, the problem of DLL HELL is also solved because the assembly is
identified not only by its name but also its version number hence it is possible to have an
assembly of different versions lying in the assembly cache. When the client calls an assembly, it
indicates the name and the version number of the same so there is no problem on the call being
relayed to the right assembly. The versioning and the simultaneous installs of multiple versions
apply only to Shared assemblies and not private assemblies since there is no point in having
multiple versions of a private assembly, which is being called on only by a single application.
If I want to build a shared assembly, does that require the overhead of signing and
managing key pairs?
Building a shared assembly does involve working with cryptographic keys. Only the public key is
strictly needed when the assembly is being built. Compilers targeting the .NET Framework provide
command line options (or use custom attributes) for supplying the public key when building the
assembly. It is common to keep a copy of a common public key in a source database and point
build scripts to this key. Before the assembly is shipped, the assembly must be fully signed with
the corresponding private key. This is done using an SDK tool called SN.exe (Strong Name).
Strong name signing does not involve certificates like Authenticode does. There are no third party
organizations involved, no fees to pay, and no certificate chains. In addition, the overhead for
verifying a strong name is much less than it is for Authenticode. However, strong names do not
make any statements about trusting a particular publisher. Strong names allow you to ensure that
the contents of a given assembly haven't been tampered with, and that the assembly loaded on
your behalf at run time comes from the same publisher as the one you developed against. But it
makes no statement about whether you can trust the identity of that publisher.
Where do I deploy an assembly that can be shared by more than one application?
Assemblies that are to be used by multiple applications (shared assemblies) are deployed to the
global assembly cache. Use the /i option to the GACUtil SDK tool to install an assembly into the
cache:
gacutil /i myDll.dll
How can I see what assemblies are installed in the global assembly cache?
The .NET Framework ships with a Windows shell extension for viewing the assembly cache.
Navigating to % windir%\assembly with the Windows Explorer activates the viewer.
nm1.vb
‘*********************************************************
Public Class ModuleOne
Public Shared Sub VBMethod()
System.Console.WriteLine(“ModuleOne:VBMethod”)
End Public
End Class>
'*********************************************************
The command will execute the Visual Basic compiler, which in turn produces the IL code for this
file and saves it into a new file call nm1.netmodule. I’ve saved the file with a .netmodule
extension, but you could save it with a .dll extension if you like. It is easier to differentiate the
main assembly file (types.dll) from the satellite files when their extensions aren’t the same. If
your netmodule files reference types in other assemblies don’t forget to include the /r switch of
the compiler to specify your references.
The C# File…
Nm2.cs
//*************************************************************
public class ModuleTwo
{
public static void CsharpMethod()
{
System.Console.WriteLine(“ModuleTwo:CsharpMethod()”);
}
}
//*************************************************************
The command will execute the C# compiler which produces the required IL code for the file and
saves it into a new file called nm2.netmodule.
Before I continue, I want to remind you that you’ve not compiled assemblies yet. You’ve compile
files that *can* make up an assembly. These modules aren’t considered assemblies because they
lack the necessary metadata to be completely self-describing. The metadata I talk about is more
commonly known as the manifest.
Let’s now write the code for our main assembly file. The file can be written in any other language
whose compiler supports the module paradigm I’m describing.
Types.vb
‘*******************************************************
Public Class Types
Public Shared Sub MethodOne()
System.Console.WriteLine(“Types:MethodOne”)
End Sub
End Class
‘*******************************************************
Now we must compile this file and add the previously compiled files. The compiler does not just
merge the files into one bigger file; it simply includes a link to the netmodule files in the manifest
of the assembly. Compile this last file as such:
The only new switch in this command is the /addmodule:module which is simple to understand.
You can have as many of those switches as you need. Again, make sure you include references to
any other types you use in your own assemblies. After the compilation is complete, we’ll have our
first assembly.
App.vb
‘*******************************************************
Public Class App
Public Shared Sub Main(args() As String)
System.Console.WriteLine(“App:Main”)
Types.MethodOne()
ModuleOne.VBMethod()
ModuleTwo.CsharpMethod()
End Sub
End Class
‘*******************************************************
This little test console application will present some output, and call shared methods in the
Types.dll assembly which it will reference. Compile this as such:
C:\App> vbc /t:exe /out:App.exe /r:Types.dll App.vb
One of the cool things about the presented approach is the fact that you can recompile each
netmodules and not have to recompile the entire assembly. This might be useful in testing and
the distribution of patches. This means you can add new types to your modules and consume
them in your client application on-the-fly. Keep in mind that your assembly cannot be strongly
named; if it is, then you must compile the entire assembly after you make changes to any
netmodule file.
Some might also be concerned with how the flexibility this partitioned approach brings affect
performance. Well, I have good news. Since the CLR uses the JIT compiler and then saves the
native code, the performance hit will only be felt when the App is first ran. Afterwards, the code
will execute as fast as the “regular” code.