2013-07-24 24 views
5

Sto sperimentando il caricamento di un assieme utilizzando solo array di byte, ma non riesco a capire come farlo funzionare correttamente. Ecco il programma di installazione:Loading Byte Array Assembly

public static void Main() 
{ 
    PermissionSet permissions = new PermissionSet(PermissionState.None); 
    AppDomainSetup setup = new AppDomainSetup { ApplicationBase = Environment.CurrentDirectory }; 
    AppDomain friendlyDomain = AppDomain.CreateDomain("Friendly", null, setup, permissions); 

    Byte[] primary = File.ReadAllBytes("Primary.dll_"); 
    Byte[] dependency = File.ReadAllBytes("Dependency.dll_"); 

    // Crashes here saying it can't find the file. 
    friendlyDomain.Load(dependency); 

    AppDomain.Unload(friendlyDomain); 

    Console.WriteLine("Stand successful"); 
    Console.ReadLine(); 
} 

ho creato due DLL finte, e rinominato la loro estensione a '.dll_' intenzionalmente in modo che il sistema non sarebbe in grado di trovare i file fisici. Sia primary e dependency riempire in modo corretto, ma quando provo a chiamare il metodo AppDomain.Load con i dati binari, ritorna con:

Could not load file or assembly 'Dependency, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

perché dovrebbe essere alla ricerca del sistema per un file?

UPDATE

Questo d'altra parte sembra funzionare:

public class Program { 
    public static void Main() { 
     PermissionSet permissions = new PermissionSet(PermissionState.Unrestricted); 
     AppDomainSetup setup = new AppDomainSetup { ApplicationBase = Environment.CurrentDirectory }; 
     AppDomain friendlyDomain = AppDomain.CreateDomain("Friendly", null, setup, permissions); 

     Byte[] primary = File.ReadAllBytes("Primary.dll_"); 
     Byte[] dependency = File.ReadAllBytes("Dependency.dll_"); 

     // Crashes here saying it can't find the file. 
     // friendlyDomain.Load(primary); 

     Stage stage = (Stage)friendlyDomain.CreateInstanceAndUnwrap(typeof(Stage).Assembly.FullName, typeof(Stage).FullName); 
     stage.LoadAssembly(dependency); 

     Console.WriteLine("Stand successful"); 
     Console.ReadLine(); 
    } 

} 

public class Stage : MarshalByRefObject { 
    public void LoadAssembly(Byte[] data) { 
     Assembly.Load(data); 
    } 
} 

Così sembra che ci sia una differenza tra AppDomain.Load e Assembly.Load.

+0

La Dipendenza DLL ha dipendenze che non sono state copiate, forse? –

+0

Il primario si affida alla dipendenza. Tuttavia, la dipendenza non ha dipendenze (non CLR). Sembra che il runtime non dovrebbe cercare il file con cui iniziare. – sircodesalot

risposta

9

Questo è normale, il CLR non considera la "dipendenza" è stato caricato per essere un assemblaggio adatto quando si cerca per l'assemblaggio che "primario" ha bisogno. Un problema associato al "contesto di caricamento", non ce n'è uno per gli assembly caricati in questo modo. Questo è intenzionale, il CLR non può garantire che l'inferno DLL non sarà un problema in quanto non ha idea da dove provenga l'assemblaggio. Dal momento che hai aperto la porta a Hell della DLL, devi anche evitare l'inferno te stesso.

È necessario implementare l'evento AppDomain.AssemblyResolve. Si attiva quando il CLR non riesce a trovare "dipendenza", è possibile restituire l'assembly che si ottiene da Assembly.Load (byte []). Dovrai comunque farlo in modo coerente quando si attiva più di una volta per lo stesso assembly, in altre parole restituire lo stesso identico Assembly, o avrai più problemi indotti dall'identità di tipo .NET. Producendo eccezioni di casting difficili da comprendere, "non è possibile utilizzare lo stile" Foo to Foo ".

