Ho due versioni di System.Data.SQLite.DLL - per la piattaforma x86 e x64. La versione x86 rimane nella cartella dell'applicazione e la versione x64 rimane nella cartella appFolder \ x64. L'applicazione compilata come AnyCPU. Come posso caricare la versione necessaria di SQLite in base alla piattaforma Windows?Caricamento dell'assembly x86 o x64
risposta
Se si utilizza SQLite da http://system.data.sqlite.org, System.Data.SQLite.DLL è completamente gestito. Esiste una DLL nativa sottostante, SQLite.Interop.DLL, che deve essere modificata in base al processo (32 o 64 bit).
Distribuisco le librerie native in ". \ Native \ X64" per 64-bit e ". \ Native \ X86" per 32-bit. A runtime P/Invoke SetDllDirectory per impostare la directory di caricamento della DLL che punta al percorso corretto per il processo. http://msdn.microsoft.com/en-us/library/ms686203(v=vs.85).aspx
(Si noti che non ho familiarità con l'architettura della versione dell'eredità System.Data.SQLite.DLL da http://sqlite.phxsoftware.com)
private static class NativeMethods
{
[DllImport("kernel32.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern bool SetDllDirectory(string pathName);
}
...
// Underlying SQLite libraries are native.
// Manually set the DLL load path depending on the process.
var path = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Native");
if(IntPtr.Size == 8) // or: if(Environment.Is64BitProcess) // .NET 4.0
{
path = Path.Combine(path, "X64");
}
else
{
// X32
path = Path.Combine(path, "X86");
}
NativeMethods.SetDllDirectory(path);
È possibile utilizzare Environment.Is64BitProcess per identificare il processo come 64 bit. (Cercherò di evitare di individuare eccezioni come controllo del flusso ovunque possibile.)
È una buona proposta ma non risolve il mio problema. – Rover
@Rover: come mai? Hai sicuramente installato .NET a 64 bit? –
Uso .NET 3.5 che non ha questa variabile. Penso di poter controllare la versione della piattaforma ma eviterò solo di notare alcune eccezioni, ma l'assembly necessario non è stato caricato. – Rover
Non potresti semplicemente usare l'origine di SQLite come progetto separato nella tua soluzione invece di un assembly precompilato? Usando AnyCPU il sistema stesso si prenderà cura di tutto e non devi farlo in codice ...
È una delle DLL che utilizzo divisa su x86 e x64. Non ho il codice sorgente di tutte le DLL utilizzate. – Rover
Questo non risolverà alcun problema, poiché l'architettura della macchina di sviluppo non è sempre la stessa della macchina di distribuzione. Qualsiasi CPU funziona solo per gli assembly .NET, non per le DLL native (streghe SQLITE utilizzate sotto il cofano) – BigBoss
Sono sorpreso che questo funzioni a tutti. Dovrebbe trovare prima la versione x86 e fallire. Un binding assembly non riuscito non produce un altro tentativo attraverso AssemblyResolve.
Chiaramente, il CLR non può effettivamente trovare la versione x86 o questo non funzionerebbe anche in modalità x64. In altre parole, quando si risolve il problema, si interromperà il codice a 64 bit. Inseguire prima il problema con x86, utilizzare Fuslogvw.exe per vedere quali cartelle vengono esaminate per l'assemblaggio.
Una correzione reale dovrebbe includere lo spostamento dell'assembly x86 in una cartella separata e la regolazione del gestore di eventi di conseguenza. È possibile testare IntPtr.Size per scoprire se si sta eseguendo in modalità 64 bit (Dimensione == 8). Assicurati anche di generare un nome di percorso completo, utilizzando il percorso relativo come fai ora può causare un errore quando la directory di lavoro dell'app non è impostata dove si spera che sia. Assembly.GetEntryAssembly(). Location ti fornisce il percorso dell'EXE.
Ho rimosso il codice per evitare confusione e lasciare solo domande. – Rover
Hmm, non penso che cambi la mia risposta. L'ultimo paragrafo ti dice come farlo nel modo giusto. –
Ok, ho controllato la piattaforma, ho trovato l'assemblaggio necessario. Cosa devo fare per caricare il montaggio su AppDomain? – Rover
- installare DLL appropriato nella GAC (ad esempio, la versione a 64 bit su una piattaforma a 64 bit)
- utilizzare l'associazione dell'assieme nella configurazione Web/app (possibilmente nella configurazione macchina)
- Completamente qualificare i riferimenti di assembly parziali nella configurazione web/dell'app.
Per questo è disponibile il supporto integrato in 1.0.80.0 e versioni successive.
Se le macchine di sviluppo e quelle del cliente possono avere architetture di processore diverse, potrebbe essere necessario più di un pacchetto binario. Per questa situazione, si consiglia vivamente di utilizzare la funzionalità di precaricamento della libreria nativa . È disponibile dalla versione 1.0.80.0 e abilitato per impostazione predefinita.(from download page)
Tuttavia, per farlo funzionare nel mio plug-in ho dovuto anche aggiungere questo prima riferimento SQLite per la prima volta:
// Make SQLite work... (loading dll from e.g. x64/SQLite.Interop.dll)
System.Environment.SetEnvironmentVariable("PreLoadSQLite_BaseDirectory", System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location));
Sql.Data.SQLite...
Vedere questa domanda: New SQLite mixed assemblies
Alcuni programmi antivir prevengono SetDllDirectory() - mi ci è voluto molto tempo per rendermene conto. Stiamo usando
System.Reflection.Assembly myass = System.Reflection.Assembly.GetExecutingAssembly();
FileInfo fi = new FileInfo(myass.Location);
System.IntPtr moduleHandle = LoadLibraryEx(fi.Directory.FullName + "\\x64\\SQLite.Interop.DLL", IntPtr.Zero, 0);
per caricare la DLL x64 con percorso esplicito. Viene caricato in quel momento e .NET Runtime userà la DLL in memoria invece di cercarla sul disco.
Voto positivo per 'myass' :) –
Non ha funzionato nel mio caso. Anche se ho verificato con il debugger, la libreria nativa viene caricata correttamente. SQLite genera ancora un'eccezione sulla DLL nativa mancante. – BartoszKP
Ho risolto questo problema. Grazie. Ho usato "IntPtr.Size == 8". Ho usato AppDomain.CurrentDomain.AssemblyResolve invece di SetDllDirectory. – Rover