среда, 16. март 2016.

Šta su sklopovi?


Još pre pojave .Net platforme, rad sa *.dll datotekama koje izvoze globalne funkcije i *.dll datoteke koje izvoze COM klase, su pravile ogromne probleme, da je Microsoft sa pravom uveo frazu „DLL pakao“ za navedenu problematiku. Aplikacije nisu radile ispravno jer bi novo instalirana aplikacija prebrisala *.dll datoteku koju je koristila druga aplikacija. Ponekad bi instalacija nove aplikacije zamenila novi *.dll starijom verzijom ili novi *.dll datoteka nije bila kompatibilna sa starijom verzijom. Tek od Windows 2000 se uvela mogućnost instaliranja *.dll datoteka u direktorijum aplikacije. Najveći problem je bio što konfiguracija COM komponente nije bila smeštena u samoj *.dll komponenti, nego u bazi Windows Registry. Za ovaj pakao, kao odgovor je stigao sklop.


( GAC - Global Assembly Cashe Folder )

Sklop je osnovna jedinica aplikacije .Net-a. Sklop je samoopisujuća kolekcija koda, resursa i meta podataka. Jednostavno govoreći, sklop je projekat koji se kompajlira u izvršnu datoteku *.exe ili u *.dll datoteku biblioteke. Sklopovi mogu biti privatni ili javni tj. deljeni. Sklop koji se nalazi u direktorijumu vaše aplikacije je privatan sklop, dok deljeni sklop je sklop smešteni u GAC - Global Assembly Cache pomoću alata Gacutil.exe ili Windows Installer-a. To su sklopovi smešteni u C:\Windows\Microsoft.NET\assembly\GAC_MSIL folderu. Deljene sklopove ili sklopove koje se nalaze u GAC-u može da koristi više aplikacija, dok privatni sklop može da koristi samo aplikacija koja sadrži sklop u svom direktorijumu. Da bi ste kreirali deljeni sklop neophodno je da vašem sklopu dodelite Strong name – strogo ime.

var assembly = Assembly.GetExecutingAssembly();
WriteLine(assembly.FullName);
// Result: AssemblyCS, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

Kao što vidite strogo ime se sastoji od imena sklopa, verzije, kulture i parnog ključa. Navedena aplikacija u kodu AssemblyCS nema dodeljeno strogo ime, što definitivno ukazuje da je reč o privatnom sklopu. Najvažnija stvar za jedan sklop je deklaracija sklopa koja sadrži informacije o tome od čega se sastoji sklop. Sklop može da sadrži samo jednu deklaraciju sklopa i ona sadrži sve podatke opisa za sklop. Deklaracija sklopa može da se nalazi u svojoj odvojenoj datoteci ili da se nalazi unutar jednog od modula sklopa.

Šta sadrži sklop?

Sklop se deli na četiri celine:

  • Manifest sklopa ili meta podaci - informacije o sklopu izložene modulu CLR-a
  • Tip meta podataka - informacije o tipovima podataka koji postoje u okviru sklopa
  • IL kod - kod prelaznog jezika za sklop
  • Resursi - izvorne datoteke koje predstavljaju delove podataka koji se ne izvršavaju
Sklop inače sadrži jedan ili više modula koda koji sačinjavaju vašu aplikaciju ili biblioteku i meta podatke koji opisuju taj kod. Znači sklop se može nalaziti u jednoj datoteci ili se prostirati preko više njih. Tako kad pokrenete kompajliranje vaše aplikacije, vaš izvorni kod se konvertuje iz koda visokog nivoa u IL. Jedan od sklopova mora sadržavati izvršnu datoteku koja je bila određena kao ulazna tačka za aplikaciju. Prevodom sklopova u IL se izbegava pitanje nekompatibilnosti jezika. Svejedno u kom .Net programskom jeziku programirate svi .Net jezici se prevode u IL. Svaki modul sadrži određeni broj tipova. Tipovi mogu biti referentni tipovi kao što su klase ili vrednosni tipovi kao što su strukture. Tipove podataka u C# programskom jeziku možete pogledati ovde. Svaki tip podataka je uvek opisan CLR-u u objavi sklopa. 

( CLR Arhitektura )

Kad god započnete izvršavanje vašeg programa, prvo se prvi sklop učitava u memoriju. CLR tada ispituje objavu sklopa i utvrđuje sve zahteve neophodne za izvršenje programa, ispituje bezbednosne dozvole zatražene od sklopa i poredi ih sa sistemskim bezbednosnim merama. Samo ukoliko vaša aplikacija prođe uspešno sistemsku proveru, CLR će izvršiti kod. Prvi delovi koda koji moraju da se izvrše, učitavaju se u memoriju i prevode u prirodni binarni kod iz IL-a od prevodioca JIT- Just In Time .Tako jednom preveden, kod se izvrši i smešta u memoriju kao izvršni kod. Svaki deo koda se prevodi samo jednom kad se aplikacija izvrši i kad se kod grana na kod koji nije izvršen; JIT ga prevodi pre izvršavanja i smešta u memoriju kao binarni kod. Na ovaj način performanse vaše aplikacije se maksimizuju.

