2009-10-15 9 views
10

¿Hay alguna forma de que la DLL particular a la que hace referencia una firma P/Invoke (DllImport) dependa de la arquitectura de la CPU?P/invocación independiente de la arquitectura de la CPU: ¿Puede el DllName o la ruta ser "dinámico"?

Estoy trabajando en una aplicación que carga una gran cantidad de firmas de métodos de un dll nativo de un proveedor tercero, en este caso, la DLL de la interfaz de espacio de usuario a una pieza de hardware. Ese proveedor ahora ha comenzado a suministrar las versiones x86 y x64 de la DLL ahora, y creo que mi aplicación se beneficiaría de ejecutarse como un proceso de 64 bits. Excepto por esta DLL, todo es código .NET, por lo que funcionar como "Cualquier CPU" funcionaría.

Todas las firmas de métodos en la DLL nativa son las mismas en 64 bits, sin embargo, el nombre de la DLL es diferente (Foo.dll vs. Foo_x64.dll). ¿Hay alguna forma, ya sea a través de las firmas P/Invoke o las entradas de app.config, de que pueda elegir qué DLL cargar según la arquitectura de la CPU en ejecución?

Si en lugar de diferentes nombres de DLL tiene el mismo nombre en diferentes carpetas, ¿abre alguna otra opción?

NB: Dado que es esencial que la versión de esta DLL de espacio de usuario coincida con el controlador kernel instalado para el hardware, la DLL no se incluye con nuestra aplicación, sino que dependemos del instalador de proveedor para colocarla en un directorio en% PATH%.

+0

Posible duplicado de http: // stackoverflow.com/preguntas/23215518/meta-32-bits o 64-bits nativo-dll-función-en-medio ambiente –

+3

Creo que la duplicación es al revés, dada a esta pregunta es cuatro años mayor que que uno :) – Cheetah

Respuesta

4

No hay forma de tener una sola firma de PInvoke y obtener el comportamiento que desea. El atributo se quema en metadatos y debe tener valores constantes. Un truco que podría hacer es tener múltiples métodos.

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(); 
    } 
} 

Sin embargo, este no es el enfoque preferido. Un enfoque más fácil sería hacer que el archivo DLL tenga el mismo nombre en múltiples plataformas y crear una firma PInvoke independiente de la plataforma. Este es el enfoque que toman la mayoría de las bibliotecas de Windows.

+0

el mismo nombre que no funciona para mi caso porque 1) se trata de un proveedor de terceros DLL 2) la DLL (s) están instalados en una carpeta en la ruta del sistema de modo que las aplicaciones se pueden encontrar de forma automática (por suerte el proveedor ya no instala en% SystemRoot% \ system32) 3) En un sistema operativo de 64 bits, las DLL de 32 y 64 bits deben estar disponibles # 1 significa que no puedo virar y # 2 entra en conflicto con # 3 . Terminé usando una solución similar a la que sugirió. Definí una interfaz con todos los métodos y usé LinFu para crear un objeto proxy en tiempo de ejecución que reenvía a los métodos estáticos correctos. – Cheetah

11

"Si en lugar de diferentes nombres de DLL era el mismo nombre en diferentes carpetas, ¿abren otras opciones?"

Tal vez esto funcionaría para usted:

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); 
    } 
    } 
} 

Consiste en cargar explícitamente la biblioteca nativa con su ruta completa antes de P/Invoke en sí trata de cargarlo.

¿Qué opinas?

+0

Sería trabajar con Window/MS.NET, pero ¿qué pasa con Mono? – AndreyAkinshin

+0

ninguna idea sobre Mono, por favor díganos lo que descubres –

+0

nada por ahora = ( – AndreyAkinshin

1

Desarrollé una biblioteca especial para el objetivo: InteropDotNet. Introduce el nuevo atributo RuntimeDllImport con la resolución dinámica de la ruta de la biblioteca (sobre la marcha). De forma predeterminada, puede escribir

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

Y la biblioteca se resolverá depende del entorno. Por ejemplo, rutas para Win/Linux, x86/x64:

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