Ci sono altri problemi, è piuttosto inefficiente. La memoria virtuale dell'assembly non può essere supportata da un file su disco, quindi è supportato dal file di paging. Che aumenta la dimensione del commit per il tuo processo.

È sicuramente meglio non farlo.

+0

Perché 'Assembly.Load' sembra consentirlo, mentre' AppDomain.Load' no? Inoltre, buon punto sulla dimensione del commit. – sircodesalot

+0

Non ha nulla a che fare con Assembly.Load. Quando usi AssemblyResolve, il CLR dice "Ho bisogno di questo" e tu dici "eccolo qui". Quindi il CLR sa cosa ha ottenuto. Il tuo approccio originale era "Here is something" e il CLR dice "non ho idea di cosa sia". –

+0

Seguo quello che stai dicendo, ma solo cambiando metodo sembra funzionare. Non ho mai dovuto usare 'AssemblyResolve' perché il CLR l'ha accettato quando proveniva da' Assembly.Load'. – sircodesalot

0

Se si utilizza FusionLogViewer è possibile visualizzare ulteriori dettagli sul problema particolare, il CLR sta avendo nel caricamento di un assembly .... è in grado di mostrare che posizioni si sta cercando di sondare per darvi un indizio, ecc

Si potrebbe anche gestire gli eventi AssemblyLoad/AssemblyResolve/ResourceResolve sul AppDomain nel codice, per tracciare la sequenza .

Questo è un esempio pratico che utilizza un MSBuild passo personalizzato per incorporare le tue progetto assembly dipendenti come risorse nel vostro programma EXE, e quindi utilizzare AssemblyResolve per caricarli da un ResourceStream (facendo Assembly.Load() sulla matrice byte []).

+0

'Può mostrare quali posizioni sta cercando di sondare'. Questo è solo, non dovrebbe essere sondaggio sembra. Sto già specificando il binario, quindi non c'è motivo per farlo uscire e caricarlo di nuovo dal file system. – sircodesalot

+0

'Primario' dipende da' Dipendenza', però. Il caricamento della 'dipendenza 'non dovrebbe causare alcun problema, e infatti ho scoperto che usare' Assembly.Load' sembra funzionare bene, mentre 'AppDomain.Load' no. – sircodesalot

3

Non vi è alcuna differenza tra questi due metodi (è possibile controllare lo official source code se lo si desidera).

Nella pagina MSDN per AppDomain.Load Method (Byte[]) è osservato che questo metodo sta caricando il gruppo nel dominio applicazione corrente:

Questo metodo deve essere utilizzato solo per caricare un assembly nel dominio corrente applicazione. Questo metodo viene fornito per la comodità dei chiamanti di interoperabilità che non possono chiamare il metodo statico Assembly.Load . Per caricare gli assembly in altri domini dell'applicazione, utilizzare un metodo come CreateInstanceAndUnwrap.

la linea:

friendlyDomain.Load(dependency); 

si comporta esattamente lo stesso con:

Assembly.Load(dependency); 

Il motivo per cui funziona nel codice di esempio aggiornato, è perché l'oggetto Stage è in realtà chiama Assembly.Load all'interno del figlio AppDomain.

Nota: questa risposta completa le risposte di Hans Passant e colinmith.

+0

Alla fine ho deciso di salvare gli assembly in una cartella temporanea su disco e quindi impostare la base 'AppDomain' su quella cartella. Potrei essere pazzo, ma giuro che sembra che AppDomain.Load controlli ancora il disco per la presenza fisica di un assembly, mentre 'Assembly.Load' no. In effetti, nella mia attuale implementazione, ho semplicemente passato il nome completo dell'assembly in 'friendlyDomain.Load' e lo recupera da' ApplicationBase' (come previsto).Ma perché non ci vorrebbe l'array di byte senza la verifica è oltre me. – sircodesalot