2009-10-15 4 views
10

C'è un modo per fare in modo che la particolare DLL a cui fa riferimento una firma P/Invoke (DllImport) dipenda dall'architettura della CPU?CPU Architecture Independent P/Invoke: DllName o percorso possono essere "dinamici"?

Sto lavorando su un'applicazione che carica un gran numero di firme di metodo da una dll nativa da un fornitore di terze parti, in questo caso la DLL dell'interfaccia utente-spazio su un pezzo di hardware. Il fornitore ha ora iniziato a fornire versioni x86 e x64 della DLL ora, e penso che la mia app trarrebbe beneficio dall'esecuzione come un processo a 64 bit. Ad eccezione di questa DLL, tutto è codice .NET, quindi la creazione come "Qualsiasi CPU" funzionerebbe.

Tutte le firme del metodo nella DLL nativa sono le stesse su 64 bit, tuttavia il nome della DLL è diverso (Foo.dll vs. Foo_x64.dll). C'è un modo attraverso le firme P/Invoke o le voci app.config posso ottenere per scegliere quale DLL caricare in base all'architettura della CPU in esecuzione?

Se invece di nomi di DLL diversi era lo stesso nome in diverse cartelle, apre altre opzioni?

NB: poiché è essenziale che la versione di questa DLL dello spazio utente corrisponda al driver del kernel installato per l'hardware, la DLL non è in bundle con la nostra app, ma dipendiamo dal programma di installazione del venditore per collocarla in un directory nel% PATH%.

+0

Possibile duplicato di http: // StackOverflow.it/questions/23215518/target-32-bit-o-64-bit-nativo-dll-a seconda dell'ambiente –

+3

Penso che la duplicazione sia il contrario, dato che questa domanda è di quattro anni più vecchia di quella :) – Cheetah

risposta

4

Non c'è modo di avere una singola firma PInvoke e ottenere il comportamento desiderato. L'attributo viene masterizzato in metadati e deve avere valori costanti. Un trucco che potresti fare è avere più metodi.

public static class NativeMethods32 { 
    [DllImport("Foo.dll")] 
    public static extern int SomeMethod(); 
} 

public static class NativeMethods64 { 
    [DllImport("Foo_x864.dll")] 
    public static extern int SomeMethod(); 
} 

public static class NativeMethods { 
    public static bool Is32Bit { return 4 == IntPtr.Size; } 
    public static SomeMethod() { 
    return Is32Bit ? 
     NativeMethods32.SomeMethod(); 
     NativeMethods64.SomeMethod(); 
    } 
} 

Tuttavia questo non è l'approccio preferito. Un approccio più semplice sarebbe quello di rendere la DLL avere lo stesso nome su più piattaforme e creare una firma PInvoke indipendente dalla piattaforma. Questo è l'approccio più/tutte le librerie di Windows prendono.

+0

La cosa con lo stesso nome non funziona per il mio caso perché 1) Questo è un fornitore di terze parti DLL 2) Le DLL sono installate in una cartella nel PATH di sistema in modo che le applicazioni possano trovarle automaticamente (per fortuna il fornitore non installa più su% SystemRoot% \ system32) 3) Su un sistema operativo a 64 bit, entrambe le DLL a 32 bit e 64 bit devono essere disponibili # 1 significa che non riesco a smanettare e # 2 è in conflitto con # 3 . Ho finito per utilizzare una soluzione simile a quella che hai suggerito. Ho definito un'interfaccia con tutti i metodi e ho usato LinFu per creare un oggetto proxy in runtime che inoltra ai metodi statici corretti. – Cheetah

11

"Se invece di nomi DLL diversi era lo stesso nome in diverse cartelle, si aprono altre opzioni?"

Forse questo dovrebbe funzionare per voi:

public static class NativeMethods 
{ 
    // here we just use "Foo" and at runtime we load "Foo.dll" dynamically 
    // from any path on disk depending on the logic you want to implement 
    [DllImport("Foo", EntryPoint = "bar")] 
    private void bar(); 

    [DllImport("kernel32")] 
    private unsafe static extern void* LoadLibrary(string dllname); 

    [DllImport("kernel32")] 
    private unsafe static extern void FreeLibrary(void* handle); 

    private sealed unsafe class LibraryUnloader 
    { 
    internal LibraryUnloader(void* handle) 
    { 
     this.handle = handle; 
    } 

    ~LibraryUnloader() 
    { 
     if (handle != null) 
     FreeLibrary(handle); 
    } 

    private void* handle; 

    } // LibraryUnloader 

    private static readonly LibraryUnloader unloader; 

    static NativeMethods() 
    { 
    string path; 

    if (IntPtr.Size == 4) 
     path = "path/to/the/32/bit/Foo.dll"; 
    else 
     path = "path/to/the/64/bit/Foo.dll"; 

    unsafe 
    { 
     void* handle = LoadLibrary(path); 

     if (handle == null) 
     throw new DllNotFoundException("unable to find the native Foo library: " + path); 

     unloader = new LibraryUnloader(handle); 
    } 
    } 
} 

Essa consiste nel caricare esplicitamente la libreria nativa con il suo percorso completo prima di P/Invoke si cerca di caricarlo.

Cosa ne pensi?

+0

Sarebbe lavoro con Window/MS.NET ma per quanto riguarda Mono? – AndreyAkinshin

+0

non ho idea di Mono, per favore dicci cosa trovi –

+0

niente per ora = ( – AndreyAkinshin

1

Ho sviluppato una libreria speciale per il target: InteropDotNet. Introduce il nuovo attributo RuntimeDllImport con risoluzione dinamica del percorso della libreria (al volo). Nel modo predefinito, è possibile scrivere

[RuntimeDllImport("NativeLib", 
    CallingConvention = CallingConvention.Cdecl, EntryPoint = "sum")] 
int Sum(int a, int b); 

E la libreria verrà risolta dipende dall'ambiente. Ad esempio, percorsi per Win/Linux, x86/x64:

x86/NativeLib.dll 
x86/libNativeLib.so 
x64/NativeLib.dll 
x64/libNativeLib.so