Kako se radi sa sklopovima pravljenim u različitim programskim jezicima?

Najbolje da pogledate praktično jedan primer. Otvorite Developer Command Prompt za Visual Studio. Zatim otkucajte:

C:\Program Files (x86)\Microsoft Visual Studio 14.0>cd..
C:\Program Files (x86)>cd..
C:\>md Test
C:\>cd test
C:\Test>notepad.exe CSharpAssembly.cs


Kad vam se otvori Notepad, prihvatite kreiranje novog dokumenta i otkucajte sledeći kod.

using System;

public class CSharpClass
{
          public static void CSharpMethod()
         {
                    Console.WriteLine("Hello from C# assembly");

         }
}


Zatvorite Notepad, prihvatite da vam se dokument snimi i vratite se na Developer Command Prompt.
Kompajlirate dokument sa CSC – C# kompajlerom i napravite CSharpAssembly.dll datoteku.

C:\Test>csc /t:library /out:CSharpAssembly.dll CSharpAssembly.cs

Zatim, napravite novi sklop u Visual Basic programskom jeziku:

C:\Test>notepad.exe VBAssembly.vb

U novom Notepad dokumentu, ukucajte sledeći Visual Basic kod:

Public Class VBClass
          Shared Sub VBMethod()
                    Console.WriteLine("Hello from VB assembly")
          End Sub
End Class


Zatvorite Notepad, prihvatite da snimite dokument i kompajlirajte dokument sa VBC – Visual Basic kompajlerom.

C:\Test>vbc /t:library /out:VBAssembly.dll VBAssembly.vb


Međutim, kako da napravite datoteku u IL programskom jeziku? Jednostavno, napravimo datoteku sa IL kodom pisanim u IL programskim jezikom.

C:\Test>notepad.exe ILAssembly.il

Prihvatite kreiranje novog dokumenta i unesite sledeći IL kod u dokument:

.assembly ILAssembly {}
 
.assembly extern mscorlib {}

.class public ILClass extends [mscorlib]System.Object {
          .method public static void ILMethod () cil managed {
                    ldstr"Hello from IL assembly"
                    call void [mscorlib]System.Console::WriteLine(string)
                    ret
         }
}


Zatvorite Notepad, prihvatite da snimite dokument i generišite IL kod sa ILASM generatorom.

C:\Test>ilasm /dll /out:ILAssembli.dll ILAssembly.il

Sad kad proverite u vašem direktorijumu Test, vi ste napravili 3 sklopa u 3 različita programska jezika.

C:\Test>dir *.dll


Directory of C:\Test

03/15/2016 02:04 AM 3,072 CSharpAssembly.dll
03/15/2016 02:19 AM 2,048 ILAssembly.dll
03/15/2016 02:11 AM 6,144 VBAssembly.dll

3 File(s) 11,264 bytes
0 Dir(s) 322,182,299,648 bytes free
 
C:\Test>

Ukucajte u Developer Command Prompt:

C:\Test>ildasm CSharpAssembly.dll

Otvoriće vam se ILDASM alat u kome možete pogledati kako izgleda sklop datoteke CSharpAssembly.dll . Kliknite na CSharpClass zatim na metodu CSharpMethod:void() i videćete sledeći IL kod:

.method public hidebysig static void CSharpMethod() cil managed
   // Code size 13 (0xd)
   .maxstack 8
   IL_0000: nop
   IL_0001: ldstr "Hello from C# assembly"
   IL_0006: call void [mscorlib]System.Console::WriteLine(string)
   IL_000b: nop
   IL_000c: ret
} // end of method CSharpClass::CSharpMethod


Ukoliko na isti način uradite i sa druge dve *.dll datoteke, videćete da je kod sličan bez obzira u kom ste programskoj jeziku pisali izvorni kod.

( ILDASM - IL Disassembler alat )

Vi možete na sličan način i da preko Developer Command Prompt-a napravite izvršni fajl, referencirate sklopove i da ih pozovete u vašem izvršnom fajlu. Komanda za referenciranje datoteka je jednostavna i datoteke se referenciraju tokom kompajliranja izvršne datoteke:

C:\Test>csc /r:ILAssembly.dll /r:CSharpAssembly.dll /r:VBAssembly.dll Assemblies.cs

Kad izvršite navedenu komandu, vi će te uspeti da referencirate sklopove u vašu izvršnu datoteku koja će se zvati Assemblies.exe. Međutim, dešava se da vaš izvršni fajl ne može da se izvrši iz Developer Command Prompt-a iako nemate prijavljenu ni jednu grešku, zato je najbolje da uvek referencirate i kompajlirate vaše datoteke iz Microsoft Visual Studio-a. Zato otvorite Microsoft Visual Studio i napravite novi projekat konzolne aplikacije u istom Test direktorijumu i nazovite ga Assemblies.

( Refenciranje sklopova iz Microsoft Visual Studio-a )

Otvorite Solution Explorer vašeg projekta, zatim kliknite desnim tasterom miša na Reference i u meniju kliknite Add Reference. Sa vaše leve strane izaberite tab Browse i u njemu čekirajte *.dll datoteke koje hoćete da referencirate u vašoj aplikaciji. U ovom slučaju izaberite sve tri *.dll datoteke, potvrdite reference i one će se pojaviti u prozoru Solution ispod direktorijuma Reference. To je sve, tako će te znati da su vaše datoteke referencirane. Onda možete da napišite sledeći kod:

using System;
using static System.Console;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Assemblies
{
    class Program
    {
        static void Main(string[] args)
        {
            CSharpClass.CSharpMethod();
            VBClass.VBMethod();
            ILClass.ILMethod();

            WriteLine( Environment.NewLine +"Press any key to continue...");
            ReadKey();

        }
    }
}

Kad izvršite navedeni kod rezultat će biti identičan.

Hello from C# assembly
Hello from VB assembly
Hello from IL assembly

Press any key to continue...


Kako navedeni program izgleda možete pogledati i na video-u:


( C# 6.0 Tutorial - Advanced - 1. Assemblies )

Kako da proverim da li je neka datoteka sklop?


Datoteka koja sadrži Managed Code – kontrolisani ili bezbedni kod ali takođe sadrži i meta podatke se naziva sklop. Postoje dva načina kako možete proveriti da li je neki *.dll ili *.exe fajl sklop. Pokrenete IL Disassembler – ildasm.exe i pokušate da otvorite vašu datoteku u njemu. Ukoliko dobijete poruku da testirana datoteka nije PE - Portable Executable fajl, onda vaša datoteka nije sklop. Međutim, programeri koji se bave testiranjem, oni prave Unit testove. Postoje različiti šabloni za Unit test u zavisnosti koji tip aplikacije pravite. Kada napravite projekat Unit Test onda ga morate pridodati vašem projektu i refencirati. Najbolje pogledajte najjednostavniji primer kako da proverite da li je neka datoteka sklop koristeći Unit test.

( Unit Test šablon za Console aplikaciju u C# programskom jeziku )

Pokrenite novi projekat, Unit Test Project za C# programski jezik i nazovite ga IsFileAssembly i unesite sledeći kod:

using System;
using static System.Console;
using System.IO;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace IsFileAssembly
{
    [TestClass]
    public class IsFileAssembly
    {
        [TestMethod]
        public void TestMethod()
        {
            try
            {
                AssemblyName testAssembly = AssemblyName.GetAssemblyName(@"C:\Test\CSharpAssembly.dll");
                WriteLine("Yes, the file is an assembly.");
            }
            catch (FileNotFoundException)
            {
                WriteLine("The file cannot be found.");

            }
            catch (BadImageFormatException)
            {
                WriteLine("The file is not an assembly.");

            }
            catch (FileLoadException)
            {
                WriteLine("The assembly has already been loaded.");

            }

        }
    }
 }

Zatim zatvorite vaš program i napravite novi projekat, Console Application - konzolnu aplikaciju i nazovite je CheckIsFileAssembly. Otvorite Solution Explorer, dodajte Existing Project…  postojeći projekat vašem Solution – rešenju i dodajte vaš IsFileAssembly - Unit test projekat. Zatim mu dodajte referencu i pokrenite metodu koja proverava da li je datoteka sklop. U navedenom primeru smo testirali CSharpAssembly.dll datoteku koju smo koristili u prethodnom primeru. Naravno vi možete testirati koju god hoćete datoteku. Pokrenite Unit test sledećim kodom.

using System;
using static System.Console;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CheckIsFileAssembly
{
    class Program
    {
        static void Main(string[] args)
        {
            IsFileAssembly.IsFileAssembly test = new IsFileAssembly.IsFileAssembly();
            test.TestMethod();
            WriteLine(Environment.NewLine + "Press any key to continue");
            ReadKey();
        }
    }
}

Kad izvršite navedeni kod rezultat će biti identičan.

Yes, the file is an assembly.

Press any key to continue...


Kako navedeni program izgleda možete pogledati i na video-u:


( C# 6.0 Tutorial - Advanced - 2. Unit Test - Check if a file is an assembly )