beh ... c'è svariata roba online anche se ho faticato un po a capire come dovevano essere caricate le assembly correttamente
hai 2 modi per caricare in memoria degli assembly:
1° - metodo abbastanza facile ma che ti da poca sicurezza e non ti permette di scaricare gli assembly caricati ... viene caricato tutto nel dominio corrente
2° - metodo molto più complesso ma che ti permette non solo di avere un dominio di esecuzione separato quindi un'ambiene più stretto e controllato ma puoi anche scaricare in blocco questo dominio per ricaricarlo, nel caso ad es di gestione dei plugin o simili
il 1° metodo te lo posto qua stesso, per il secondo vediamo quando ho + tempo ti spiego come fare, se ti interessa, ma è abbastanza complicato
ad esempio il primo sistema lo uso nel mio software di backup per caricarmi i provider vari che mi gestiscono in blocco la copia dei file
Ci sono 2 parti:
- un'interfaccia, che devono implementare TUTTE le classi che vuoi utilizzare all'interno degli assembly
- un gestore, che ti esegua il caricamento e ti gestica le operazioni comuni per semplificarti la vita
Inoltre, dipende da come imposti il tutto, puoi anche avere una sola classe che ti interessa gestire esternamente e quindi semplicemente, come faccio in questi casi, il nome della classe è l'ultima parte nel nome dell'assembly
Ad es
xyz.BAS.BackupProviders.SimpleCopy
la classe è SimpleCopy
al gestore tu passi la stringa SimpleCopy e lui carica l'assembly xyz.bas.backupprovider.simplecopy e la classe (passandogli il fullname, quindi compreso il namespace) xyz.bas.backupprovider.simplecopy
altrimenti hai 2 opzioni:
- usi gli attributi per definire delle classi che sono visibili all'esterno, dove magari metti una descrizione ed un codice
- usi la reflection per enumerare i metodi che estendono delle specifiche interfacce/classi
ora ti posto il codice più semplice che c'è per farti un po capire
codice:
using System;
using System.Reflection;
using System.IO;
namespace Computering.BAS.Helper
{
/// <summary>
/// Descrizione di riepilogo per Interface.
/// </summary>
public class BackupProviderManager
{
public static string[] EnumerateProviders()
{
string[] fileNames = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "Computering.BAS.Helper.BackupProvider.*.dll");
for(int currentIndex = 0; currentIndex < fileNames.Length; currentIndex++)
{
fileNames[currentIndex] = Path.GetFileNameWithoutExtension(fileNames[currentIndex]);
}
return fileNames;
}
public static IBackupProvider LoadProvider(string providerName)
{
string baseDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
if (File.Exists(baseDirectory + "\\" + providerName + ".dll") == false)
{
throw new FileNotFoundException("Impossibile caricare il provider " + baseDirectory + "\\" + providerName + ".dll", baseDirectory + "\\" + providerName + ".dll");
}
IBackupProvider backupProviderInterface = (IBackupProvider)(Activator.CreateInstanceFrom(baseDirectory + "\\" + providerName + ".dll", providerName)).Unwrap();
return backupProviderInterface;
}
}
}
il codice sicuramente non è il massimo, anzi, però è abbastanza semplice da capire:
- il primo mi elenca semplicemente i provider
- il secondo mi carica il provider
Tutto si può concentrare in una sola riga
IBackupProvider backupProviderInterface = (IBackupProvider)(Activator.CreateInstanceFrom(bas eDirectory + "\\" + providerName + ".dll", providerName)).Unwrap();
nel mio caso, ad esempio per simplecoy, providerName corrisponde a
Computering.BAS.Helper.BackupProvider.SimpleCopy
lo salvo per intero e lo uso per caricare il tutto
ovviamente usando questo sistema potrei caricare un'assembly mio all'interno del tuo software e fare QUALSIASI tipo di danno e accedere a qualsiasi tipo di dato, ma non avendo dati di alcun genere all'interno del mio software non mi interessa più di tanto
passiamo al caricamento tramite attributi e controllo estensione classe/interfaccia
sono MOLTOOO simili, l'85% del codice è uguale e comunque se si usano gli attributi, in ogni caso, il secondo deve comunque essere controllato onde evitare rischi e problemi
codice:
Assembly loadedAssembly = Assembly.LoadFile(path);
in questo modo carichi un'assembly e ti viene restituito l'oggetto. Se per qualche motivo potessi conservare solo il nome ti basta ciclare tutte le assembly caricate, tramite il metodo AppDomain.CurrentDomain.GetAssemblies, e confrontare i nomi o la location in base a quello che hai
Acquisito l'oggetto assembly è tutto abbastanza facile ...
codice:
foreach(Type foundedType in loadedAssembly.GetTypes())
{
if (foundedType.IsClass == true)
{
MessageBox.Show(foundedType.FullName + ":" + foundedType.IsSubclassOf(typeof(IBackupProvider)).ToString());
}
}
con questo semplice codice ti cicli tutti i tipi contenuti nell'assembly e quelli che sono classi vengono stampati a video e li viene scritto il loro nome per intero e se sono sotto classi di IBackupProvider.
IBackupProvider non è un'interfaccia ma una classe astratta, mi serviva che venisserò implementate delle funzionalità a priori. Comunque modificare il codice per verificare un'interfaccia è molto semplice ... non so se puoi lasciare questo identico codice cmq ti basta fare
foundedType.GetInterface("nomeInterfaccia", true)
e verificare se restituisce null
(il secondo parametro verifica il casing del testo o meno)
a questo codice, se vuoi verificare gli attributi, ti basta lavorare con
foundedType.GetCustomAttributes(tipodacercare, false)
il tipo lo recuperi facendo
typeof(attributo)
a quel punto, dopo che trovi le classi che ti interessano, puoi fare quello che ti serve ovvero instanziare le classi stesse usando il codice di sopra
spero di averti chiarito qualche dubbio