Angella Andrea - Italian Blog

Infinita passione per lo sviluppo software !
posts - 133, comments - 216, trackbacks - 9

My Links

News

MIT OpenCourseWare: I'm invested Wikipedia Affiliate Button


Sto leggendo:

Archives

Post Categories

Siti web realizzati

Siti web tecnici

[70-536] - Application Domain and Reflection


Area di riferimento

- Implementing service processes, threading, and application domains in a .NET Framework application
   - Create a unit of isolation for common language runtime in a .NET Framework application by using application domains
      - Create an application domain
      - Unload an application domain
      - Configure an application domain
      - Retrieve setup information from an application domain
      - Load assemblies into an application domain
- Implementing interoperability, reflection, and mailing functionality in a .NET Framework application
    - Implement reflection functionality in a .NET Framework application, and create metadata, Microsoft Intermediate language (MSIL), and a PE file by using System.Reflection.Emit namespace
      - Assembly class 


 


Application Domain

Gli Application Domain, brevemente AppDomains,permettono al codice di terze parti (e quindi non sicuro) di eseguire all'interno di un processo esistente con la garanzia che le strutture dati, il codice e il contesto di sicurezza non sarà compromesso. Permettono quindi di realizzare una importate caratteristica: l'isolamento.

Quando il CLR viene inizializzato, automaticamente viene creato un AppDomain chiamato Default AppDomain che sarà distrutto solamente quando il processo di Windows che lo ospita termininerà.

Le caratteristiche offerte da un AppDomain sono:

  • Il codice di un AppDomain non può essere acceduto da un altro AppDomain se non utilizzando la semantica marshal-by-reference o marshal-by-value scatenando controlli di Code Access Security (CAS)
  • Gli AppDomains possono essere scaricati (unload)
  • Tramite la CAS è possibile definire in modo estremamente dettagliato il contesto di sicurezza in cui esegue uno specifico AppDomain
  • Ciascun AppDomain può essere configurato in modo autonomo

E' possibile ottenere un riferimento all' AppDomain in cui sta eseguendo il thread chiamante in questo modo:

AppDomain app = Thread.GetDomain();


E' possibile creare un AppDomain utilizzando il metodo CreateDomain della classe AppDomain che accetta come primo argomento il nome e come altri argomenti un oggetto Evidence e un oggetto AppDomainSetup. L'oggetto Evidence permette di specificare le opzioni relative al contesto di sicurezza (scriverò in seguito post specifici sulla CAS) mentre l'oggetto AppDomainSetup permette di configurare l'AppDomain.
Tramite il metodo Unload della classe AppDomain è invece possibile scaricare un AppDomain quando non è più necessario.

La semantica marshal-by-reference consiste nell'ottenere un oggetto di tipo MarshalByRefObject che rappresenta un Proxy verso un oggetto presente in un altro AppDomain. In questo modo è possibile invocare tutti i metodi di quell'oggetto come se potessimo accedervi direttamente, in realtà sotto sotto il CLR fa transire il thread chiamante dall'AppDomain corrente al nuovo AppDomain garantendo quindi isolamento e sicurezza. I metodi per ottenere un proxy sono CreateInstanceAndUnwrap, CreateInstanceFromAndUnwrap e simili.

La semantica marshal-by-value consiste invece nell'ottenere un oggetto che è una perfetta copia dell'oggetto presente nell'altro AppDomain e totalmente indipendente. E' fondamentale in questo caso che l'oggetto che si vuole far transitare sia decorato con l'attributo [Serializable]. I metodi per ottenere un comportamento di questo tipo sono gli stessi della semantica marshal-by-reference, ciò che cambia infatti è il modo in cui sono definiti i tipi nell'AppDomain e come vegono trattati i risultati.

Un esempio pratico

Mi rendo conto che questo argomento non è dei più semplici. Per questo motivo ho deciso di creare una semplice ma significativa applicazione che sfrutta i vantaggi offerti dagli AppDomain.
Questa applicazione permette semplicemente di effettuare delle operazioni a partire da due numeri interi. La cosa particolare è che l'applicazione stessa può essere estesa tramite altre operazioni presenti in apposite dll (comunemente chiamate AddIn o PlugIn). Vediamo quindi come realizzare una semplice applicazione estensibile con AddIn.

Il problema principale di questo tipo di applicazioni è che non è possibile fidarsi del codice prodotto da terze parti, quindi è necessario, anzi direi obbligatorio, sfruttare l'isolamento offerto dagli AppDomain per garantire robustezza e sicurezza all'applicazione. L'applicazioni può essere scaricata al seguente indirizzo: http://www.angellaa.it/lab/dettagli-soluzione.aspx?id=12

Generalmente si procede nel seguente modo:

  • Si crea un assembly "Contratto" che definisce una interfaccia / classe-base i cui metodi sono usati dal meccanismo di comunicazione tra l'applicazione host e gli add-in. Si dovrebbe anche dare all'assembly uno strong name ma nell'esempio per semplicità non l'ho fatto
  • Gli sviluppatori degli add-in fanno riferimento all'assembly "Contratto" per costruire i loro assembly
  • Si costruisce l'applicazione "Host" che ovviamente anch'essa farà riferimento al "Contratto" e in più sarà presente una cartella in cui incollare i vari add-in. Al caricamento o al verificarsi di particolari eventi, l'applicazione cercherà in questa cartella gli add-in che definiscono le funzionalità aggiuntive

Partiamo dal contratto rappresentato dall'assembly DemoAddInApplicationApi:

namespace DemoAddInApplicationApi
{
    public interface DemoAddInApplicationInterface
    {
        int elabora(int a, int b);
    }

    public abstract class DemoAddInApplicationBase : MarshalByRefObject, DemoAddInApplicationInterface
    {
        public abstract int elabora(int a, int b);
    }

}


La classe astratta DemoAddInApplicationBase sarà quella da cui dovranno ereditare le classi specifiche create dagli sviluppatori degli Add-In. Ho introdotto questa classe per il semplice motivo che affinchè sia possibile sfruttare la semantica mashal-by-reference è necessario che l'oggetto scambiato sia un MarshalByRefObject. In questo modo evito che gli sviluppatori degli add-in si preoccupino di questi dettagli, lasciamo a loro il solo compito di definire un'implementazione specifica dell'operazione.

Ecco una parte del codice che ha scritto lo sviluppatore dell'add-in DemoAddInApplication1:

namespace DemoAddInApplication1
{
    public class DemoAddInApplicationDifferenza : DemoAddInApplicationBase
    {
        public override int elabora(int a, int b)
        {
            return a - b;
        }
    }

    public class DemoAddInApplicationProdotto : DemoAddInApplicationBase
    {
        public override int elabora(int a, int b)
        {
            return a * b;
        } 
    }

    ...
}


Andiamo quindi a vedere come è stata realizzata l'applicazione host.
Il seguente codice carica in una combo box l'elenco degli add-in presenti nella cartella add-in:

// Funzione che scandisce la cartella Add-in per rilevare la presenza di eventuali
// nuovi Add-In. Visualizza quindi i loro nomi nella Combo-Box per la scelta.
private void ricaricaAddIn()
{    
    string[] files = Directory.GetFiles("Add-in", "*.dll");

    cbAddIn.Items.Clear();
    cbOperazioni.Items.Clear();
    btnCalcola.Enabled = false;

    foreach (string file in files)
    {
        cbAddIn.Items.Add( file );
    }
}


Nel momento in cui viene selezionato l'add-in che si desidera utilizzare, inizia la ricerca delle possibili operazioni offerte all'interno di quello specifico add-in:

private void cbAddIn_SelectedIndexChanged(object sender, EventArgs e)
{   
    object item = cbAddIn.Items[cbAddIn.SelectedIndex];
    Assembly a = Assembly.LoadFrom( item.ToString() );

    cbOperazioni.Items.Clear();
    foreach (Type t in a.GetExportedTypes())
    {
        if (t.IsClass && typeof(DemoAddInApplicationBase).IsAssignableFrom(t))
        {
            cbOperazioni.Items.Add(t);
        }
    }
}


Il metodo LoadFrom permette di caricare un assembly. Il metodo GetExportedTypes restituisce tutti i tipi pubblici esposti da quell'assembly. Tramite la proprietà isClass è possibile determinare se il tipo è una classe e tramite il metodo IsAssignableFrom è possibile determinare se t è di tipo DemoAddInApplicationBase.

Il gestore dell'evento click del pulsante Calcola è il cuore del nostro discorso. Viene infatti creato un AppDomain a partire dal nome del file che individua l'add-in, dopodichè viene costruito un oggetto proxy che fa riferimento all'oggetto specifico che implementa l'operazione. Quindi eseguiamo l'operazione all'interno dell'AppDomain creato, visualizziamo il risultato e scarichiamo l'AppDomain.

private void btnCalcola_Click(object sender, EventArgs e)
{
    // recupero il tipo dell'operazione selezionata
    Type t = (Type)cbOperazioni.Items[cbOperazioni.SelectedIndex];
    string addInPath = cbAddIn.Items[cbAddIn.SelectedIndex].ToString();

    // Costruisco un AppDomain in cui eseguire il codice dell'Add-in
    AppDomain appDomain = AppDomain.CreateDomain("Add-in", null, null);

    DemoAddInApplicationBase addIn =
        (DemoAddInApplicationBase)appDomain.CreateInstanceFromAndUnwrap(addInPath, t.FullName);

    // Recupero gli operandi
    int a = Int32.Parse(txtA.Text);
    int b = Int32.Parse(txtB.Text);

    // Eseguo il metodo (Nota addIn è un Proxy)
    int ris = addIn.elabora(a, b);

    // Visualizzo i risultati
    txtRisultato.Text = ris.ToString();

    AppDomain.Unload(appDomain);
}


Ovviamente come avrete sicuramente notato, non ho gestito le possibili eccezioni che potrebbero verificarsi e nemmeno ho impostato l'oggetto Evidence per definire i permessi di esecuzione del codice degli add-in. Tornerò su questo ultimo punto nei post dedicati all'importante argomento della sicurezza applicativa.

Print | posted on mercoledì 9 aprile 2008 17:15 | Filed Under [ Exam 70-536 Application Development Foundation ]

Feedback

Gravatar

# re: [70-536] - Application Domain and Reflection

Ciao Andrea,

Perdonami, forse ti dovresti far spiegare da qualcuno degli anziani (o forse basta una ricerca con Google), come fare a far apparire sul "muro" solo la prima parte (o un sommario) di un post molto lungo. Pubblicare post cosi' lunghi senza accorgimenti rende il "muro" illegibile ed e' considerato una violazione dell'etichetta. Comunque nulla di grave, semplicemente non si finisce mai di imparare.

Purtroppo non sono in grado di darti indicazioni tecniche piu' precise io stesso, perche' non ho un blog su Ugi.

Keep up the good work!

-LV
09/04/2008 19:35 | LudovicoVan
Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET