Come associare tardivamente le librerie a 32 bit/64 bit in fase di runtime
Domanda
Ho un problema simile a quello descritto, ma leggermente diverso da quello descritto Qui (Caricamento degli assembly e relative dipendenze).
Ho una DLL C++ per il rendering 3D che è ciò che vendiamo ai clienti.Per gli utenti .NET avremo un wrapper CLR attorno ad esso.La DLL C++ può essere creata sia nella versione a 32 che a 64 bit, ma penso che questo significhi che dobbiamo avere due wrapper CLR poiché CLR si lega a una DLL specifica?
Supponiamo ora che il nostro cliente abbia un'app .NET che può essere a 32 o 64 bit e che essendo un'app .NET pura lascia che sia CLR a elaborarla da un singolo set di assembly.La domanda è: come può il codice dell'app scegliere dinamicamente tra le nostre combinazioni CLR/DLL a 32 e 64 bit in fase di esecuzione?
Ancora più specificatamente, anche in questo caso è applicabile la risposta suggerita alla suddetta domanda (i.e.creare un gestore ResolveEvent)?
Soluzione
Finalmente ho una risposta che sembra funzionare.
Compila entrambe le versioni a 32 e 64 bit, sia gestite che non gestite, in cartelle separate.Chiedi quindi all'app .NET di scegliere in fase di esecuzione da quale directory caricare gli assembly.
Il problema con l'utilizzo di ResolveEvent è che viene chiamato solo se non vengono trovati assembly, quindi è fin troppo facile ritrovarsi accidentalmente con versioni a 32 bit.Utilizza invece un secondo oggetto AppDomain in cui possiamo modificare la proprietà ApplicationBase in modo che punti alla cartella giusta.Quindi ti ritroverai con un codice come:
static void Main(String[] argv)
{
// Create a new AppDomain, but with the base directory set to either the 32-bit or 64-bit
// sub-directories.
AppDomainSetup objADS = new AppDomainSetup();
System.String assemblyDir = System.IO.Path.GetDirectoryName(Application.ExecutablePath);
switch (System.IntPtr.Size)
{
case (4): assemblyDir += "\\win32\\";
break;
case (8): assemblyDir += "\\x64\\";
break;
}
objADS.ApplicationBase = assemblyDir;
// We set the PrivateBinPath to the application directory, so that we can still
// load the platform neutral assemblies from the app directory.
objADS.PrivateBinPath = System.IO.Path.GetDirectoryName(Application.ExecutablePath);
AppDomain objAD = AppDomain.CreateDomain("", null, objADS);
if (argv.Length > 0)
objAD.ExecuteAssembly(argv[0]);
else
objAD.ExecuteAssembly("MyApplication.exe");
AppDomain.Unload(objAD);
}
Alla fine ti ritroverai con 2 ex: la tua app normale e una seconda app di commutazione che sceglie quali bit caricare.Nota: non posso prendermi il merito per i dettagli di questo personalmente.Uno dei miei colleghi lo ha capito grazie al mio suggerimento iniziale.Se e quando si iscriverà a StackOverflow gli assegnerò la risposta
Altri suggerimenti
Ho potuto farlo circa un anno fa, ma non ricordo più tutti i dettagli.Fondamentalmente, puoi utilizzare IntPtr.Size per determinare quale DLL caricare, quindi eseguire l'effettivo LoadLibrary tramite p/Invoke.A quel punto, hai il modulo in memoria e dovresti essere in grado di eseguire semplicemente le funzioni p/Invoke dal suo interno: lo stesso nome del modulo non dovrebbe essere ricaricato di nuovo.
Penso, tuttavia, che nella mia applicazione la DLL C++ si registrasse come server COM e quindi accedessi alle sue funzionalità tramite un wrapper .NET generato, quindi non so se ho mai testato direttamente p/Invoking.
Ho riscontrato uno scenario simile qualche tempo fa.Un toolkit che stavo utilizzando non si comportava bene in un ambiente a 64 bit e non sono riuscito a trovare un modo per forzare dinamicamente il collegamento degli assembly a 32 bit.
È possibile forzare i tuoi assembly a funzionare in modalità a 32 bit, ma ciò richiede l'applicazione di patch all'intestazione CLR (esiste uno strumento che lo fa nel Framework) e se i tuoi assembly hanno nomi forti, questo non funziona.
Temo che dovrai creare e pubblicare due set di binari per piattaforme a 32 e 64 bit.