What are Assemblies?

 
Chapter 8 - Assemblies
bySimon Robinsonet al.
Wrox Press 2002
  

Before the .NET Platform was introduced we had to deal with the predecessors of assemblies: normal DLLs exporting global functions, and COM DLLs exporting COM classes. Microsoft itself introduced the phrase "DLL-Hell" to describe traditional problems with DLLs problems that we know all too well.

Often applications break because a newly installed application overwrites a DLL that was also used by another application. Sometimes it happens that the installation replaces a new DLL with an old one, because the installation program doesn't correctly check the versions, or the versions are not correctly set. More often, an old DLL is replaced by a new version. Normally, this shouldn't be a problem, but the reality is different. Although the new DLL should be backwardly compatible with the old version, sometimes it isn't. This situation arises far too often.

Windows 2000 introduced the side-by-side feature that allows the installation of DLLs in the application's directory. With side-by-side, a different version of an already-installed, shared DLL may be installed to the directory of the application. The LoadLibrary() Win32 API call was rewritten so that it first checks for a .local file in the application directory. If it's found, the API first checks if a DLL was in the same directory of the application, before the other mechanisms are used to find a shared DLL. This also modifies the fixed path that is in the Registry for COM DLLs. Side-by-side is an afterthought, and doesn't solve all of the issues, and also introduces some new problems with COM DLLs. Another feature of Windows 2000 that deals with DLL-Hell is file protection: system-DLLs are protected from being overwritten by unauthorized parties. All of these Windows 2000 features treat the symptoms and not the causes.

The versioning problems of DLLs exist because it's not clear which version of a specific DLL each application needs. Dependencies are not tracked or enforced with the traditional DLL architecture. COM DLLs seem to solve a lot of the DLL problems because of a better separation of the implementation and the interface. The interface is a contract between the client and the component, which, according to COM rules, may never be changed, and thus can't break. However, even with COM, changes of implementations can break existing applications.

Side-by-side also supports COM DLLs. If you're ever tried side-by-side with COM DLLs, you have seen it's just a hack. New problems arise when using side-by-side COM DLLs. Also, if we're not uninstalling, rather we're installing the new DLL over the old one, what happens when two versions of the same component use different threading configurations? The configuration information is taken from the last installed version. This problem exists because the configuration of a COM component is not stored in the component DLL itself, but in the Registry instead.

The Answer to DLL Hell

The .NET platform's answer to DLL Hell and all of its problems is assemblies . Assemblies are self-describing installation units, consisting of one or more files. One assembly could be a single DLL or EXE that includes metadata, or it can be made of different files, for example, resource files, metadata, DLLs, and an EXE. Installation of an assembly can be as simple as copying all of its files. An xcopy installation can be done. Another big feature of assemblies is that they can be private or shared . With COM this differentiation doesn't exist, since practically all COM components are shared. If you search for a COM component in the Registry or using OleView , you have to walk through hundreds and hundreds of components. Only a small number of these components were ever meant to be used from more than one application, but every component must have a global unique identifier (GUID).

There's a big difference between private and shared assemblies. Many developers will be happy with just private assemblies. No special management, registration, versioning, and so on need to be done with private assemblies. The only application that could have version problems with private assemblies is your own application. The private components you use within your application are installed at the same time as the application itself. Local application directories are used for the assemblies of the components, so you shouldn't have any versioning problem. No other application will ever overwrite your private assemblies. Of course it is still a good idea to use version numbers for private assemblies, too. This helps a lot with code changes, but this is not a requirement of .NET.

With private assemblies you can still have versioning problems during development time. Let's see an example: if a component you use in your application references version 1 of assembly X , and you use version 2 of assembly X in your application, which version of the assembly is copied to your application directory?

The answer to this depends on what assembly you referenced first this versioning problem must be solved during development time. On the installed system, a hot fix can be easily applied to an application by simply replacing a private assembly with a new version. The only application that could have problems with the new version is the one where this fix is applied, as no other applications can be influenced.

When using shared assemblies, several applications can use this assembly and have a dependency on it. With shared assemblies, many rules must be fulfilled. A shared assembly must have a special version number, a unique name , and usually it's installed in the global assembly cache .

Features of Assemblies

The features of assemblies can be summarized as follows :

  • Assemblies are self-describing. It's no longer necessary to pay attention to Registry keys for apartments, to get the type library from some other place, and so on. Assemblies include metadata that describes the assembly. The metadata includes the types exported from the assembly and a manifest; we'll look at exactly what a manifest is in the next section.

  • Version dependencies are recorded inside an assembly manifest. By storing the version of any referenced assemblies in the manifest of the assembly, we are able to know exactly the version number of the referenced assembly that was used during development. The version of the referenced assembly that will be used can be configured by the developer and the system administrator. In a later section of this chapter, we will look at which version policies are available, and how they work.

  • Assemblies can be loaded side-by-side . Using Windows 2000 we already have a side-by-side feature where different versions of the same DLL can be used on a system. .NET extends this functionality of Windows 2000, allowing different versions of the same assembly to be used inside a single process! Maybe you're asking where this could be useful? If assembly A references version 1 of the shared assembly Shared , and assembly B uses version 2 of the shared assembly Shared , and you are using both assembly A and B , guess which versions of the shared assembly Shared are needed in your application you need both, and with .NET both versions are loaded and used.

  • Application isolation is assured using application domains . With application domains a number of applications can run independently inside a single process. Faults in one application cannot directly affect other applications inside the same process.

  • Installation can be as easy as copying the files that belong to an assembly. An xcopy can be enough. This feature is named zero-impact installation .

