2012-08-13 11 views
14

Duplicar posible:
preprocessor directive…C#Cambio C# DllImport código de destino en función de x64/x86

tengo una C++ DLL externa para importar usando DLLImport. Si mi aplicación se está compilando en x64, necesito importar la versión x64 de esta dll, si es una versión x86, necesito la x86 dll.

¿Cuál es la mejor manera de lograr esto?

Idealmente, me gustaría alguna directiva de preprocesador, pero entiendo que esto no funciona en C#?

Más información: el archivo DLL está siendo importado por un proyecto que está configurado en AnyCPU. Un proyecto principal es el que determina si la aplicación se compila como x64 o x86. Compilamos ambas versiones para diferentes clientes, y quiero compartir el proyecto hijo en ambas versiones.

+0

¿Qué hay para importar AMBAS versiones (métodos privados) pero exponer al código del cliente el correcto según el entorno? Con .NET 4, simplemente marque [Environment.Is64BitOperatingSystem] (http://msdn.microsoft.com/en-us/library/system.environment.is64bitoperatingsystem.aspx). Tenga en cuenta que no mantendría dos versiones diferentes de la aplicación C# debido a la DLL nativa dependiente (por lo que no usaría el preprocesador para esto). –

+0

Michael: esa es casi mi pregunta, pero tengo una complicación adicional que significa que su solución no funcionará. Mi dll es importado por un proyecto que es anycpu, y un proyecto principal decide si la aplicación es x64 o x86 – Sugrue

+0

@Sugrue Luego tendrá que usar una solución de tiempo de ejecución, es decir, importar ambas y usar 'Environment.Is64BitProcess', o' sizeof (void *) ', o' IntPtr.Size'. –

Respuesta

22

Esto es principalmente un problema de implementación, solo haga que su instalador copie la DLL correcta según la versión de Windows en la máquina de destino.

Pero a nadie le gusta hacer eso. Pinkear dinámicamente la función correcta de la DLL es enormemente dolorosa, debe escribir tipos de delegados para cada función exportada y usar LoadLibrary + GetProcAddress + Marshal.GetDelegateForFunctionPointer para crear el objeto delegado.

Pero a nadie le gusta hacer eso. La táctica menos dolorosa es declarar la función dos veces, darle diferentes nombres y usar la propiedad EntryPoint en el atributo [DllImport] para especificar el nombre real. Luego prueba en el tiempo de ejecución al que deseas llamar.

Pero a nadie le gusta hacer eso. El truco más efectivo es orientar a Windows para que cargue la DLL correcta por usted. Lo primero que debe hacer es copiar el archivo DLL en un directorio donde Windows no lo busque. La mejor manera es crear un subdirectorio "x86" y un "x64" en su directorio de compilación y copiar el DLL apropiado en cada uno. Hágalo escribiendo un evento de creación posterior que cree los directorios y copie los archivos DLL.

Luego cuéntele a Windows al respecto pinvoking SetDllDirectory(). La ruta que especifique se agregará a los directorios que Windows busca para una DLL. De esta manera:

using System; 
using System.Runtime.InteropServices; 
using System.Reflection; 
using System.IO; 

class Program { 
    static void Main(string[] args) { 
     var path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); 
     path = Path.Combine(path, IntPtr.Size == 8 ? "x64" : "x86"); 
     bool ok = SetDllDirectory(path); 
     if (!ok) throw new System.ComponentModel.Win32Exception(); 
     //etc.. 
    } 
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    private static extern bool SetDllDirectory(string path); 
} 

Considere si el hecho de tener el código ejecutado en modo de 64 bits es realmente útil para usted. Es bastante raro que necesite el espacio de direcciones de memoria virtual gigante que obtiene de él, el único beneficio real. Aún necesita admitir la versión de 32 bits que necesita funcionar correctamente en el caso de 2 gigabytes.

+0

Buena solución. PhonicUK y Michael Graczyk también tienen buenas soluciones, pero esta no incluye un código repetitivo, que me gusta. – Sugrue

+1

Personalmente prefiero una llamada explícita a 'LoadLibrary' pasando la ruta completa, en lugar de modificar la ruta de búsqueda de DLL que siempre me parece torpe. La idea esencial es la misma, por supuesto. –

+0

LoadLibrary está cubierto por el segundo párrafo. Mala idea. –

5

Agregue las importaciones de DLL x86 y x86_64 con diferentes nombres, luego puede invocarlas de manera condicional según la arquitectura en tiempo de ejecución comprobando el valor de Environment.Is64BitProcess (o IntPtr.size si está usando < .Net 4). Esto funcionará independientemente de si el proyecto está construido como x86, x86_64 o AnyCPU

Como alternativa, configure 2 configuraciones de compilación diferentes - una que solo haga x86 y otra que solo haga x86_64, déle a cada una un símbolo de compilación condicional y use un #ifdef en su símbolo personalizado.

+0

Creo que el OP no sabe cómo usar '# ifdef' ¿puede proporcionar un ejemplo de código rápido para él, y quizás un ejemplo de código de carga de las bibliotecas también? Aun así, dio un +1 sin los ejemplos. –

+0

'Environment.Is64BitProcess' Realmente me ayudó gracias – LuckyLikey

Cuestiones relacionadas