Why the Microsoft Windows Installer (MSI) is Still Important

I'm often asked why the Microsoft Windows Installer is still needed when xcopy is enough to install .NET applications. The simple answer is that we often want more than the simple copying of files when installing Windows applications.

Usually, you want to access the application from the Start menu, install it in a subdirectory of Program Files , let the user choose some options, show copyright screens, and so on. The Windows Installer supports a lot of additional features that can't be solved using assemblies. Applications can use their own Registry settings, group policies for easier management where specific users can access specific features, advertisement for installing parts of the application later, when requested by the user, and the repair feature to easily do repairs when files have become corrupted.

Application Domains and Assemblies

Before .NET, processes were used as isolation boundaries, with every process having its private virtual memory; an application running in one process cannot write to the memory of another application and thereby crash the other application. The process is used as an isolation and security boundary between applications. With the .NET architecture we have a new boundary for applications: application domains . With managed IL code the runtime can ensure that access to the memory of another application inside a single process can't happen. Multiple applications can run in a single process within multiple application domains:

click to expand

An assembly is loaded into an application domain. In the above figure you see process 4711 with two application domains. In application domain A , the objects one and two are instantiated , one probably in assembly One , and two in assembly Two . The second application domain in process 4711 has an instance one . To minimize memory consumption, the code of assemblies is only loaded once into an application domain. Instance and static members are not shared between application domains. It's not possible to directly access objects within another application domain; a proxy is needed instead. So in the figure above, the object one in application domain B cannot directly access the objects one or two in application domain A without a proxy. You can read more about proxies and communication across application domains in Chapter 21.

The AppDomain class is used to create and terminate application domains, load and unload assemblies and types, and to enumerate assemblies, and threads in a domain. Let's code a small example to see application domains in action.

Firstly, I'm creating a C# Console Application AssemblyA . I'm just doing a Console.WriteLine() in the Main() method so that we can see when this method gets called. In addition, I've added a constructor with two int values as arguments, so that we can also see how to create instances with the AppDomain class. The AssemblyA.exe assembly will be loaded from the second application that we're creating:

 namespace Wrox.ProCSharp.Assemblies.AppDomains {    class Class1    {   public Class1(int val1, int val2)     {     Console.WriteLine("Constructor with the values {0}, {1}" +     " in domain {2} called", val1, val2,     AppDomain.CurrentDomain.FriendlyName);     }   [STAThread]       static void Main(string[] args)       {   Console.WriteLine("Main in domain {0} called",     AppDomain.CurrentDomain.FriendlyName);   }    } } 

The second project created is again a C# Console Application: DomainTest . First, I'm displaying the name of the current domain. With the CreateDomain() method, a new application domain with the friendly name New AppDomain is created. Then we load the assembly AssemblyA into the new domain and call the Main() method by calling ExecuteAssembly() :

 using System; namespace Wrox.ProCSharp.Assemblies.AppDomains {    class Test    {       [STAThread]       static void Main(string[] args)       {   AppDomain currentDomain = AppDomain.CurrentDomain;     Console.WriteLine(currentDomain.FriendlyName);     AppDomain secondDomain = AppDomain.CreateDomain("New AppDomain");     secondDomain.ExecuteAssembly("AssemblyA.exe");   }    } } 

Before starting the program DomainTest.exe , we have to copy the assembly AssemblyA.exe to the directory of DomainTest.exe so that the assembly can be found. It's not possible to add a reference to AssemblyA.exe , because Visual Studio .NET only supports adding references to assemblies stored in DLL formats, and not EXE formats. However, this is possible from the command line. If the assembly cannot be found, we get a System.IO.FileNotFoundException exception.

When DomainTest.exe is run, we see this console output:

click to expand

DomainTest.exe is the friendly name of the first application domain. The second line is the output of the newly loaded assembly in the New AppDomain . With a process viewer you will not see the process AssemblyA.exe executing because there's no new process created. AssemblyA is loaded into the process DomainTest.exe .

Instead of calling the Main() method in the newly loaded assembly, you can also create a new instance. In the following example I'm replacing the ExecuteAssembly() method with a CreateInstance() . The first argument is the name of the assembly, AssemblyA . The second argument defines the type that should be instantiated: Wrox.ProCSharp.Assemblies.AppDomains.Class1 . The third argument, true , means that case is ignored. System.Reflection.BindingFlags.CreateInstance is a binding flag enumeration value to specify that the constructor should be called:

 AppDomain secondDomain = AppDomain.CreateDomain("New AppDomain");   // secondDomain.ExecuteAssembly("AssemblyA.exe");     secondDomain.CreateInstance("AssemblyA",     "Wrox.ProCSharp.Assemblies.AppDomains.Class1", true,     System.Reflection.BindingFlags.CreateInstance,     null, new object[] {7, 3}, null, null, null);   

With a successful run we get this console output:

click to expand

We have seen how to create and call application domains. In runtime hosts , application domains are created automatically. ASP.NET creates an application domain for each web application that runs on a web server. Internet Explorer creates application domains in which managed controls will run. For applications, it can be useful to create application domains if you want to unload an assembly. Unloading assemblies can only be done by terminating an application domain.

  


Professional C#. 2nd Edition
Performance Consulting: A Practical Guide for HR and Learning Professionals
ISBN: 1576754359
EAN: 2147483647
Year: 2002
Pages: 244

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net